From df25df1af84487b9ce9e793834e91bbf1485cecf Mon Sep 17 00:00:00 2001 From: EFanZh Date: Sat, 27 Dec 2025 12:29:19 +0800 Subject: [PATCH 001/319] Constify `fmt::from_fn` --- library/core/src/fmt/builders.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library/core/src/fmt/builders.rs b/library/core/src/fmt/builders.rs index 197cddd3fa9d..7550dac45cd0 100644 --- a/library/core/src/fmt/builders.rs +++ b/library/core/src/fmt/builders.rs @@ -1227,8 +1227,9 @@ impl<'a, 'b: 'a> DebugMap<'a, 'b> { /// assert_eq!(format!("{:?}", wrapped), "'a'"); /// ``` #[stable(feature = "fmt_from_fn", since = "1.93.0")] +#[rustc_const_stable(feature = "const_fmt_from_fn", since = "CURRENT_RUSTC_VERSION")] #[must_use = "returns a type implementing Debug and Display, which do not have any effects unless they are used"] -pub fn from_fn) -> fmt::Result>(f: F) -> FromFn { +pub const fn from_fn) -> fmt::Result>(f: F) -> FromFn { FromFn(f) } From 979704dacdbb0ba839de2c3f73acee891be17a63 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 26 Oct 2025 16:18:50 +0800 Subject: [PATCH 002/319] Fix not applicable on statement for convert_to_guarded_return Fix not applicable in statement when exist else block Example --- ```rust fn main() { some_statements(); if$0 let Ok(x) = Err(92) { foo(x); } else { return; } some_statements(); } ``` **Before this PR** Assist not applicable **After this PR** ```rust fn main() { some_statements(); let Ok(x) = Err(92) else { return; }; foo(x); some_statements(); } ``` --- .../src/handlers/convert_to_guarded_return.rs | 72 ++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs index 08b114072fd9..ea5c1637b760 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_to_guarded_return.rs @@ -95,7 +95,9 @@ fn if_expr_to_guarded_return( let parent_block = if_expr.syntax().parent()?.ancestors().find_map(ast::BlockExpr::cast)?; - if parent_block.tail_expr()? != if_expr.clone().into() { + if parent_block.tail_expr() != Some(if_expr.clone().into()) + && !(else_block.is_some() && ast::ExprStmt::can_cast(if_expr.syntax().parent()?.kind())) + { return None; } @@ -502,6 +504,36 @@ fn main() { ); } + #[test] + fn convert_if_let_has_else_block_in_statement() { + check_assist( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + if$0 let Ok(x) = Err(92) { + foo(x); + } else { + // needless comment + return; + } + some_statements(); +} +"#, + r#" +fn main() { + some_statements(); + let Ok(x) = Err(92) else { + // needless comment + return; + }; + foo(x); + some_statements(); +} +"#, + ); + } + #[test] fn convert_if_let_result_inside_let() { check_assist( @@ -1136,6 +1168,44 @@ fn main() { ); } + #[test] + fn ignore_else_if() { + check_assist_not_applicable( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + if cond { + () + } else if$0 let Ok(x) = Err(92) { + foo(x); + } else { + return; + } + some_statements(); +} +"#, + ); + } + + #[test] + fn ignore_if_inside_let() { + check_assist_not_applicable( + convert_to_guarded_return, + r#" +fn main() { + some_statements(); + let _ = if$0 let Ok(x) = Err(92) { + foo(x); + } else { + return; + } + some_statements(); +} +"#, + ); + } + #[test] fn ignore_let_else_branch() { check_assist_not_applicable( From 57e44f5046058d467b9f8731f4c49885a8664d8e Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 2 Jan 2026 18:39:06 +0100 Subject: [PATCH 003/319] skip codegen for intrinsics with big fallback bodies if backend does not need them --- compiler/rustc_codegen_llvm/src/lib.rs | 6 +++++- compiler/rustc_codegen_ssa/src/traits/backend.rs | 6 ++++++ compiler/rustc_interface/src/interface.rs | 1 + compiler/rustc_monomorphize/src/collector.rs | 9 +++++---- compiler/rustc_session/src/session.rs | 7 ++++++- tests/codegen-llvm/intrinsics/carrying_mul_add.rs | 5 ++--- 6 files changed, 25 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 095274744993..875b60ef6ddb 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -42,7 +42,7 @@ use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; use rustc_session::config::{OptLevel, OutputFilenames, PrintKind, PrintRequest}; -use rustc_span::Symbol; +use rustc_span::{Symbol, sym}; use rustc_target::spec::{RelocModel, TlsModel}; use crate::llvm::ToLlvmBool; @@ -333,6 +333,10 @@ impl CodegenBackend for LlvmCodegenBackend { target_config(sess) } + fn replaced_intrinsics(&self) -> Vec { + vec![sym::unchecked_funnel_shl, sym::unchecked_funnel_shr, sym::carrying_mul_add] + } + fn codegen_crate<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Box { Box::new(rustc_codegen_ssa::base::codegen_crate( LlvmCodegenBackend(()), diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index cb74e2e46d65..2ca24836d046 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -78,6 +78,12 @@ pub trait CodegenBackend { fn print_version(&self) {} + /// Returns a list of all intrinsics that this backend definitely + /// replaces, which means their fallback bodies do not need to be monomorphized. + fn replaced_intrinsics(&self) -> Vec { + vec![] + } + /// Value printed by `--print=backend-has-zstd`. /// /// Used by compiletest to determine whether tests involving zstd compression diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index c0f8f33692e8..c60cf9b8992a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -496,6 +496,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se ); codegen_backend.init(&sess); + sess.replaced_intrinsics = FxHashSet::from_iter(codegen_backend.replaced_intrinsics()); let cfg = parse_cfg(sess.dcx(), config.crate_cfg); let mut cfg = config::build_configuration(&sess, cfg); diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index 4b2f8e03afc1..622fdeb4e81c 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -1001,11 +1001,12 @@ fn visit_instance_use<'tcx>( if tcx.should_codegen_locally(panic_instance) { output.push(create_fn_mono_item(tcx, panic_instance, source)); } - } else if !intrinsic.must_be_overridden { + } else if !intrinsic.must_be_overridden + && !tcx.sess.replaced_intrinsics.contains(&intrinsic.name) + { // Codegen the fallback body of intrinsics with fallback bodies. - // We explicitly skip this otherwise to ensure we get a linker error - // if anyone tries to call this intrinsic and the codegen backend did not - // override the implementation. + // We have to skip this otherwise as there's no body to codegen. + // We also skip intrinsics the backend handles, to reduce monomorphizations. let instance = ty::Instance::new_raw(instance.def_id(), instance.args); if tcx.should_codegen_locally(instance) { output.push(create_fn_mono_item(tcx, instance, source)); diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 1a0ec600af47..b3027d902b4c 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -8,7 +8,7 @@ use std::{env, io}; use rand::{RngCore, rng}; use rustc_data_structures::base_n::{CASE_INSENSITIVE, ToBaseN}; use rustc_data_structures::flock; -use rustc_data_structures::fx::{FxHashMap, FxIndexSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; use rustc_data_structures::profiling::{SelfProfiler, SelfProfilerRef}; use rustc_data_structures::sync::{DynSend, DynSync, Lock, MappedReadGuard, ReadGuard, RwLock}; use rustc_errors::annotate_snippet_emitter_writer::AnnotateSnippetEmitter; @@ -154,6 +154,10 @@ pub struct Session { /// preserved with a flag like `-C save-temps`, since these files may be /// hard linked. pub invocation_temp: Option, + + /// The names of intrinsics that the current codegen backend replaces + /// with its own implementations. + pub replaced_intrinsics: FxHashSet, } #[derive(Clone, Copy)] @@ -1091,6 +1095,7 @@ pub fn build_session( target_filesearch, host_filesearch, invocation_temp, + replaced_intrinsics: FxHashSet::default(), // filled by `run_compiler` }; validate_commandline_args_with_session_available(&sess); diff --git a/tests/codegen-llvm/intrinsics/carrying_mul_add.rs b/tests/codegen-llvm/intrinsics/carrying_mul_add.rs index 21fb49a3786a..844f4f6cff64 100644 --- a/tests/codegen-llvm/intrinsics/carrying_mul_add.rs +++ b/tests/codegen-llvm/intrinsics/carrying_mul_add.rs @@ -11,10 +11,9 @@ use std::intrinsics::{carrying_mul_add, fallback}; -// The fallbacks are emitted even when they're never used, but optimize out. +// The fallbacks should not be emitted. -// RAW: wide_mul_u128 -// OPT-NOT: wide_mul_u128 +// NOT: wide_mul_u128 // CHECK-LABEL: @cma_u8 #[no_mangle] From c0ecf1ad40a953f01d8ebf5c035eef268cc001f8 Mon Sep 17 00:00:00 2001 From: Till Adam Date: Fri, 2 Jan 2026 21:57:02 +0100 Subject: [PATCH 004/319] Implement Span::line() and Span::column() for proc-macro server Add proper line/column resolution for proc-macro spans via a callback mechanism. Previously these methods returned hardcoded 1 values. The implementation adds: - SubRequest::LineColumn and SubResponse::LineColumnResult to the bidirectional protocol - ProcMacroClientInterface::line_column() method - Callback handling in load-cargo using LineIndex - Server implementation in RaSpanServer that uses the callback - a test for Span::line() and Span::column() in proc-macro server Add fn_like_span_line_column test proc-macro that exercises the new line/column API, and a corresponding test with a mock callback. --- src/tools/rust-analyzer/Cargo.lock | 1 + .../crates/load-cargo/src/lib.rs | 42 +++++++++--- .../src/bidirectional_protocol/msg.rs | 18 ++++- .../proc-macro-srv-cli/src/main_loop.rs | 14 ++++ .../crates/proc-macro-srv/Cargo.toml | 1 + .../proc-macro-test/imp/src/lib.rs | 10 +++ .../crates/proc-macro-srv/src/lib.rs | 2 + .../src/server_impl/rust_analyzer_span.rs | 10 ++- .../crates/proc-macro-srv/src/tests/mod.rs | 15 +++++ .../crates/proc-macro-srv/src/tests/utils.rs | 66 ++++++++++++++++++- 10 files changed, 161 insertions(+), 18 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 42eaeb01f1f2..8188fbf96064 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1864,6 +1864,7 @@ dependencies = [ "intern", "libc", "libloading", + "line-index 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", "memmap2", "object", "paths", 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 e8d98b1ce661..33468a5003c3 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -554,14 +554,12 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::LocalFilePathResult { name }) } SubRequest::SourceText { file_id, ast_id, start, end } => { - let ast_id = span::ErasedFileAstId::from_raw(ast_id); - let editioned_file_id = span::EditionedFileId::from_raw(file_id); - let span = Span { - range: TextRange::new(TextSize::from(start), TextSize::from(end)), - anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, - ctx: SyntaxContext::root(editioned_file_id.edition()), - }; - let range = db.resolve_span(span); + let range = resolve_sub_span( + db, + file_id, + ast_id, + TextRange::new(TextSize::from(start), TextSize::from(end)), + ); let source = db.file_text(range.file_id.file_id(db)).text(db); let text = source .get(usize::from(range.range.start())..usize::from(range.range.end())) @@ -569,6 +567,18 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SourceTextResult { text }) } + SubRequest::LineColumn { file_id, ast_id, offset } => { + let range = + resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); + let source = db.file_text(range.file_id.file_id(db)).text(db); + let line_index = ide_db::line_index::LineIndex::new(source); + let (line, column) = line_index + .try_line_col(range.range.start()) + .map(|lc| (lc.line + 1, lc.col + 1)) + .unwrap_or((1, 1)); + // proc_macro::Span line/column are 1-based + Ok(SubResponse::LineColumnResult { line, column }) + } SubRequest::FilePath { file_id } => { let file_id = FileId::from_raw(file_id); let source_root_id = db.file_source_root(file_id).source_root_id(db); @@ -603,6 +613,22 @@ impl ProcMacroExpander for Expander { } } +fn resolve_sub_span( + db: &dyn ExpandDatabase, + file_id: u32, + ast_id: u32, + range: TextRange, +) -> hir_expand::FileRange { + let ast_id = span::ErasedFileAstId::from_raw(ast_id); + let editioned_file_id = span::EditionedFileId::from_raw(file_id); + let span = Span { + range, + anchor: SpanAnchor { file_id: editioned_file_id, ast_id }, + ctx: SyntaxContext::root(editioned_file_id.edition()), + }; + db.resolve_span(span) +} + #[cfg(test)] mod tests { use ide_db::base_db::RootQueryDb; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index e41f8a5d7da7..0e3b700dcc5a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -13,13 +13,25 @@ pub enum SubRequest { FilePath { file_id: u32 }, SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, LocalFilePath { file_id: u32 }, + LineColumn { file_id: u32, ast_id: u32, offset: u32 }, } #[derive(Debug, Serialize, Deserialize)] pub enum SubResponse { - FilePathResult { name: String }, - SourceTextResult { text: Option }, - LocalFilePathResult { name: Option }, + FilePathResult { + name: String, + }, + SourceTextResult { + text: Option, + }, + LocalFilePathResult { + name: Option, + }, + /// Line and column are 1-based. + LineColumnResult { + line: u32, + column: u32, + }, } #[derive(Debug, Serialize, Deserialize)] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index b2f4b96bd255..22536a4e52b1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -220,6 +220,20 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl _ => None, } } + + fn line_column(&mut self, span: proc_macro_srv::span::Span) -> Option<(u32, u32)> { + let proc_macro_srv::span::Span { range, anchor, ctx: _ } = span; + match self.roundtrip(bidirectional::SubRequest::LineColumn { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + offset: range.start().into(), + }) { + Some(bidirectional::BidirectionalMessage::SubResponse( + bidirectional::SubResponse::LineColumnResult { line, column }, + )) => Some((line, column)), + _ => None, + } + } } fn handle_expand_ra( diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml index 361017178409..8e5617f8a20e 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/Cargo.toml @@ -31,6 +31,7 @@ libc.workspace = true [dev-dependencies] expect-test.workspace = true +line-index.workspace = true # used as proc macro test targets proc-macro-test.path = "./proc-macro-test" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs index b4fac26d6e72..06c76b6d0381 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/proc-macro-test/imp/src/lib.rs @@ -79,6 +79,16 @@ pub fn fn_like_span_ops(args: TokenStream) -> TokenStream { TokenStream::from_iter(vec![first, second, third]) } +/// Returns the line and column of the first token's span as two integer literals. +#[proc_macro] +pub fn fn_like_span_line_column(args: TokenStream) -> TokenStream { + let first = args.into_iter().next().unwrap(); + let span = first.span(); + let line = Literal::usize_unsuffixed(span.line()); + let column = Literal::usize_unsuffixed(span.column()); + TokenStream::from_iter(vec![TokenTree::Literal(line), TokenTree::Literal(column)]) +} + #[proc_macro_attribute] pub fn attr_noop(_args: TokenStream, item: TokenStream) -> TokenStream { item diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index f2d1dfbba4cc..c1ef49a7176b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -98,6 +98,8 @@ pub trait ProcMacroClientInterface { fn file(&mut self, file_id: span::FileId) -> String; fn source_text(&mut self, span: Span) -> Option; fn local_file(&mut self, file_id: span::FileId) -> Option; + /// Line and column are 1-based. + fn line_column(&mut self, span: Span) -> Option<(u32, u32)>; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 32725afc5527..3a25391b573b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -257,14 +257,12 @@ impl server::Span for RaSpanServer<'_> { Span { range: TextRange::empty(span.range.start()), ..span } } - fn line(&mut self, _span: Self::Span) -> usize { - // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 1 + fn line(&mut self, span: Self::Span) -> usize { + self.callback.as_mut().and_then(|cb| cb.line_column(span)).map_or(1, |(l, _)| l as usize) } - fn column(&mut self, _span: Self::Span) -> usize { - // FIXME requires db to resolve line index, THIS IS NOT INCREMENTAL - 1 + fn column(&mut self, span: Self::Span) -> usize { + self.callback.as_mut().and_then(|cb| cb.line_column(span)).map_or(1, |(_, c)| c as usize) } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs index 20507a6def54..ebef9a9a519a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/mod.rs @@ -703,6 +703,7 @@ fn list_test_macros() { fn_like_mk_idents [Bang] fn_like_span_join [Bang] fn_like_span_ops [Bang] + fn_like_span_line_column [Bang] attr_noop [Attr] attr_panic [Attr] attr_error [Attr] @@ -712,3 +713,17 @@ fn list_test_macros() { DeriveError [CustomDerive]"#]] .assert_eq(&res); } + +#[test] +fn test_fn_like_span_line_column() { + assert_expand_with_callback( + "fn_like_span_line_column", + // Input text with known position: "hello" starts at offset 1 (line 2, column 1 in 1-based) + " +hello", + expect![[r#" + LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 2 + LITER 42:Root[0000, 0]@0..100#ROOT2024 Integer 1 + "#]], + ); +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 61fcd810b1d9..81ff1965d68b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -6,7 +6,8 @@ use span::{ }; use crate::{ - EnvSnapshot, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, token_stream::TokenStream, + EnvSnapshot, ProcMacroClientInterface, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, + token_stream::TokenStream, }; fn parse_string(call_site: SpanId, src: &str) -> TokenStream { @@ -109,3 +110,66 @@ pub(crate) fn list() -> Vec { let res = srv.list_macros(&dylib_path).unwrap(); res.into_iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect() } + +/// A mock callback for testing that computes line/column from the input text. +struct MockCallback<'a> { + text: &'a str, +} + +impl ProcMacroClientInterface for MockCallback<'_> { + fn source_text(&mut self, span: Span) -> Option { + self.text + .get(usize::from(span.range.start())..usize::from(span.range.end())) + .map(ToOwned::to_owned) + } + + fn file(&mut self, _file_id: FileId) -> String { + String::new() + } + + fn local_file(&mut self, _file_id: FileId) -> Option { + None + } + + fn line_column(&mut self, span: Span) -> Option<(u32, u32)> { + let line_index = line_index::LineIndex::new(self.text); + let line_col = line_index.try_line_col(span.range.start())?; + // proc_macro uses 1-based line/column + Some((line_col.line as u32 + 1, line_col.col as u32 + 1)) + } +} + +pub fn assert_expand_with_callback( + macro_name: &str, + #[rust_analyzer::rust_fixture] ra_fixture: &str, + expect_spanned: Expect, +) { + let path = proc_macro_test_dylib_path(); + let expander = dylib::Expander::new(&temp_dir::TempDir::new().unwrap(), &path).unwrap(); + + let def_site = Span { + range: TextRange::new(0.into(), 150.into()), + anchor: SpanAnchor { + file_id: EditionedFileId::current_edition(FileId::from_raw(41)), + ast_id: ROOT_ERASED_FILE_AST_ID, + }, + ctx: SyntaxContext::root(span::Edition::CURRENT), + }; + let call_site = Span { + range: TextRange::new(0.into(), 100.into()), + anchor: SpanAnchor { + file_id: EditionedFileId::current_edition(FileId::from_raw(42)), + ast_id: ROOT_ERASED_FILE_AST_ID, + }, + ctx: SyntaxContext::root(span::Edition::CURRENT), + }; + let mixed_site = call_site; + + let fixture = parse_string_spanned(call_site.anchor, call_site.ctx, ra_fixture); + + let mut callback = MockCallback { text: ra_fixture }; + let res = expander + .expand(macro_name, fixture, None, def_site, call_site, mixed_site, Some(&mut callback)) + .unwrap(); + expect_spanned.assert_eq(&format!("{res:?}")); +} From 4fbc52085fda746d8128a79b878a5008e94a81fe Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Mon, 5 Jan 2026 11:30:51 +0100 Subject: [PATCH 005/319] perf: Re-use scratch allocations for `try_evaluate_obligations` --- .../crates/hir-ty/src/next_solver/fulfill.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs index 0fe073297279..a8bff44a0258 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/fulfill.rs @@ -48,6 +48,7 @@ pub struct FulfillmentCtxt<'db> { /// use the context in exactly this snapshot. #[expect(unused)] usable_in_snapshot: usize, + try_evaluate_obligations_scratch: PendingObligations<'db>, } #[derive(Default, Debug, Clone)] @@ -115,6 +116,7 @@ impl<'db> FulfillmentCtxt<'db> { FulfillmentCtxt { obligations: Default::default(), usable_in_snapshot: infcx.num_open_snapshots(), + try_evaluate_obligations_scratch: Default::default(), } } } @@ -162,12 +164,12 @@ impl<'db> FulfillmentCtxt<'db> { // and select. They should use a different `ObligationCtxt` instead. Then we'll be also able // to not put the obligations queue in `InferenceTable`'s snapshots. // assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); + self.try_evaluate_obligations_scratch.clear(); let mut errors = Vec::new(); - let mut obligations = Vec::new(); loop { let mut any_changed = false; - obligations.extend(self.obligations.drain_pending(|_| true)); - for (mut obligation, stalled_on) in obligations.drain(..) { + self.try_evaluate_obligations_scratch.extend(self.obligations.drain_pending(|_| true)); + for (mut obligation, stalled_on) in self.try_evaluate_obligations_scratch.drain(..) { if obligation.recursion_depth >= infcx.interner.recursion_limit() { self.obligations.on_fulfillment_overflow(infcx); // Only return true errors that we have accumulated while processing. From 4699fdc2887bd9a28921ca9e73193aa38f94d0c4 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Tue, 6 Jan 2026 16:53:34 +0800 Subject: [PATCH 006/319] Fix loses exists guard for move_guard Example --- ```rust fn main() { let cond = true; match 92 { 3 => true, x if cond => if x $0> 10 { false } else if x > 5 { true } else if x > 4 || x < -2 { false } else { true }, } } ``` **Before this PR** ```rust fn main() { let cond = true; match 92 { 3 => true, x if x > 10 => false, x if x > 5 => true, x if x > 4 || x < -2 => false, x => true, } } ``` **After this PR** ```rust fn main() { let cond = true; match 92 { 3 => true, x if cond && x > 10 => false, x if cond && x > 5 => true, x if cond && (x > 4 || x < -2) => false, x if cond => true, } } ``` --- .../ide-assists/src/handlers/move_guard.rs | 65 +++++++++++++++++-- 1 file changed, 60 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs index 1c0c6e43d53b..31baa63372ff 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs @@ -3,7 +3,7 @@ use syntax::{ SyntaxKind::WHITESPACE, ast::{ AstNode, BlockExpr, ElseBranch, Expr, IfExpr, MatchArm, Pat, edit::AstNodeEdit, make, - syntax_factory::SyntaxFactory, + prec::ExprPrecedence, syntax_factory::SyntaxFactory, }, syntax_editor::Element, }; @@ -109,6 +109,7 @@ pub(crate) fn move_arm_cond_to_match_guard( let match_arm: MatchArm = ctx.find_node_at_offset::()?; let match_pat = match_arm.pat()?; let arm_body = match_arm.expr()?; + let arm_guard = match_arm.guard().and_then(|it| it.condition()); let mut replace_node = None; let if_expr: IfExpr = IfExpr::cast(arm_body.syntax().clone()).or_else(|| { @@ -149,6 +150,25 @@ pub(crate) fn move_arm_cond_to_match_guard( 0 }; let indent_level = match_arm.indent_level(); + let make_guard = |cond: Option| { + let condition = match (arm_guard.clone(), cond) { + (None, None) => return None, + (None, Some(it)) | (Some(it), None) => it, + (Some(lhs), Some(rhs)) => { + let op_expr = |expr: Expr| { + if expr.precedence().needs_parentheses_in(ExprPrecedence::LAnd) { + make.expr_paren(expr).into() + } else { + expr + } + }; + let op = syntax::ast::BinaryOp::LogicOp(syntax::ast::LogicOp::And); + let expr_bin = make.expr_bin(op_expr(lhs), op, op_expr(rhs)); + expr_bin.into() + } + }; + Some(make.match_guard(condition)) + }; for (cond, block) in conds_blocks { let only_expr = block.statements().next().is_none(); @@ -156,8 +176,7 @@ pub(crate) fn move_arm_cond_to_match_guard( Some(then_expr) if only_expr => then_expr, _ => block.dedent(dedent.into()).into(), }; - let guard = make.match_guard(cond); - let new_arm = make.match_arm(match_pat.clone(), Some(guard), expr); + let new_arm = make.match_arm(match_pat.clone(), make_guard(Some(cond)), expr); replace_arms.push(new_arm); } if let Some(block) = tail { @@ -170,7 +189,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => block.dedent(dedent.into()).into(), }; - let new_arm = make.match_arm(match_pat, None, expr); + let new_arm = make.match_arm(match_pat, make_guard(None), expr); replace_arms.push(new_arm); } else { // There's no else branch. Add a pattern without guard, unless the following match @@ -185,7 +204,7 @@ pub(crate) fn move_arm_cond_to_match_guard( } _ => { let block_expr = make.expr_empty_block().into(); - replace_arms.push(make.match_arm(match_pat, None, block_expr)); + replace_arms.push(make.match_arm(match_pat, make_guard(None), block_expr)); } } } @@ -1081,6 +1100,42 @@ fn main() { x => {} } } +"#, + ) + } + + #[test] + fn move_arm_cond_to_match_guard_elseif_exist_guard() { + check_assist( + move_arm_cond_to_match_guard, + r#" +fn main() { + let cond = true; + match 92 { + 3 => true, + x if cond => if x $0> 10 { + false + } else if x > 5 { + true + } else if x > 4 || x < -2 { + false + } else { + true + }, + } +} +"#, + r#" +fn main() { + let cond = true; + match 92 { + 3 => true, + x if cond && x > 10 => false, + x if cond && x > 5 => true, + x if cond && (x > 4 || x < -2) => false, + x if cond => true, + } +} "#, ) } From a978fdcdace3c9bee4d11e03ee3ea2be5792025c Mon Sep 17 00:00:00 2001 From: Hendrik Lind Date: Tue, 6 Jan 2026 20:49:14 +0100 Subject: [PATCH 007/319] fix: use crates where ADT was defined in deref_chain of trait_applicable_items --- .../ide-completion/src/tests/flyimport.rs | 48 +++++++++++++++++++ .../ide-db/src/imports/import_assets.rs | 14 +++++- 2 files changed, 61 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs index 797df3f163da..d7db896679df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/flyimport.rs @@ -1976,3 +1976,51 @@ fn main() { "#]], ); } + +#[test] +fn trait_method_import_across_multiple_crates() { + let fixture = r#" + //- /lib.rs crate:test-trait + pub trait TestTrait { + fn test_function(&self) -> u32; + } + + //- /lib.rs crate:test-implementation deps:test-trait + pub struct TestStruct(pub usize); + + impl test_trait::TestTrait for TestStruct { + fn test_function(&self) -> u32 { + 1 + } + } + + //- /main.rs crate:main deps:test-implementation,test-trait + use test_implementation::TestStruct; + + fn main() { + let test = TestStruct(42); + test.test_f$0 + } + "#; + + check( + fixture, + expect![[r#" + me test_function() (use test_trait::TestTrait) fn(&self) -> u32 + "#]], + ); + + check_edit( + "test_function", + fixture, + r#" +use test_implementation::TestStruct; +use test_trait::TestTrait; + +fn main() { + let test = TestStruct(42); + test.test_function()$0 +} +"#, + ); +} 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 90e3bb61f44d..35579eb2590d 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 @@ -600,7 +600,19 @@ fn trait_applicable_items<'db>( } deref_chain .into_iter() - .filter_map(|ty| Some((ty.krate(db).into(), ty.fingerprint_for_trait_impl()?))) + .flat_map(|ty| { + let fingerprint = ty.fingerprint_for_trait_impl()?; + let mut crates = vec![]; + + if let Some(adt) = ty.as_adt() { + // Push crate where ADT was defined + crates.push((adt.krate(db).into(), fingerprint)); + } + // Always include environment crate + crates.push((ty.krate(db).into(), fingerprint)); + Some(crates) + }) + .flatten() .unique() .collect::>() }; From 6cc9e5ccd3728842f23502dc9d1f9de08056a340 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Jan 2026 08:15:27 +0100 Subject: [PATCH 008/319] Document `Query` --- .../crates/ide-db/src/symbol_index.rs | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index eb0529d6b5e7..06e1f6bb4560 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -40,15 +40,61 @@ use salsa::Update; use crate::RootDatabase; +/// A query for searching symbols in the workspace or dependencies. +/// +/// This struct configures how symbol search is performed, including the search text, +/// matching strategy, and filtering options. It is used by [`world_symbols`] to find +/// symbols across the codebase. +/// +/// # Example +/// ```ignore +/// let mut query = Query::new("MyStruct".to_string()); +/// query.only_types(); // Only search for type definitions +/// query.libs(); // Include library dependencies +/// query.exact(); // Use exact matching instead of fuzzy +/// ``` #[derive(Debug, Clone)] pub struct Query { + /// The original search query string as provided by the user. + /// Used for the final matching check via [`SearchMode::check`]. query: String, + /// Lowercase version of [`Self::query`], pre-computed for efficiency. + /// Used to build FST automata for case-insensitive index lookups. lowercased: String, + /// The search strategy to use when matching symbols. + /// - [`SearchMode::Exact`]: Symbol name must exactly match the query. + /// - [`SearchMode::Fuzzy`]: Symbol name must contain all query characters in order (subsequence match). + /// - [`SearchMode::Prefix`]: Symbol name must start with the query string. + /// + /// Defaults to [`SearchMode::Fuzzy`]. mode: SearchMode, + /// Controls filtering of trait-associated items (methods, constants, types). + /// - [`AssocSearchMode::Include`]: Include both associated and non-associated items. + /// - [`AssocSearchMode::Exclude`]: Exclude trait-associated items from results. + /// - [`AssocSearchMode::AssocItemsOnly`]: Only return trait-associated items. + /// + /// Defaults to [`AssocSearchMode::Include`]. assoc_mode: AssocSearchMode, + /// Whether the final symbol name comparison should be case-sensitive. + /// When `false`, matching is case-insensitive (e.g., "foo" matches "Foo"). + /// + /// Defaults to `false`. case_sensitive: bool, + /// When `true`, only return type definitions: structs, enums, unions, + /// type aliases, built-in types, and traits. Functions, constants, statics, + /// and modules are excluded. + /// + /// Defaults to `false`. only_types: bool, + /// When `true`, search library dependency roots instead of local workspace crates. + /// This enables finding symbols in external dependencies including the standard library. + /// + /// Defaults to `false` (search local workspace only). libs: bool, + /// When `true`, exclude re-exported/imported symbols from results, + /// showing only the original definitions. + /// + /// Defaults to `false`. exclude_imports: bool, } From 1af7813baa02ec2f05acbaae5238b37bc0ebe5d7 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Jan 2026 09:12:00 +0100 Subject: [PATCH 009/319] Document `WithFixture` --- .../crates/test-fixture/src/lib.rs | 109 ++++++++++++++++++ 1 file changed, 109 insertions(+) diff --git a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs index d81f27d7c3b1..ca68edd88c05 100644 --- a/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs +++ b/src/tools/rust-analyzer/crates/test-fixture/src/lib.rs @@ -37,7 +37,110 @@ use triomphe::Arc; pub const WORKSPACE: base_db::SourceRootId = base_db::SourceRootId(0); +/// A trait for setting up test databases from fixture strings. +/// +/// Fixtures are strings containing Rust source code with optional metadata that describe +/// a project setup. This is the primary way to write tests for rust-analyzer without +/// having to depend on the entire sysroot. +/// +/// # Fixture Syntax +/// +/// ## Basic Structure +/// +/// A fixture without metadata is parsed into a single source file (`/main.rs`). +/// Metadata is added after a `//-` comment prefix. +/// +/// ```text +/// //- /main.rs +/// fn main() { +/// println!("Hello"); +/// } +/// ``` +/// +/// Note that the fixture syntax is optional and can be omitted if the test only requires +/// a simple single file. +/// +/// ## File Metadata +/// +/// Each file can have the following metadata after `//-`: +/// +/// - **Path** (required): Must start with `/`, e.g., `/main.rs`, `/lib.rs`, `/foo/bar.rs` +/// - **`crate:`**: Defines a new crate with this file as its root +/// - Optional version: `crate:foo@0.1.0,https://example.com/repo.git` +/// - **`deps:,`**: Dependencies (requires `crate:`) +/// - **`extern-prelude:,`**: Limits extern prelude to specified crates +/// - **`edition:`**: Rust edition (2015, 2018, 2021, 2024). Defaults to current. +/// - **`cfg:=,`**: Configuration options, e.g., `cfg:test,feature="foo"` +/// - **`env:=`**: Environment variables +/// - **`crate-attr:`**: Crate-level attributes, e.g., `crate-attr:no_std` +/// - **`new_source_root:local|library`**: Starts a new source root +/// - **`library`**: Marks crate as external library (not workspace member) +/// +/// ## Global Meta (must appear at the top, in order) +/// +/// - **`//- toolchain: nightly|stable`**: Sets the Rust toolchain (default: stable) +/// - **`//- target_data_layout: `**: LLVM data layout string +/// - **`//- target_arch: `**: Target architecture (default: x86_64) +/// - **`//- proc_macros: ,`**: Enables predefined test proc macros +/// - **`//- minicore: , `**: Includes subset of libcore +/// +/// ## Cursor Markers +/// +/// Use `$0` to mark cursor position(s) in the fixture: +/// - Single `$0`: marks a position (use with [`with_position`](Self::with_position)) +/// - Two `$0` markers: marks a range (use with [`with_range`](Self::with_range)) +/// - Escape as `\$0` if you need a literal `$0` +/// +/// # Examples +/// +/// ## Single file with cursor position +/// ```text +/// r#" +/// fn main() { +/// let x$0 = 42; +/// } +/// "# +/// ``` +/// +/// ## Multiple crates with dependencies +/// ```text +/// r#" +/// //- /main.rs crate:main deps:helper +/// use helper::greet; +/// fn main() { greet(); } +/// +/// //- /lib.rs crate:helper +/// pub fn greet() {} +/// "# +/// ``` +/// +/// ## Using minicore for lang items +/// ```text +/// r#" +/// //- minicore: option, result, iterator +/// //- /main.rs +/// fn foo() -> Option { Some(42) } +/// "# +/// ``` +/// +/// The available minicore flags are listed at the top of crates\test-utils\src\minicore.rs. +/// +/// ## Using test proc macros +/// ```text +/// r#" +/// //- proc_macros: identity, mirror +/// //- /main.rs crate:main deps:proc_macros +/// use proc_macros::identity; +/// +/// #[identity] +/// fn foo() {} +/// "# +/// ``` +/// +/// Available proc macros: `identity` (attr), `DeriveIdentity` (derive), `input_replace` (attr), +/// `mirror` (bang), `shorten` (bang) pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_single_file( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -50,6 +153,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, file) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_many_files( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -66,6 +170,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, files) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_files(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> Self { let mut db = Self::default(); @@ -75,6 +180,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { db } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_files_extra_proc_macros( #[rust_analyzer::rust_fixture] ra_fixture: &str, @@ -88,6 +194,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { db } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_position(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FilePosition) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); @@ -95,6 +202,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, FilePosition { file_id, offset }) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_range(#[rust_analyzer::rust_fixture] ra_fixture: &str) -> (Self, FileRange) { let (db, file_id, range_or_offset) = Self::with_range_or_offset(ra_fixture); @@ -102,6 +210,7 @@ pub trait WithFixture: Default + ExpandDatabase + SourceDatabase + 'static { (db, FileRange { file_id, range }) } + /// See the trait documentation for more information on fixtures. #[track_caller] fn with_range_or_offset( #[rust_analyzer::rust_fixture] ra_fixture: &str, From ad020937855df0af1dc7416be0b12d0b8c2a89a1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Wed, 7 Jan 2026 08:46:47 +0100 Subject: [PATCH 010/319] feat: Allow rust paths in symbol search --- .../rust-analyzer/crates/hir/src/symbols.rs | 72 +- .../crates/ide-db/src/symbol_index.rs | 664 +++++++++++++++++- 2 files changed, 724 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 073142670d2a..544c759ed3a7 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -9,6 +9,7 @@ use hir_def::{ ModuleDefId, ModuleId, TraitId, db::DefDatabase, item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob}, + nameres::crate_def_map, per_ns::Item, src::{HasChildSource, HasSource}, visibility::{Visibility, VisibilityExplicitness}, @@ -20,9 +21,12 @@ use hir_ty::{ }; use intern::Symbol; use rustc_hash::FxHashMap; -use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName}; +use syntax::{ + AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, + ast::{HasModuleItem, HasName}, +}; -use crate::{HasCrate, Module, ModuleDef, Semantics}; +use crate::{Crate, HasCrate, Module, ModuleDef, Semantics}; /// The actual data that is stored in the index. It should be as compact as /// possible. @@ -57,6 +61,70 @@ impl DeclarationLocation { } } +impl<'db> FileSymbol<'db> { + /// Create a `FileSymbol` representing a crate's root module. + /// This is used for crate search queries like `::` or `::foo`. + pub fn for_crate_root(db: &'db dyn HirDatabase, krate: Crate) -> Option> { + let display_name = krate.display_name(db)?; + let crate_name = display_name.crate_name(); + let root_module = krate.root_module(db); + let def_map = crate_def_map(db, krate.into()); + let module_data = &def_map[root_module.into()]; + + // Get the definition source (the source file for crate roots) + let definition = module_data.origin.definition_source(db); + let hir_file_id = definition.file_id; + + // For a crate root, the "declaration" is the source file itself + // We use the entire file's syntax node as the location + let syntax_node = definition.value.node(); + let ptr = SyntaxNodePtr::new(&syntax_node); + + // For the name, we need to create a synthetic name pointer. + // We'll use the first token of the file as a placeholder since crate roots + // don't have an explicit name in the source. + // We create a name_ptr pointing to the start of the file. + let name_ptr = match &definition.value { + crate::ModuleSource::SourceFile(sf) => { + // Try to find the first item with a name as a reasonable location for focus + // This is a bit of a hack but works for navigation purposes + let first_item: Option = sf.items().next(); + if let Some(item) = first_item { + if let Some(name) = item.syntax().children().find_map(syntax::ast::Name::cast) { + AstPtr::new(&name).wrap_left() + } else { + // No name found, try to use a NameRef instead + if let Some(name_ref) = + item.syntax().descendants().find_map(syntax::ast::NameRef::cast) + { + AstPtr::new(&name_ref).wrap_right() + } else { + return None; + } + } + } else { + return None; + } + } + _ => return None, + }; + + let loc = DeclarationLocation { hir_file_id, ptr, name_ptr }; + + Some(FileSymbol { + name: Symbol::intern(crate_name.as_str()), + def: ModuleDef::Module(root_module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }) + } +} + /// Represents an outstanding module that the symbol collector must collect symbols from. #[derive(Debug)] struct SymbolCollectorWork { diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index 06e1f6bb4560..ca0d5ec1e5e6 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -55,12 +55,17 @@ use crate::RootDatabase; /// ``` #[derive(Debug, Clone)] pub struct Query { - /// The original search query string as provided by the user. - /// Used for the final matching check via [`SearchMode::check`]. + /// The item name to search for (last segment of the path, or full query if no path). + /// When empty with a non-empty `path_filter`, returns all items in that module. query: String, /// Lowercase version of [`Self::query`], pre-computed for efficiency. /// Used to build FST automata for case-insensitive index lookups. lowercased: String, + /// Path segments to filter by (all segments except the last). + /// Empty if no `::` in the original query. + path_filter: Vec, + /// If true, the first path segment must be a crate name (query started with `::`). + anchor_to_crate: bool, /// The search strategy to use when matching symbols. /// - [`SearchMode::Exact`]: Symbol name must exactly match the query. /// - [`SearchMode::Fuzzy`]: Symbol name must contain all query characters in order (subsequence match). @@ -100,10 +105,13 @@ pub struct Query { impl Query { pub fn new(query: String) -> Query { - let lowercased = query.to_lowercase(); + let (path_filter, item_query, anchor_to_crate) = Self::parse_path_query(&query); + let lowercased = item_query.to_lowercase(); Query { - query, + query: item_query, lowercased, + path_filter, + anchor_to_crate, only_types: false, libs: false, mode: SearchMode::Fuzzy, @@ -113,6 +121,74 @@ impl Query { } } + /// Parse a query string that may contain path segments. + /// + /// Returns (path_filter, item_query, anchor_to_crate) where: + /// - `path_filter`: Path segments to match (all but the last segment) + /// - `item_query`: The item name to search for (last segment) + /// - `anchor_to_crate`: Whether the first segment must be a crate name + fn parse_path_query(query: &str) -> (Vec, String, bool) { + // Check for leading :: (absolute path / crate search) + let anchor_to_crate = query.starts_with("::"); + let query = if anchor_to_crate { &query[2..] } else { query }; + + // Handle sole "::" - return all crates + if query.is_empty() && anchor_to_crate { + return (vec![], String::new(), true); + } + + // Check for trailing :: (module browsing - returns all items in module) + let return_all_in_module = query.ends_with("::"); + let query = if return_all_in_module { query.trim_end_matches("::") } else { query }; + + if !query.contains("::") { + // No path separator - single segment + if anchor_to_crate && !return_all_in_module { + // "::foo" - fuzzy search crate names only + return (vec![], query.to_string(), true); + } + if return_all_in_module { + // "foo::" - browse all items in module "foo" + // path_filter = ["foo"], query = "", anchor_to_crate = false/true + return (vec![query.to_string()], String::new(), anchor_to_crate); + } + // Plain "foo" - normal fuzzy search + return (vec![], query.to_string(), false); + } + + // Filter out empty segments (e.g., "foo::::bar" -> "foo::bar") + let segments: Vec<&str> = query.split("::").filter(|s| !s.is_empty()).collect(); + + if segments.is_empty() { + return (vec![], String::new(), anchor_to_crate); + } + + let path: Vec = + segments[..segments.len() - 1].iter().map(|s| s.to_string()).collect(); + let item = if return_all_in_module { + // All segments go to path, item is empty + let mut path = path; + path.push(segments.last().unwrap().to_string()); + return (path, String::new(), anchor_to_crate); + } else { + segments.last().unwrap_or(&"").to_string() + }; + + (path, item, anchor_to_crate) + } + + /// Returns true if this query should return all items in a module + /// (i.e., the original query ended with `::`) + fn is_module_browsing(&self) -> bool { + self.query.is_empty() && !self.path_filter.is_empty() + } + + /// Returns true if this query is searching for crates + /// (i.e., the query was "::" alone or "::foo" for fuzzy crate search) + fn is_crate_search(&self) -> bool { + self.anchor_to_crate && self.path_filter.is_empty() + } + pub fn only_types(&mut self) { self.only_types = true; } @@ -181,7 +257,28 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); - let indices: Vec<_> = if query.libs { + // Handle special case: "::" alone or "::foo" for crate search + if query.is_crate_search() { + return search_crates(db, &query); + } + + // If we have a path filter, resolve it to target modules first + let indices: Vec<_> = if !query.path_filter.is_empty() { + let target_modules = resolve_path_to_modules( + db, + &query.path_filter, + query.anchor_to_crate, + query.case_sensitive, + ); + + if target_modules.is_empty() { + return vec![]; // Path doesn't resolve to any module + } + + // Get symbol indices only for the resolved modules + target_modules.iter().map(|&module| SymbolIndex::module_symbols(db, module)).collect() + } else if query.libs { + // Original behavior for non-path queries searching libs LibraryRoots::get(db) .roots(db) .par_iter() @@ -192,6 +289,7 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { .map(|&root| SymbolIndex::library_symbols(db, root)) .collect() } else { + // Original behavior for non-path queries searching local crates let mut crates = Vec::new(); for &root in LocalRoots::get(db).roots(db).iter() { @@ -204,13 +302,131 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { }; let mut res = vec![]; - query.search::<()>(&indices, |f| { - res.push(f.clone()); - ControlFlow::Continue(()) - }); + + // For module browsing (empty query, non-empty path_filter), return all symbols + if query.is_module_browsing() { + for index in &indices { + for symbol in index.symbols.iter() { + // Apply existing filters (only_types, assoc_mode, exclude_imports, etc.) + if query.matches_symbol_filters(symbol) { + res.push(symbol.clone()); + } + } + } + } else { + // Normal search: use FST to match item name + query.search::<()>(&indices, |f| { + res.push(f.clone()); + ControlFlow::Continue(()) + }); + } + res } +/// Search for crates by name (handles "::" and "::foo" queries) +fn search_crates<'db>(db: &'db RootDatabase, query: &Query) -> Vec> { + let mut res = vec![]; + + for krate in Crate::all(db) { + let Some(display_name) = krate.display_name(db) else { continue }; + let crate_name = display_name.crate_name().as_str(); + + // If query is empty (sole "::"), return all crates + // Otherwise, fuzzy match the crate name + let matches = if query.query.is_empty() { + true + } else { + query.mode.check(&query.query, query.case_sensitive, crate_name) + }; + + if matches { + // Create a FileSymbol for the crate's root module + if let Some(symbol) = hir::symbols::FileSymbol::for_crate_root(db, krate) { + res.push(symbol); + } + } + } + + res +} + +/// Resolve a path filter to the target module(s) it points to. +/// Returns the modules whose symbol indices should be searched. +/// +/// The path_filter contains segments like ["std", "vec"] for a query like "std::vec::Vec". +/// We resolve this by: +/// 1. Finding crates matching the first segment +/// 2. Walking down the module tree following subsequent segments +fn resolve_path_to_modules( + db: &dyn HirDatabase, + path_filter: &[String], + anchor_to_crate: bool, + case_sensitive: bool, +) -> Vec { + let [first_segment, rest_segments @ ..] = path_filter else { + return vec![]; + }; + + // Helper for name comparison + let names_match = |actual: &str, expected: &str| -> bool { + if case_sensitive { actual == expected } else { actual.eq_ignore_ascii_case(expected) } + }; + + // Find crates matching the first segment + let matching_crates: Vec = Crate::all(db) + .into_iter() + .filter(|krate| { + krate + .display_name(db) + .is_some_and(|name| names_match(name.crate_name().as_str(), first_segment)) + }) + .collect(); + + // If anchor_to_crate is true, first segment MUST be a crate name + // If anchor_to_crate is false, first segment could be a crate OR a module in local crates + let mut candidate_modules: Vec = vec![]; + + // Add crate root modules for matching crates + for krate in matching_crates { + candidate_modules.push(krate.root_module(db)); + } + + // If not anchored to crate, also search for modules matching first segment in local crates + if !anchor_to_crate { + for &root in LocalRoots::get(db).roots(db).iter() { + for &krate in db.source_root_crates(root).iter() { + let root_module = Crate::from(krate).root_module(db); + for child in root_module.children(db) { + if let Some(name) = child.name(db) { + if names_match(name.as_str(), first_segment) { + candidate_modules.push(child); + } + } + } + } + } + } + + // Walk down the module tree for remaining path segments + for segment in rest_segments { + candidate_modules = candidate_modules + .into_iter() + .flat_map(|module| { + module.children(db).filter(|child| { + child.name(db).is_some_and(|name| names_match(name.as_str(), segment)) + }) + }) + .collect(); + + if candidate_modules.is_empty() { + break; + } + } + + candidate_modules +} + #[derive(Default)] pub struct SymbolIndex<'db> { symbols: Box<[FileSymbol<'db>]>, @@ -382,12 +598,14 @@ impl<'db> SymbolIndex<'db> { } impl Query { + /// Search symbols in the given indices. pub(crate) fn search<'db, T>( - self, + &self, indices: &[&'db SymbolIndex<'db>], cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow, ) -> Option { let _p = tracing::info_span!("symbol_index::Query::search").entered(); + let mut op = fst::map::OpBuilder::new(); match self.mode { SearchMode::Exact => { @@ -466,6 +684,41 @@ impl Query { (true, AssocSearchMode::Exclude) | (false, AssocSearchMode::AssocItemsOnly) ) } + + /// Check if a symbol passes all filters except name matching. + /// Used for module browsing where we want all items in a module. + fn matches_symbol_filters(&self, symbol: &FileSymbol<'_>) -> bool { + // Check only_types filter + if self.only_types + && !matches!( + symbol.def, + hir::ModuleDef::Adt(..) + | hir::ModuleDef::TypeAlias(..) + | hir::ModuleDef::BuiltinType(..) + | hir::ModuleDef::Trait(..) + ) + { + return false; + } + + // Check assoc_mode filter + if !self.matches_assoc_mode(symbol.is_assoc) { + return false; + } + + // Check exclude_imports filter + if self.exclude_imports && symbol.is_import { + return false; + } + + // Check underscore prefix + let ignore_underscore_prefixed = !self.query.starts_with("__"); + if ignore_underscore_prefixed && symbol.name.as_str().starts_with("__") { + return false; + } + + true + } } #[cfg(test)] @@ -622,4 +875,395 @@ pub struct Foo; let symbols = world_symbols(&db, query); expect_file!["./test_data/test_symbols_exclude_imports.txt"].assert_debug_eq(&symbols); } + + #[test] + fn test_parse_path_query() { + // Plain query - no path + let (path, item, anchor) = Query::parse_path_query("Item"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Path with item + let (path, item, anchor) = Query::parse_path_query("foo::Item"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Multi-segment path + let (path, item, anchor) = Query::parse_path_query("foo::bar::Item"); + assert_eq!(path, vec!["foo", "bar"]); + assert_eq!(item, "Item"); + assert!(!anchor); + + // Leading :: (anchor to crate) + let (path, item, anchor) = Query::parse_path_query("::std::vec::Vec"); + assert_eq!(path, vec!["std", "vec"]); + assert_eq!(item, "Vec"); + assert!(anchor); + + // Just "::" - return all crates + let (path, item, anchor) = Query::parse_path_query("::"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, ""); + assert!(anchor); + + // "::foo" - fuzzy search crate names + let (path, item, anchor) = Query::parse_path_query("::foo"); + assert_eq!(path, Vec::::new()); + assert_eq!(item, "foo"); + assert!(anchor); + + // Trailing :: (module browsing) + let (path, item, anchor) = Query::parse_path_query("foo::"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, ""); + assert!(!anchor); + + // Full path with trailing :: + let (path, item, anchor) = Query::parse_path_query("foo::bar::"); + assert_eq!(path, vec!["foo", "bar"]); + assert_eq!(item, ""); + assert!(!anchor); + + // Absolute path with trailing :: + let (path, item, anchor) = Query::parse_path_query("::std::vec::"); + assert_eq!(path, vec!["std", "vec"]); + assert_eq!(item, ""); + assert!(anchor); + + // Empty segments should be filtered + let (path, item, anchor) = Query::parse_path_query("foo::::bar"); + assert_eq!(path, vec!["foo"]); + assert_eq!(item, "bar"); + assert!(!anchor); + } + + #[test] + fn test_query_modes() { + // Test is_module_browsing + let query = Query::new("foo::".to_owned()); + assert!(query.is_module_browsing()); + assert!(!query.is_crate_search()); + + // Test is_crate_search with sole :: + let query = Query::new("::".to_owned()); + assert!(!query.is_module_browsing()); + assert!(query.is_crate_search()); + + // Test is_crate_search with ::foo + let query = Query::new("::foo".to_owned()); + assert!(!query.is_module_browsing()); + assert!(query.is_crate_search()); + + // Normal query should be neither + let query = Query::new("foo".to_owned()); + assert!(!query.is_module_browsing()); + assert!(!query.is_crate_search()); + + // Path query should be neither + let query = Query::new("foo::bar".to_owned()); + assert!(!query.is_module_browsing()); + assert!(!query.is_crate_search()); + } + + #[test] + fn test_path_search() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod inner; +pub struct RootStruct; + +//- /inner.rs +pub struct InnerStruct; +pub mod nested { + pub struct NestedStruct; +} +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Search for item in specific module + let query = Query::new("inner::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"InnerStruct"), "Expected InnerStruct in {:?}", names); + + // Search for item in nested module + let query = Query::new("inner::nested::NestedStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"NestedStruct"), "Expected NestedStruct in {:?}", names); + + // Search with crate prefix + let query = Query::new("main::inner::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"InnerStruct"), "Expected InnerStruct in {:?}", names); + + // Wrong path should return empty + let query = Query::new("wrong::InnerStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong path"); + } + + #[test] + fn test_module_browsing() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod mymod; + +//- /mymod.rs +pub struct MyStruct; +pub fn my_func() {} +pub const MY_CONST: u32 = 1; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Browse all items in module + let query = Query::new("main::mymod::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"MyStruct"), "Expected MyStruct in {:?}", names); + assert!(names.contains(&"my_func"), "Expected my_func in {:?}", names); + assert!(names.contains(&"MY_CONST"), "Expected MY_CONST in {:?}", names); + } + + #[test] + fn test_fuzzy_item_with_path() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod mymod; + +//- /mymod.rs +pub struct MyLongStructName; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Fuzzy match on item name with exact path + let query = Query::new("main::mymod::MyLong".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"MyLongStructName"), + "Expected fuzzy match for MyLongStructName in {:?}", + names + ); + } + + #[test] + fn test_case_insensitive_path() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod MyMod; + +//- /MyMod.rs +pub struct MyStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Case insensitive path matching (default) + let query = Query::new("main::mymod::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"MyStruct"), "Expected case-insensitive match in {:?}", names); + } + + #[test] + fn test_absolute_path_search() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:mycrate +mod inner; +pub struct CrateRoot; + +//- /inner.rs +pub struct InnerItem; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Absolute path with leading :: + let query = Query::new("::mycrate::inner::InnerItem".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"InnerItem"), + "Expected InnerItem with absolute path in {:?}", + names + ); + + // Absolute path should NOT match if crate name is wrong + let query = Query::new("::wrongcrate::inner::InnerItem".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong crate name"); + } + + #[test] + fn test_wrong_path_returns_empty() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:main +mod existing; + +//- /existing.rs +pub struct MyStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Non-existent module path + let query = Query::new("nonexistent::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for non-existent path"); + + // Correct item, wrong module + let query = Query::new("wrongmod::MyStruct".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for wrong module"); + } + + #[test] + fn test_root_module_items() { + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:mylib +pub struct RootItem; +pub fn root_fn() {} +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Items at crate root - path is just the crate name + let query = Query::new("mylib::RootItem".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!(names.contains(&"RootItem"), "Expected RootItem at crate root in {:?}", names); + + // Browse crate root + let query = Query::new("mylib::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + assert!( + names.contains(&"RootItem"), + "Expected RootItem when browsing crate root in {:?}", + names + ); + assert!( + names.contains(&"root_fn"), + "Expected root_fn when browsing crate root in {:?}", + names + ); + } + + #[test] + fn test_crate_search_all() { + // Test that sole "::" returns all crates + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:alpha +pub struct AlphaStruct; + +//- /beta.rs crate:beta +pub struct BetaStruct; + +//- /gamma.rs crate:gamma +pub struct GammaStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // Sole "::" should return all crates (as module symbols) + let query = Query::new("::".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"alpha"), "Expected alpha crate in {:?}", names); + assert!(names.contains(&"beta"), "Expected beta crate in {:?}", names); + assert!(names.contains(&"gamma"), "Expected gamma crate in {:?}", names); + assert_eq!(symbols.len(), 3, "Expected exactly 3 crates, got {:?}", names); + } + + #[test] + fn test_crate_search_fuzzy() { + // Test that "::foo" fuzzy-matches crate names + let (mut db, _) = RootDatabase::with_many_files( + r#" +//- /lib.rs crate:my_awesome_lib +pub struct AwesomeStruct; + +//- /other.rs crate:another_lib +pub struct OtherStruct; + +//- /foo.rs crate:foobar +pub struct FooStruct; +"#, + ); + + let mut local_roots = FxHashSet::default(); + local_roots.insert(WORKSPACE); + LocalRoots::get(&db).set_roots(&mut db).to(local_roots); + + // "::foo" should fuzzy-match crate names containing "foo" + let query = Query::new("::foo".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"foobar"), "Expected foobar crate in {:?}", names); + assert_eq!(symbols.len(), 1, "Expected only foobar crate, got {:?}", names); + + // "::awesome" should match my_awesome_lib + let query = Query::new("::awesome".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"my_awesome_lib"), "Expected my_awesome_lib crate in {:?}", names); + assert_eq!(symbols.len(), 1, "Expected only my_awesome_lib crate, got {:?}", names); + + // "::lib" should match multiple crates + let query = Query::new("::lib".to_owned()); + let symbols = world_symbols(&db, query); + let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect(); + + assert!(names.contains(&"my_awesome_lib"), "Expected my_awesome_lib in {:?}", names); + assert!(names.contains(&"another_lib"), "Expected another_lib in {:?}", names); + assert_eq!(symbols.len(), 2, "Expected 2 crates matching 'lib', got {:?}", names); + + // "::nonexistent" should return empty + let query = Query::new("::nonexistent".to_owned()); + let symbols = world_symbols(&db, query); + assert!(symbols.is_empty(), "Expected empty results for non-matching crate pattern"); + } } From dc64aef1ed2ea86e42fe83f10e00c2f362dec114 Mon Sep 17 00:00:00 2001 From: Shoyu Vanilla Date: Sun, 4 Jan 2026 19:10:10 +0900 Subject: [PATCH 011/319] fix: Properly lower `SelfOnly` predicates --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 369 +++++++++++++----- .../crates/hir-ty/src/lower/path.rs | 34 +- .../crates/hir-ty/src/next_solver/interner.rs | 7 +- .../crates/hir-ty/src/next_solver/util.rs | 15 + .../hir-ty/src/tests/regression/new_solver.rs | 60 +++ 5 files changed, 373 insertions(+), 112 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index 46ec554e0a65..9da32464c86f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -77,6 +77,7 @@ pub struct ImplTraits { #[derive(PartialEq, Eq, Debug, Hash)] pub struct ImplTrait { pub(crate) predicates: StoredClauses, + pub(crate) assoc_ty_bounds_start: u32, } pub type ImplTraitIdx = Idx; @@ -166,6 +167,12 @@ impl<'db> LifetimeElisionKind<'db> { } } +#[derive(Clone, Copy, PartialEq, Debug)] +pub(crate) enum GenericPredicateSource { + SelfOnly, + AssocTyBound, +} + #[derive(Debug)] pub struct TyLoweringContext<'db, 'a> { pub db: &'db dyn HirDatabase, @@ -465,10 +472,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { // this dance is to make sure the data is in the right // place even if we encounter more opaque types while // lowering the bounds - let idx = self - .impl_trait_mode - .opaque_type_data - .alloc(ImplTrait { predicates: Clauses::empty(interner).store() }); + let idx = self.impl_trait_mode.opaque_type_data.alloc(ImplTrait { + predicates: Clauses::empty(interner).store(), + assoc_ty_bounds_start: 0, + }); let impl_trait_id = origin.either( |f| ImplTraitId::ReturnTypeImplTrait(f, idx), @@ -608,7 +615,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { ignore_bindings: bool, generics: &Generics, predicate_filter: PredicateFilter, - ) -> impl Iterator> + use<'a, 'b, 'db> { + ) -> impl Iterator, GenericPredicateSource)> + use<'a, 'b, 'db> { match where_predicate { WherePredicate::ForLifetime { target, bound, .. } | WherePredicate::TypeBound { target, bound } => { @@ -634,8 +641,8 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { let self_ty = self.lower_ty(*target); Either::Left(Either::Right(self.lower_type_bound(bound, self_ty, ignore_bindings))) } - &WherePredicate::Lifetime { bound, target } => { - Either::Right(iter::once(Clause(Predicate::new( + &WherePredicate::Lifetime { bound, target } => Either::Right(iter::once(( + Clause(Predicate::new( self.interner, Binder::dummy(rustc_type_ir::PredicateKind::Clause( rustc_type_ir::ClauseKind::RegionOutlives(OutlivesPredicate( @@ -643,8 +650,9 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { self.lower_lifetime(target), )), )), - )))) - } + )), + GenericPredicateSource::SelfOnly, + ))), } .into_iter() } @@ -654,7 +662,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { bound: &'b TypeBound, self_ty: Ty<'db>, ignore_bindings: bool, - ) -> impl Iterator> + use<'b, 'a, 'db> { + ) -> impl Iterator, GenericPredicateSource)> + use<'b, 'a, 'db> { let interner = self.interner; let meta_sized = self.lang_items.MetaSized; let pointee_sized = self.lang_items.PointeeSized; @@ -712,7 +720,10 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { } TypeBound::Use(_) | TypeBound::Error => {} } - clause.into_iter().chain(assoc_bounds.into_iter().flatten()) + clause + .into_iter() + .map(|pred| (pred, GenericPredicateSource::SelfOnly)) + .chain(assoc_bounds.into_iter().flatten()) } fn lower_dyn_trait(&mut self, bounds: &[TypeBound]) -> Ty<'db> { @@ -732,7 +743,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { for b in bounds { let db = ctx.db; - ctx.lower_type_bound(b, dummy_self_ty, false).for_each(|b| { + ctx.lower_type_bound(b, dummy_self_ty, false).for_each(|(b, _)| { match b.kind().skip_binder() { rustc_type_ir::ClauseKind::Trait(t) => { let id = t.def_id(); @@ -990,35 +1001,49 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { rustc_type_ir::AliasTyKind::Opaque, AliasTy::new_from_args(interner, def_id, args), ); - let predicates = self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { - let mut predicates = Vec::new(); - for b in bounds { - predicates.extend(ctx.lower_type_bound(b, self_ty, false)); - } + let (predicates, assoc_ty_bounds_start) = + self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx| { + let mut predicates = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); + for b in bounds { + for (pred, source) in ctx.lower_type_bound(b, self_ty, false) { + match source { + GenericPredicateSource::SelfOnly => predicates.push(pred), + GenericPredicateSource::AssocTyBound => assoc_ty_bounds.push(pred), + } + } + } - if !ctx.unsized_types.contains(&self_ty) { - let sized_trait = self.lang_items.Sized; - let sized_clause = sized_trait.map(|trait_id| { - let trait_ref = TraitRef::new_from_args( - interner, - trait_id.into(), - GenericArgs::new_from_slice(&[self_ty.into()]), - ); - Clause(Predicate::new( - interner, - Binder::dummy(rustc_type_ir::PredicateKind::Clause( - rustc_type_ir::ClauseKind::Trait(TraitPredicate { - trait_ref, - polarity: rustc_type_ir::PredicatePolarity::Positive, - }), - )), - )) - }); - predicates.extend(sized_clause); - } - predicates - }); - ImplTrait { predicates: Clauses::new_from_slice(&predicates).store() } + if !ctx.unsized_types.contains(&self_ty) { + let sized_trait = self.lang_items.Sized; + let sized_clause = sized_trait.map(|trait_id| { + let trait_ref = TraitRef::new_from_args( + interner, + trait_id.into(), + GenericArgs::new_from_slice(&[self_ty.into()]), + ); + Clause(Predicate::new( + interner, + Binder::dummy(rustc_type_ir::PredicateKind::Clause( + rustc_type_ir::ClauseKind::Trait(TraitPredicate { + trait_ref, + polarity: rustc_type_ir::PredicatePolarity::Positive, + }), + )), + )) + }); + predicates.extend(sized_clause); + } + + let assoc_ty_bounds_start = predicates.len() as u32; + predicates.extend(assoc_ty_bounds); + (predicates, assoc_ty_bounds_start) + }); + + ImplTrait { + predicates: Clauses::new_from_slice(&predicates).store(), + assoc_ty_bounds_start, + } } pub(crate) fn lower_lifetime(&mut self, lifetime: LifetimeRefId) -> Region<'db> { @@ -1139,6 +1164,31 @@ impl ImplTraitId { .expect("owner should have opaque type") .get_with(|it| it.impl_traits[idx].predicates.as_ref().as_slice()) } + + #[inline] + pub fn self_predicates<'db>( + self, + db: &'db dyn HirDatabase, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + let (impl_traits, idx) = match self { + ImplTraitId::ReturnTypeImplTrait(owner, idx) => { + (ImplTraits::return_type_impl_traits(db, owner), idx) + } + ImplTraitId::TypeAliasImplTrait(owner, idx) => { + (ImplTraits::type_alias_impl_traits(db, owner), idx) + } + }; + let predicates = + impl_traits.as_deref().expect("owner should have opaque type").get_with(|it| { + let impl_trait = &it.impl_traits[idx]; + ( + impl_trait.predicates.as_ref().as_slice(), + impl_trait.assoc_ty_bounds_start as usize, + ) + }); + + predicates.map_bound(|(preds, len)| &preds[..len]) + } } impl InternedOpaqueTyId { @@ -1146,6 +1196,14 @@ impl InternedOpaqueTyId { pub fn predicates<'db>(self, db: &'db dyn HirDatabase) -> EarlyBinder<'db, &'db [Clause<'db>]> { self.loc(db).predicates(db) } + + #[inline] + pub fn self_predicates<'db>( + self, + db: &'db dyn HirDatabase, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + self.loc(db).self_predicates(db) + } } #[salsa::tracked] @@ -1655,12 +1713,15 @@ pub(crate) fn generic_predicates_for_param<'db>( ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { if predicate(pred, &mut ctx) { - predicates.extend(ctx.lower_where_predicate( - pred, - true, - maybe_parent_generics, - PredicateFilter::All, - )); + predicates.extend( + ctx.lower_where_predicate( + pred, + true, + maybe_parent_generics, + PredicateFilter::All, + ) + .map(|(pred, _)| pred), + ); } } } @@ -1696,21 +1757,44 @@ pub(crate) fn type_alias_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, ) -> EarlyBinder<'db, &'db [Clause<'db>]> { - type_alias_bounds_with_diagnostics(db, type_alias).0.map_bound(|it| it.as_slice()) + type_alias_bounds_with_diagnostics(db, type_alias).0.predicates.map_bound(|it| it.as_slice()) } -pub(crate) fn type_alias_bounds_with_diagnostics<'db>( +#[inline] +pub(crate) fn type_alias_self_bounds<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, -) -> (EarlyBinder<'db, Clauses<'db>>, Diagnostics) { - let (bounds, diags) = type_alias_bounds_with_diagnostics_query(db, type_alias); - return (bounds.get(), diags.clone()); +) -> EarlyBinder<'db, &'db [Clause<'db>]> { + let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, _) = + type_alias_bounds_with_diagnostics(db, type_alias); + predicates.map_bound(|it| &it.as_slice()[..assoc_ty_bounds_start as usize]) +} + +#[derive(PartialEq, Eq, Debug, Hash)] +struct TypeAliasBounds { + predicates: T, + assoc_ty_bounds_start: u32, +} + +fn type_alias_bounds_with_diagnostics<'db>( + db: &'db dyn HirDatabase, + type_alias: TypeAliasId, +) -> (TypeAliasBounds>>, Diagnostics) { + let (TypeAliasBounds { predicates, assoc_ty_bounds_start }, diags) = + type_alias_bounds_with_diagnostics_query(db, type_alias); + return ( + TypeAliasBounds { + predicates: predicates.get(), + assoc_ty_bounds_start: *assoc_ty_bounds_start, + }, + diags.clone(), + ); #[salsa::tracked(returns(ref))] pub fn type_alias_bounds_with_diagnostics_query<'db>( db: &'db dyn HirDatabase, type_alias: TypeAliasId, - ) -> (StoredEarlyBinder, Diagnostics) { + ) -> (TypeAliasBounds>, Diagnostics) { let type_alias_data = db.type_alias_signature(type_alias); let resolver = hir_def::resolver::HasResolver::resolver(type_alias, db); let mut ctx = TyLoweringContext::new( @@ -1727,10 +1811,18 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( let interner_ty = Ty::new_projection_from_args(interner, def_id, item_args); let mut bounds = Vec::new(); + let mut assoc_ty_bounds = Vec::new(); for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, interner_ty, false).for_each(|pred| { - bounds.push(pred); - }); + ctx.lower_type_bound(bound, interner_ty, false).for_each( + |(pred, source)| match source { + GenericPredicateSource::SelfOnly => { + bounds.push(pred); + } + GenericPredicateSource::AssocTyBound => { + assoc_ty_bounds.push(pred); + } + }, + ); } if !ctx.unsized_types.contains(&interner_ty) { @@ -1745,8 +1837,14 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( }; } + let assoc_ty_bounds_start = bounds.len() as u32; + bounds.extend(assoc_ty_bounds); + ( - StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), + TypeAliasBounds { + predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&bounds).store()), + assoc_ty_bounds_start, + }, create_diagnostics(ctx.diagnostics), ) } @@ -1754,11 +1852,15 @@ pub(crate) fn type_alias_bounds_with_diagnostics<'db>( #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct GenericPredicates { - // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait predicate for the - // parent. Then come the explicit predicates for the parent, then the explicit trait predicate for the child, + // The order is the following: first, if `parent_is_trait == true`, comes the implicit trait + // predicate for the parent. Then come the bounds of the associated types of the parents, + // then the explicit, self-only predicates for the parent, then the explicit, self-only trait + // predicate for the child, then the bounds of the associated types of the child, // then the implicit trait predicate for the child, if `is_trait` is `true`. predicates: StoredEarlyBinder, + parent_explicit_self_predicates_start: u32, own_predicates_start: u32, + own_assoc_ty_bounds_start: u32, is_trait: bool, parent_is_trait: bool, } @@ -1782,7 +1884,15 @@ impl GenericPredicates { pub(crate) fn from_explicit_own_predicates( predicates: StoredEarlyBinder, ) -> Self { - Self { predicates, own_predicates_start: 0, is_trait: false, parent_is_trait: false } + let len = predicates.get().skip_binder().len() as u32; + Self { + predicates, + parent_explicit_self_predicates_start: 0, + own_predicates_start: 0, + own_assoc_ty_bounds_start: len, + is_trait: false, + parent_is_trait: false, + } } #[inline] @@ -1814,6 +1924,14 @@ impl GenericPredicates { Self::query(db, def).explicit_predicates() } + #[inline] + pub fn query_explicit_implied<'db>( + db: &'db dyn HirDatabase, + def: GenericDefId, + ) -> EarlyBinder<'db, &'db [Clause<'db>]> { + Self::query(db, def).explicit_implied_predicates() + } + #[inline] pub fn all_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { self.predicates.get().map_bound(|it| it.as_slice()) @@ -1824,9 +1942,18 @@ impl GenericPredicates { self.predicates.get().map_bound(|it| &it.as_slice()[self.own_predicates_start as usize..]) } - /// Returns the predicates, minus the implicit `Self: Trait` predicate for a trait. + /// Returns the predicates, minus the implicit `Self: Trait` predicate and bounds of the + /// associated types for a trait. #[inline] pub fn explicit_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { + self.predicates.get().map_bound(|it| { + &it.as_slice()[self.parent_explicit_self_predicates_start as usize + ..self.own_assoc_ty_bounds_start as usize] + }) + } + + #[inline] + pub fn explicit_implied_predicates(&self) -> EarlyBinder<'_, &[Clause<'_>]> { self.predicates.get().map_bound(|it| { &it.as_slice()[usize::from(self.parent_is_trait)..it.len() - usize::from(self.is_trait)] }) @@ -1902,26 +2029,22 @@ where ); let sized_trait = ctx.lang_items.Sized; - let mut predicates = Vec::new(); + // We need to lower parents and self separately - see the comment below lowering of implicit + // `Sized` predicates for why. + let mut own_predicates = Vec::new(); + let mut parent_predicates = Vec::new(); + let mut own_assoc_ty_bounds = Vec::new(); + let mut parent_assoc_ty_bounds = Vec::new(); let all_generics = std::iter::successors(Some(&generics), |generics| generics.parent_generics()) .collect::>(); - let mut is_trait = false; - let mut parent_is_trait = false; - if all_generics.len() > 1 { - add_implicit_trait_predicate( - interner, - all_generics.last().unwrap().def(), - predicate_filter, - &mut predicates, - &mut parent_is_trait, - ); - } - // We need to lower parent predicates first - see the comment below lowering of implicit `Sized` predicates - // for why. - let mut own_predicates_start = 0; + let own_implicit_trait_predicate = implicit_trait_predicate(interner, def, predicate_filter); + let parent_implicit_trait_predicate = if all_generics.len() > 1 { + implicit_trait_predicate(interner, all_generics.last().unwrap().def(), predicate_filter) + } else { + None + }; for &maybe_parent_generics in all_generics.iter().rev() { - let current_def_predicates_start = predicates.len(); // Collect only diagnostics from the child, not including parents. ctx.diagnostics.clear(); @@ -1929,15 +2052,37 @@ where ctx.store = maybe_parent_generics.store(); for pred in maybe_parent_generics.where_predicates() { tracing::debug!(?pred); - predicates.extend(ctx.lower_where_predicate( - pred, - false, - maybe_parent_generics, - predicate_filter, - )); + for (pred, source) in + ctx.lower_where_predicate(pred, false, maybe_parent_generics, predicate_filter) + { + match source { + GenericPredicateSource::SelfOnly => { + if maybe_parent_generics.def() == def { + own_predicates.push(pred); + } else { + parent_predicates.push(pred); + } + } + GenericPredicateSource::AssocTyBound => { + if maybe_parent_generics.def() == def { + own_assoc_ty_bounds.push(pred); + } else { + parent_assoc_ty_bounds.push(pred); + } + } + } + } } - push_const_arg_has_type_predicates(db, &mut predicates, maybe_parent_generics); + if maybe_parent_generics.def() == def { + push_const_arg_has_type_predicates(db, &mut own_predicates, maybe_parent_generics); + } else { + push_const_arg_has_type_predicates( + db, + &mut parent_predicates, + maybe_parent_generics, + ); + } if let Some(sized_trait) = sized_trait { let mut add_sized_clause = |param_idx, param_id, param_data| { @@ -1971,7 +2116,11 @@ where }), )), )); - predicates.push(clause); + if maybe_parent_generics.def() == def { + own_predicates.push(clause); + } else { + parent_predicates.push(clause); + } }; let parent_params_len = maybe_parent_generics.len_parent(); maybe_parent_generics.iter_self().enumerate().for_each( @@ -1990,30 +2139,55 @@ where // predicates before lowering the child, as a child cannot define a `?Sized` predicate for its parent. // But we do have to lower the parent first. } - - if maybe_parent_generics.def() == def { - own_predicates_start = current_def_predicates_start as u32; - } } - add_implicit_trait_predicate(interner, def, predicate_filter, &mut predicates, &mut is_trait); - let diagnostics = create_diagnostics(ctx.diagnostics); + + // The order is: + // + // 1. parent implicit trait pred + // 2. parent assoc bounds + // 3. parent self only preds + // 4. own self only preds + // 5. own assoc ty bounds + // 6. own implicit trait pred + // + // The purpose of this is to index the slice of the followings, without making extra `Vec`s or + // iterators: + // - explicit self only predicates, of own or own + self + // - explicit predicates, of own or own + self + let predicates = parent_implicit_trait_predicate + .iter() + .chain(parent_assoc_ty_bounds.iter()) + .chain(parent_predicates.iter()) + .chain(own_predicates.iter()) + .chain(own_assoc_ty_bounds.iter()) + .chain(own_implicit_trait_predicate.iter()) + .copied() + .collect::>(); + let parent_is_trait = parent_implicit_trait_predicate.is_some(); + let is_trait = own_implicit_trait_predicate.is_some(); + let parent_explicit_self_predicates_start = + parent_is_trait as u32 + parent_assoc_ty_bounds.len() as u32; + let own_predicates_start = + parent_explicit_self_predicates_start + parent_predicates.len() as u32; + let own_assoc_ty_bounds_start = own_predicates_start + own_predicates.len() as u32; + let predicates = GenericPredicates { + parent_explicit_self_predicates_start, own_predicates_start, + own_assoc_ty_bounds_start, is_trait, parent_is_trait, predicates: StoredEarlyBinder::bind(Clauses::new_from_slice(&predicates).store()), }; return (predicates, diagnostics); - fn add_implicit_trait_predicate<'db>( + fn implicit_trait_predicate<'db>( interner: DbInterner<'db>, def: GenericDefId, predicate_filter: PredicateFilter, - predicates: &mut Vec>, - set_is_trait: &mut bool, - ) { + ) -> Option> { // For traits, add `Self: Trait` predicate. This is // not part of the predicates that a user writes, but it // is something that one must prove in order to invoke a @@ -2029,8 +2203,9 @@ where if let GenericDefId::TraitId(def_id) = def && predicate_filter == PredicateFilter::All { - *set_is_trait = true; - predicates.push(TraitRef::identity(interner, def_id.into()).upcast(interner)); + Some(TraitRef::identity(interner, def_id.into()).upcast(interner)) + } else { + None } } } @@ -2327,7 +2502,7 @@ pub(crate) fn associated_ty_item_bounds<'db>( let mut bounds = Vec::new(); for bound in &type_alias_data.bounds { - ctx.lower_type_bound(bound, self_ty, false).for_each(|pred| { + ctx.lower_type_bound(bound, self_ty, false).for_each(|(pred, _)| { if let Some(bound) = pred .kind() .map_bound(|c| match c { 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 a79f547c2a44..b77aeab62d15 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 @@ -32,7 +32,8 @@ use crate::{ db::HirDatabase, generics::{Generics, generics}, lower::{ - LifetimeElisionKind, PathDiagnosticCallbackData, named_associated_type_shorthand_candidates, + GenericPredicateSource, LifetimeElisionKind, PathDiagnosticCallbackData, + named_associated_type_shorthand_candidates, }, next_solver::{ Binder, Clause, Const, DbInterner, ErrorGuaranteed, GenericArg, GenericArgs, Predicate, @@ -853,7 +854,8 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { pub(super) fn assoc_type_bindings_from_type_bound<'c>( mut self, trait_ref: TraitRef<'db>, - ) -> Option> + use<'a, 'b, 'c, 'db>> { + ) -> Option, GenericPredicateSource)> + use<'a, 'b, 'c, 'db>> + { let interner = self.ctx.interner; self.current_or_prev_segment.args_and_bindings.map(|args_and_bindings| { args_and_bindings.bindings.iter().enumerate().flat_map(move |(binding_idx, binding)| { @@ -921,21 +923,29 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { ), )), )); - predicates.push(pred); + predicates.push((pred, GenericPredicateSource::SelfOnly)); } } }) } for bound in binding.bounds.iter() { - predicates.extend(self.ctx.lower_type_bound( - bound, - Ty::new_alias( - self.ctx.interner, - AliasTyKind::Projection, - AliasTy::new_from_args(self.ctx.interner, associated_ty.into(), args), - ), - false, - )); + predicates.extend( + self.ctx + .lower_type_bound( + bound, + Ty::new_alias( + self.ctx.interner, + AliasTyKind::Projection, + AliasTy::new_from_args( + self.ctx.interner, + associated_ty.into(), + args, + ), + ), + false, + ) + .map(|(pred, _)| (pred, GenericPredicateSource::AssocTyBound)), + ); } predicates }) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs index 2a3df1d32a30..e17bdac68cdd 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/interner.rs @@ -41,7 +41,8 @@ use crate::{ AdtIdWrapper, AnyImplId, BoundConst, CallableIdWrapper, CanonicalVarKind, ClosureIdWrapper, CoroutineIdWrapper, Ctor, FnSig, FxIndexMap, GeneralConstIdWrapper, OpaqueTypeKey, RegionAssumptions, SimplifiedType, SolverContext, SolverDefIds, TraitIdWrapper, - TypeAliasIdWrapper, UnevaluatedConst, util::explicit_item_bounds, + TypeAliasIdWrapper, UnevaluatedConst, + util::{explicit_item_bounds, explicit_item_self_bounds}, }, }; @@ -1421,7 +1422,7 @@ impl<'db> Interner for DbInterner<'db> { self, def_id: Self::DefId, ) -> EarlyBinder> { - explicit_item_bounds(self, def_id) + explicit_item_self_bounds(self, def_id) .map_bound(|bounds| elaborate(self, bounds).filter_only_self()) } @@ -1500,7 +1501,7 @@ impl<'db> Interner for DbInterner<'db> { } } - predicates_of(self.db, def_id).explicit_predicates().map_bound(|predicates| { + predicates_of(self.db, def_id).explicit_implied_predicates().map_bound(|predicates| { predicates .iter() .copied() diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs index 34ecfed08f29..9a1b476976e3 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/util.rs @@ -455,6 +455,21 @@ pub fn explicit_item_bounds<'db>( clauses.map_bound(|clauses| clauses.iter().copied()) } +pub fn explicit_item_self_bounds<'db>( + interner: DbInterner<'db>, + def_id: SolverDefId, +) -> EarlyBinder<'db, impl DoubleEndedIterator> + ExactSizeIterator> { + let db = interner.db(); + let clauses = match def_id { + SolverDefId::TypeAliasId(type_alias) => { + crate::lower::type_alias_self_bounds(db, type_alias) + } + SolverDefId::InternedOpaqueTyId(id) => id.self_predicates(db), + _ => panic!("Unexpected GenericDefId"), + }; + clauses.map_bound(|clauses| clauses.iter().copied()) +} + pub struct ContainsTypeErrors; impl<'db> TypeVisitor> for ContainsTypeErrors { diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs index a4554673cdd5..be6ab23ad761 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs @@ -750,3 +750,63 @@ fn main() { "#]], ); } + +#[test] +fn regression_19339() { + check_infer( + r#" +trait Bar { + type Baz; + + fn baz(&self) -> Self::Baz; +} + +trait Foo { + type Bar; + + fn bar(&self) -> Self::Bar; +} + +trait FooFactory { + type Output: Foo>; + + fn foo(&self) -> Self::Output; + + fn foo_rpit(&self) -> impl Foo>; +} + +fn test1(foo: impl Foo>) { + let baz = foo.bar().baz(); +} + +fn test2(factory: T) { + let baz = factory.foo().bar().baz(); + let baz = factory.foo_rpit().bar().baz(); +} +"#, + expect![[r#" + 39..43 'self': &'? Self + 101..105 'self': &'? Self + 198..202 'self': &'? Self + 239..243 'self': &'? Self + 290..293 'foo': impl Foo + ?Sized + 325..359 '{ ...z(); }': () + 335..338 'baz': u8 + 341..344 'foo': impl Foo + ?Sized + 341..350 'foo.bar()': impl Bar + 341..356 'foo.bar().baz()': u8 + 385..392 'factory': T + 397..487 '{ ...z(); }': () + 407..410 'baz': u8 + 413..420 'factory': T + 413..426 'factory.foo()': ::Output + 413..432 'factor....bar()': <::Output as Foo>::Bar + 413..438 'factor....baz()': u8 + 448..451 'baz': u8 + 454..461 'factory': T + 454..472 'factor...rpit()': impl Foo + Bar + ?Sized + 454..478 'factor....bar()': + ?Sized as Foo>::Bar + 454..484 'factor....baz()': u8 + "#]], + ); +} From e2b507233c3224187dd5ef53a9044a430e4f9129 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 13:05:47 +1100 Subject: [PATCH 012/319] Add ProjectJsonTargetSpec.project_root Needed to support flychecking in a later diff --- src/tools/rust-analyzer/crates/rust-analyzer/src/global_state.rs | 1 + src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs | 1 + 2 files changed, 2 insertions(+) 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 9beab3c0e45c..81d60179cc94 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 @@ -833,6 +833,7 @@ impl GlobalStateSnapshot { label: build.label, target_kind: build.target_kind, shell_runnables: project.runnables().to_owned(), + project_root: project.project_root().to_owned(), })); } ProjectWorkspaceKind::DetachedFile { .. } => {} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index e0f95a7830ea..8452b6493e87 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -68,6 +68,7 @@ pub(crate) struct ProjectJsonTargetSpec { pub(crate) label: String, pub(crate) target_kind: TargetKind, pub(crate) shell_runnables: Vec, + pub(crate) project_root: AbsPathBuf, } impl ProjectJsonTargetSpec { From ff94498ef2a831c47d9237262f612a3bb530bd69 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 14:02:07 +1100 Subject: [PATCH 013/319] project-model: Helpers for traversing dep graph in ProjectJson Needed for all_workspace_dependencies_for_package implementation. --- .../crates/project-model/src/project_json.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index b3478d2cfe03..adc9b1a49fd4 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -78,6 +78,13 @@ pub struct ProjectJson { runnables: Vec, } +impl std::ops::Index for ProjectJson { + type Output = Crate; + fn index(&self, index: CrateArrayIdx) -> &Self::Output { + &self.crates[index.0] + } +} + impl ProjectJson { /// Create a new ProjectJson instance. /// @@ -218,6 +225,14 @@ impl ProjectJson { .find(|build| build.build_file.as_std_path() == path) } + pub fn crate_by_label(&self, label: &str) -> Option<&Crate> { + // this is fast enough for now, but it's unfortunate that this is O(crates). + self.crates + .iter() + .filter(|krate| krate.is_workspace_member) + .find(|krate| krate.build.as_ref().is_some_and(|build| build.label == label)) + } + /// Returns the path to the project's manifest or root folder, if no manifest exists. pub fn manifest_or_root(&self) -> &AbsPath { self.manifest.as_ref().map_or(&self.project_root, |manifest| manifest.as_ref()) @@ -258,6 +273,12 @@ pub struct Crate { pub build: Option, } +impl Crate { + pub fn iter_deps(&self) -> impl ExactSizeIterator { + self.deps.iter().map(|dep| dep.krate) + } +} + /// Additional, build-specific data about a crate. #[derive(Clone, Debug, Eq, PartialEq)] pub struct Build { From 3083bde73d2e7a1e2227ef77f1f248598005ba08 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 14:02:25 +1100 Subject: [PATCH 014/319] project-model: Don't do O(n) clones as well as O(n) search --- .../rust-analyzer/crates/project-model/src/project_json.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index adc9b1a49fd4..8fe7885983a7 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -221,8 +221,9 @@ impl ProjectJson { self.crates .iter() .filter(|krate| krate.is_workspace_member) - .filter_map(|krate| krate.build.clone()) + .filter_map(|krate| krate.build.as_ref()) .find(|build| build.build_file.as_std_path() == path) + .cloned() } pub fn crate_by_label(&self, label: &str) -> Option<&Crate> { From ac641771a83d23f965b3dcb180bb854de21002fb Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 015/319] project-model: Return crate by reference --- .../rust-analyzer/crates/project-model/src/project_json.rs | 3 +-- .../rust-analyzer/crates/rust-analyzer/src/global_state.rs | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 8fe7885983a7..a7fba6936244 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -202,12 +202,11 @@ impl ProjectJson { &self.project_root } - pub fn crate_by_root(&self, root: &AbsPath) -> Option { + pub fn crate_by_root(&self, root: &AbsPath) -> Option<&Crate> { self.crates .iter() .filter(|krate| krate.is_workspace_member) .find(|krate| krate.root_module == root) - .cloned() } /// Returns the path to the project's manifest, if it exists. 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 81d60179cc94..99dc8bce062e 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 @@ -825,7 +825,7 @@ impl GlobalStateSnapshot { let Some(krate) = project.crate_by_root(path) else { continue; }; - let Some(build) = krate.build else { + let Some(build) = krate.build.clone() else { continue; }; From 3b97d38702700a274b22dcd9cc21d836d4dc1ce0 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Tue, 2 Dec 2025 14:11:52 +1100 Subject: [PATCH 016/319] Fix misuse of ? This exited the whole loop instead of having continue semantics and continuing to find workspaces. So wrap in find_map. --- .../crates/rust-analyzer/src/global_state.rs | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) 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 99dc8bce062e..68d65cdee6f3 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 @@ -847,21 +847,18 @@ impl GlobalStateSnapshot { &self, package: &Arc, ) -> Option>> { - for workspace in self.workspaces.iter() { - match &workspace.kind { - ProjectWorkspaceKind::Cargo { cargo, .. } - | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { - let package = cargo.packages().find(|p| cargo[*p].id == *package)?; + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { + let package = cargo.packages().find(|p| cargo[*p].id == *package)?; - return cargo[package] - .all_member_deps - .as_ref() - .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); - } - _ => {} + return cargo[package] + .all_member_deps + .as_ref() + .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); } - } - None + _ => None, + }) } pub(crate) fn file_exists(&self, file_id: FileId) -> bool { From 327ea186f07053f70b4bf6af394e6f8ec80f303e Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 017/319] flycheck: Make the flycheckable unit a flycheck::PackageSpecifier enum You should be able to flycheck a ProjectJson crate based on its build label. This paves the way for that. Context: I don't think this has been working for some time. It used to be that we would use cargo to build ProjectJson crates. Support for ProjectJson seems to have been somewhat steamrolled in PR 18845 (e4bf6e1bc36e4cbc8a36d7911788176eb9fac76e). --- .../crates/rust-analyzer/src/diagnostics.rs | 16 +++-- .../crates/rust-analyzer/src/flycheck.rs | 69 +++++++++++++++---- .../crates/rust-analyzer/src/global_state.rs | 50 ++++++++++---- .../src/handlers/notification.rs | 42 +++++++---- 4 files changed, 130 insertions(+), 47 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 4a247800af9d..712960f13d7e 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -3,7 +3,6 @@ pub(crate) mod flycheck_to_proto; use std::mem; -use cargo_metadata::PackageId; use ide::FileId; use ide_db::{FxHashMap, base_db::DbPanicContext}; use itertools::Itertools; @@ -12,10 +11,13 @@ use smallvec::SmallVec; use stdx::iter_eq_by; use triomphe::Arc; -use crate::{global_state::GlobalStateSnapshot, lsp, lsp_ext, main_loop::DiagnosticsTaskKind}; +use crate::{ + flycheck::PackageSpecifier, global_state::GlobalStateSnapshot, lsp, lsp_ext, + main_loop::DiagnosticsTaskKind, +}; pub(crate) type CheckFixes = - Arc>, FxHashMap>>>>; + Arc, FxHashMap>>>>; #[derive(Debug, Default, Clone)] pub struct DiagnosticsMapConfig { @@ -29,7 +31,7 @@ pub(crate) type DiagnosticsGeneration = usize; #[derive(Debug, Clone, Default)] pub(crate) struct WorkspaceFlycheckDiagnostic { - pub(crate) per_package: FxHashMap>, PackageFlycheckDiagnostic>, + pub(crate) per_package: FxHashMap, PackageFlycheckDiagnostic>, } #[derive(Debug, Clone)] @@ -85,7 +87,7 @@ impl DiagnosticCollection { pub(crate) fn clear_check_for_package( &mut self, flycheck_id: usize, - package_id: Arc, + package_id: PackageSpecifier, ) { let Some(check) = self.check.get_mut(flycheck_id) else { return; @@ -124,7 +126,7 @@ impl DiagnosticCollection { pub(crate) fn clear_check_older_than_for_package( &mut self, flycheck_id: usize, - package_id: Arc, + package_id: PackageSpecifier, generation: DiagnosticsGeneration, ) { let Some(check) = self.check.get_mut(flycheck_id) else { @@ -154,7 +156,7 @@ impl DiagnosticCollection { &mut self, flycheck_id: usize, generation: DiagnosticsGeneration, - package_id: &Option>, + package_id: &Option, file_id: FileId, diagnostic: lsp_types::Diagnostic, fix: Option>, 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 b06264169188..2819ae98daaf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -195,9 +195,9 @@ impl FlycheckHandle { /// Schedule a re-start of the cargo check worker to do a package wide check. pub(crate) fn restart_for_package( &self, - package: Arc, + package: PackageSpecifier, target: Option, - workspace_deps: Option>>, + workspace_deps: Option>, ) { let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender @@ -233,7 +233,7 @@ pub(crate) enum ClearDiagnosticsKind { #[derive(Debug)] pub(crate) enum ClearScope { Workspace, - Package(Arc), + Package(PackageSpecifier), } pub(crate) enum FlycheckMessage { @@ -243,7 +243,7 @@ pub(crate) enum FlycheckMessage { generation: DiagnosticsGeneration, workspace_root: Arc, diagnostic: Diagnostic, - package_id: Option>, + package_id: Option, }, /// Request clearing all outdated diagnostics. @@ -295,7 +295,32 @@ pub(crate) enum Progress { enum FlycheckScope { Workspace, - Package { package: Arc, workspace_deps: Option>> }, + Package { + // Either a cargo package or a $label in rust-project.check.overrideCommand + package: PackageSpecifier, + workspace_deps: Option>, + }, +} + +#[derive(Debug, Hash, PartialEq, Eq, Clone)] +pub(crate) enum PackageSpecifier { + Cargo { + /// The one in Cargo.toml, assumed to work with `cargo check -p {}` etc + package_id: Arc, + }, + BuildInfo { + /// If a `build` field is present in rust-project.json, its label field + label: String, + }, +} + +impl PackageSpecifier { + pub(crate) fn as_str(&self) -> &str { + match self { + Self::Cargo { package_id } => &package_id.repr, + Self::BuildInfo { label } => label, + } + } } enum StateChange { @@ -331,7 +356,7 @@ struct FlycheckActor { command_handle: Option>, /// The receiver side of the channel mentioned above. command_receiver: Option>, - diagnostics_cleared_for: FxHashSet>, + diagnostics_cleared_for: FxHashSet, diagnostics_received: DiagnosticsReceived, } @@ -564,7 +589,10 @@ impl FlycheckActor { msg.target.kind.iter().format_with(", ", |kind, f| f(&kind)), ))); let package_id = Arc::new(msg.package_id); - if self.diagnostics_cleared_for.insert(package_id.clone()) { + if self + .diagnostics_cleared_for + .insert(PackageSpecifier::Cargo { package_id: package_id.clone() }) + { tracing::trace!( flycheck_id = self.id, package_id = package_id.repr, @@ -572,7 +600,9 @@ impl FlycheckActor { ); self.send(FlycheckMessage::ClearDiagnostics { id: self.id, - kind: ClearDiagnosticsKind::All(ClearScope::Package(package_id)), + kind: ClearDiagnosticsKind::All(ClearScope::Package( + PackageSpecifier::Cargo { package_id }, + )), }); } } @@ -580,7 +610,7 @@ impl FlycheckActor { tracing::trace!( flycheck_id = self.id, message = diagnostic.message, - package_id = package_id.as_ref().map(|it| &it.repr), + package_id = package_id.as_ref().map(|it| it.as_str()), "diagnostic received" ); if self.diagnostics_received == DiagnosticsReceived::No { @@ -590,7 +620,7 @@ impl FlycheckActor { if self.diagnostics_cleared_for.insert(package_id.clone()) { tracing::trace!( flycheck_id = self.id, - package_id = package_id.repr, + package_id = package_id.as_str(), "clearing diagnostics" ); self.send(FlycheckMessage::ClearDiagnostics { @@ -666,7 +696,18 @@ impl FlycheckActor { match scope { FlycheckScope::Workspace => cmd.arg("--workspace"), - FlycheckScope::Package { package, .. } => cmd.arg("-p").arg(&package.repr), + FlycheckScope::Package { + package: PackageSpecifier::Cargo { package_id }, + .. + } => cmd.arg("-p").arg(&package_id.repr), + FlycheckScope::Package { + package: PackageSpecifier::BuildInfo { .. }, .. + } => { + // No way to flycheck this single package. All we have is a build label. + // There's no way to really say whether this build label happens to be + // a cargo canonical name, so we won't try. + return None; + } }; if let Some(tgt) = target { @@ -748,7 +789,7 @@ impl FlycheckActor { #[allow(clippy::large_enum_variant)] enum CargoCheckMessage { CompilerArtifact(cargo_metadata::Artifact), - Diagnostic { diagnostic: Diagnostic, package_id: Option> }, + Diagnostic { diagnostic: Diagnostic, package_id: Option }, } struct CargoCheckParser; @@ -767,7 +808,9 @@ impl JsonLinesParser for CargoCheckParser { cargo_metadata::Message::CompilerMessage(msg) => { Some(CargoCheckMessage::Diagnostic { diagnostic: msg.message, - package_id: Some(Arc::new(msg.package_id)), + package_id: Some(PackageSpecifier::Cargo { + package_id: Arc::new(msg.package_id), + }), }) } _ => None, 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 68d65cdee6f3..0cfd0a141bae 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 @@ -9,7 +9,6 @@ use std::{ time::{Duration, Instant}, }; -use cargo_metadata::PackageId; use crossbeam_channel::{Receiver, Sender, unbounded}; use hir::ChangeWithProcMacros; use ide::{Analysis, AnalysisHost, Cancellable, FileId, SourceRootId}; @@ -36,7 +35,7 @@ use crate::{ config::{Config, ConfigChange, ConfigErrors, RatomlFileKind}, diagnostics::{CheckFixes, DiagnosticCollection}, discover, - flycheck::{FlycheckHandle, FlycheckMessage}, + flycheck::{FlycheckHandle, FlycheckMessage, PackageSpecifier}, line_index::{LineEndings, LineIndex}, lsp::{from_proto, to_proto::url_from_abs_path}, lsp_ext, @@ -845,20 +844,43 @@ impl GlobalStateSnapshot { pub(crate) fn all_workspace_dependencies_for_package( &self, - package: &Arc, - ) -> Option>> { - self.workspaces.iter().find_map(|workspace| match &workspace.kind { - ProjectWorkspaceKind::Cargo { cargo, .. } - | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { - let package = cargo.packages().find(|p| cargo[*p].id == *package)?; + package: &PackageSpecifier, + ) -> Option> { + match package { + PackageSpecifier::Cargo { package_id } => { + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Cargo { cargo, .. } + | ProjectWorkspaceKind::DetachedFile { cargo: Some((cargo, _, _)), .. } => { + let package = cargo.packages().find(|p| cargo[*p].id == *package_id)?; - return cargo[package] - .all_member_deps - .as_ref() - .map(|deps| deps.iter().map(|dep| cargo[*dep].id.clone()).collect()); + cargo[package].all_member_deps.as_ref().map(|deps| { + deps.iter() + .map(|dep| cargo[*dep].id.clone()) + .map(|p| PackageSpecifier::Cargo { package_id: p }) + .collect() + }) + } + _ => None, + }) } - _ => None, - }) + PackageSpecifier::BuildInfo { label } => { + self.workspaces.iter().find_map(|workspace| match &workspace.kind { + ProjectWorkspaceKind::Json(p) => { + let krate = p.crate_by_label(label)?; + Some( + krate + .iter_deps() + .filter_map(|dep| p[dep].build.as_ref()) + .map(|build| PackageSpecifier::BuildInfo { + label: build.label.clone(), + }) + .collect(), + ) + } + _ => None, + }) + } + } } pub(crate) fn file_exists(&self, file_id: FileId) -> bool { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 4a6544508ff4..57adbbfe72a7 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -18,7 +18,7 @@ use vfs::{AbsPathBuf, ChangeKind, VfsPath}; use crate::{ config::{Config, ConfigChange}, - flycheck::{InvocationStrategy, Target}, + flycheck::{InvocationStrategy, PackageSpecifier, Target}, global_state::{FetchWorkspaceRequest, GlobalState}, lsp::{from_proto, utils::apply_document_changes}, lsp_ext::{self, RunFlycheckParams}, @@ -328,22 +328,32 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } InvocationStrategy::PerWorkspace => { Box::new(move || { - let target = TargetSpec::for_file(&world, file_id)?.and_then(|it| { + let target = TargetSpec::for_file(&world, file_id)?.map(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { - TargetSpec::Cargo(c) => (c.target, c.workspace_root, c.package_id), - _ => return None, + TargetSpec::Cargo(c) => ( + Some(c.target), + c.workspace_root, + PackageSpecifier::Cargo { package_id: c.package_id }, + ), + TargetSpec::ProjectJson(p) => ( + None, + p.project_root, + PackageSpecifier::BuildInfo { label: p.label.clone() }, + ), }; - let tgt = match tgt_kind { - project_model::TargetKind::Bin => Target::Bin(tgt_name), - project_model::TargetKind::Example => Target::Example(tgt_name), - project_model::TargetKind::Test => Target::Test(tgt_name), - project_model::TargetKind::Bench => Target::Benchmark(tgt_name), - _ => return Some((None, root, package)), - }; + let tgt = tgt_name.and_then(|tgt_name| { + Some(match tgt_kind { + project_model::TargetKind::Bin => Target::Bin(tgt_name), + project_model::TargetKind::Example => Target::Example(tgt_name), + project_model::TargetKind::Test => Target::Test(tgt_name), + project_model::TargetKind::Bench => Target::Benchmark(tgt_name), + _ => return None, + }) + }); - Some((Some(tgt), root, package)) + (tgt, root, package) }); tracing::debug!(?target, "flycheck target"); // we have a specific non-library target, attempt to only check that target, nothing @@ -365,7 +375,13 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { cargo: Some((cargo, _, _)), .. } => *cargo.workspace_root() == root, - _ => false, + project_model::ProjectWorkspaceKind::Json(p) => { + *p.project_root() == root + } + project_model::ProjectWorkspaceKind::DetachedFile { + cargo: None, + .. + } => false, }); if let Some(idx) = package_workspace_idx { let workspace_deps = From 95a07dbfa06ad7c30d4e9fa2f85de2b991d610f3 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 018/319] project-model: Introduce RunnableKind::Flycheck We need to distinguish from RunnableKind::Check, which is human-readable. --- .../crates/project-model/src/project_json.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index a7fba6936244..536f170e1192 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -246,6 +246,10 @@ impl ProjectJson { pub fn runnables(&self) -> &[Runnable] { &self.runnables } + + pub fn runnable_template(&self, kind: RunnableKind) -> Option<&Runnable> { + self.runnables().iter().find(|r| r.kind == kind) + } } /// A crate points to the root module of a crate and lists the dependencies of the crate. This is @@ -349,6 +353,7 @@ pub struct Runnable { /// The kind of runnable. #[derive(Debug, Clone, PartialEq, Eq)] pub enum RunnableKind { + /// `cargo check`, basically, with human-readable output. Check, /// Can run a binary. @@ -356,6 +361,10 @@ pub enum RunnableKind { /// Run a single test. TestOne, + + /// Template for checking a target, emitting rustc JSON diagnostics. + /// May include {label} which will get the label from the `build` section of a crate. + Flycheck, } #[derive(Serialize, Deserialize, Debug, Clone, Eq, PartialEq)] @@ -462,6 +471,7 @@ pub struct RunnableData { #[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)] #[serde(rename_all = "camelCase")] pub enum RunnableKindData { + Flycheck, Check, Run, TestOne, @@ -532,6 +542,7 @@ impl From for RunnableKind { RunnableKindData::Check => RunnableKind::Check, RunnableKindData::Run => RunnableKind::Run, RunnableKindData::TestOne => RunnableKind::TestOne, + RunnableKindData::Flycheck => RunnableKind::Flycheck, } } } From 2a899bb119bd9e33eadce3b7179cb1b8bc49fd78 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 019/319] flycheck: Use RunnableKind::Flycheck from ProjectJson to flycheck This adds a substitution helper to get the right behaviour re {label} and $saved_file. --- .../crates/rust-analyzer/src/flycheck.rs | 226 +++++++++++++++++- .../crates/rust-analyzer/src/reload.rs | 19 +- 2 files changed, 238 insertions(+), 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 2819ae98daaf..1b1e3344e25f 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -14,6 +14,7 @@ use ide_db::FxHashSet; use itertools::Itertools; use paths::{AbsPath, AbsPathBuf, Utf8Path, Utf8PathBuf}; use project_model::TargetDirectoryConfig; +use project_model::project_json; use rustc_hash::FxHashMap; use serde::Deserialize as _; use serde_derive::Deserialize; @@ -89,6 +90,24 @@ impl CargoOptions { } } +/// The flycheck config from a rust-project.json file or discoverConfig JSON output. +#[derive(Debug, Default)] +pub(crate) struct FlycheckConfigJson { + /// The template with [project_json::RunnableKind::Flycheck] + pub single_template: Option, +} + +impl FlycheckConfigJson { + pub(crate) fn any_configured(&self) -> bool { + // self.workspace_template.is_some() || + self.single_template.is_some() + } +} + +/// The flycheck config from rust-analyzer's own configuration. +/// +/// We rely on this when rust-project.json does not specify a flycheck runnable +/// #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum FlycheckConfig { CargoCommand { @@ -128,7 +147,7 @@ impl fmt::Display for FlycheckConfig { // in the IDE (e.g. in the VS Code status bar). let display_args = args .iter() - .map(|arg| if arg == SAVED_FILE_PLACEHOLDER { "..." } else { arg }) + .map(|arg| if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { "..." } else { arg }) .collect::>(); write!(f, "{command} {}", display_args.join(" ")) @@ -156,6 +175,7 @@ impl FlycheckHandle { generation: Arc, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, @@ -166,6 +186,7 @@ impl FlycheckHandle { generation.load(Ordering::Relaxed), sender, config, + config_json, sysroot_root, workspace_root, manifest_path, @@ -341,6 +362,8 @@ struct FlycheckActor { generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, + manifest_path: Option, ws_target_dir: Option, /// Either the workspace root of the workspace we are flychecking, @@ -373,7 +396,66 @@ enum Event { CheckEvent(Option), } -pub(crate) const SAVED_FILE_PLACEHOLDER: &str = "$saved_file"; +/// This is stable behaviour. Don't change. +const SAVED_FILE_PLACEHOLDER_DOLLAR: &str = "$saved_file"; +const LABEL_INLINE: &str = "{label}"; +const SAVED_FILE_INLINE: &str = "{saved_file}"; + +struct Substitutions<'a> { + label: Option<&'a str>, + saved_file: Option<&'a str>, +} + +impl<'a> Substitutions<'a> { + /// If you have a runnable, and it has {label} in it somewhere, treat it as a template that + /// may be unsatisfied if you do not provide a label to substitute into it. Returns None in + /// that situation. Otherwise performs the requested substitutions. + /// + /// Same for {saved_file}. + /// + #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ + fn substitute( + self, + template: &project_json::Runnable, + extra_env: &std::collections::HashMap, H>, + ) -> Option { + let mut cmd = toolchain::command(&template.program, &template.cwd, extra_env); + for arg in &template.args { + if let Some(ix) = arg.find(LABEL_INLINE) { + if let Some(label) = self.label { + let mut arg = arg.to_string(); + arg.replace_range(ix..ix + LABEL_INLINE.len(), label); + cmd.arg(arg); + continue; + } else { + return None; + } + } + if let Some(ix) = arg.find(SAVED_FILE_INLINE) { + if let Some(saved_file) = self.saved_file { + let mut arg = arg.to_string(); + arg.replace_range(ix..ix + SAVED_FILE_INLINE.len(), saved_file); + cmd.arg(arg); + continue; + } else { + return None; + } + } + // Legacy syntax: full argument match + if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { + if let Some(saved_file) = self.saved_file { + cmd.arg(saved_file); + continue; + } else { + return None; + } + } + cmd.arg(arg); + } + cmd.current_dir(&template.cwd); + Some(cmd) + } +} impl FlycheckActor { fn new( @@ -381,6 +463,7 @@ impl FlycheckActor { generation: DiagnosticsGeneration, sender: Sender, config: FlycheckConfig, + config_json: FlycheckConfigJson, sysroot_root: Option, workspace_root: AbsPathBuf, manifest_path: Option, @@ -392,6 +475,7 @@ impl FlycheckActor { generation, sender, config, + config_json, sysroot_root, root: Arc::new(workspace_root), scope: FlycheckScope::Workspace, @@ -672,6 +756,29 @@ impl FlycheckActor { self.diagnostics_received = DiagnosticsReceived::No; } + fn explicit_check_command( + &self, + scope: &FlycheckScope, + saved_file: Option<&AbsPath>, + ) -> Option { + let label = match scope { + // We could add a runnable like "RunnableKind::FlycheckWorkspace". But generally + // if you're not running cargo, it's because your workspace is too big to check + // all at once. You can always use `check_overrideCommand` with no {label}. + FlycheckScope::Workspace => return None, + FlycheckScope::Package { package: PackageSpecifier::BuildInfo { label }, .. } => { + label.as_str() + } + FlycheckScope::Package { + package: PackageSpecifier::Cargo { package_id: label }, + .. + } => &label.repr, + }; + let template = self.config_json.single_template.as_ref()?; + let subs = Substitutions { label: Some(label), saved_file: saved_file.map(|x| x.as_str()) }; + subs.substitute(template, &FxHashMap::default()) + } + /// Construct a `Command` object for checking the user's code. If the user /// has specified a custom command with placeholders that we cannot fill, /// return None. @@ -683,6 +790,20 @@ impl FlycheckActor { ) -> Option { match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { + // Only use the rust-project.json's flycheck config when no check_overrideCommand + // is configured. In the FlycheckConcig::CustomCommand branch we will still do + // label substitution, but on the overrideCommand instead. + // + // There needs to be SOME way to override what your discoverConfig tool says, + // because to change the flycheck runnable there you may have to literally + // recompile the tool. + if self.config_json.any_configured() { + // Completely handle according to rust-project.json. + // We don't consider this to be "using cargo" so we will not apply any of the + // CargoOptions to the command. + return self.explicit_check_command(scope, saved_file); + } + let mut cmd = toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env); if let Some(sysroot_root) = &self.sysroot_root @@ -757,7 +878,7 @@ impl FlycheckActor { // we're saving a file, replace the placeholder in the arguments. if let Some(saved_file) = saved_file { for arg in args { - if arg == SAVED_FILE_PLACEHOLDER { + if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { cmd.arg(saved_file); } else { cmd.arg(arg); @@ -765,7 +886,7 @@ impl FlycheckActor { } } else { for arg in args { - if arg == SAVED_FILE_PLACEHOLDER { + if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { // The custom command has a $saved_file placeholder, // but we had an IDE event that wasn't a file save. Do nothing. return None; @@ -837,3 +958,100 @@ enum JsonMessage { Cargo(cargo_metadata::Message), Rustc(Diagnostic), } + +#[cfg(test)] +mod tests { + use ide_db::FxHashMap; + use itertools::Itertools; + use paths::Utf8Path; + use project_model::project_json; + + use crate::flycheck::Substitutions; + + #[test] + fn test_substitutions() { + let label = ":label"; + let saved_file = "file.rs"; + + // Runnable says it needs both; you need both. + assert_eq!(test_substitute(None, None, "{label} {saved_file}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{label} {saved_file}").as_deref(), None); + assert_eq!( + test_substitute(None, Some(saved_file), "{label} {saved_file}").as_deref(), + None + ); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{label} {saved_file}").as_deref(), + Some("build :label file.rs") + ); + + // Only need label? only need label. + assert_eq!(test_substitute(None, None, "{label}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{label}").as_deref(), Some("build :label"),); + assert_eq!(test_substitute(None, Some(saved_file), "{label}").as_deref(), None,); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{label}").as_deref(), + Some("build :label"), + ); + + // Only need saved_file + assert_eq!(test_substitute(None, None, "{saved_file}").as_deref(), None); + assert_eq!(test_substitute(Some(label), None, "{saved_file}").as_deref(), None); + assert_eq!( + test_substitute(None, Some(saved_file), "{saved_file}").as_deref(), + Some("build file.rs") + ); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "{saved_file}").as_deref(), + Some("build file.rs") + ); + + // Need neither + assert_eq!(test_substitute(None, None, "xxx").as_deref(), Some("build xxx")); + assert_eq!(test_substitute(Some(label), None, "xxx").as_deref(), Some("build xxx")); + assert_eq!(test_substitute(None, Some(saved_file), "xxx").as_deref(), Some("build xxx")); + assert_eq!( + test_substitute(Some(label), Some(saved_file), "xxx").as_deref(), + Some("build xxx") + ); + + // {label} mid-argument substitution + assert_eq!( + test_substitute(Some(label), None, "--label={label}").as_deref(), + Some("build --label=:label") + ); + + // {saved_file} mid-argument substitution + assert_eq!( + test_substitute(None, Some(saved_file), "--saved={saved_file}").as_deref(), + Some("build --saved=file.rs") + ); + + // $saved_file legacy support (no mid-argument substitution, we never supported that) + assert_eq!( + test_substitute(None, Some(saved_file), "$saved_file").as_deref(), + Some("build file.rs") + ); + + fn test_substitute( + label: Option<&str>, + saved_file: Option<&str>, + args: &str, + ) -> Option { + Substitutions { label, saved_file } + .substitute( + &project_json::Runnable { + program: "build".to_owned(), + args: Vec::from_iter(args.split_whitespace().map(ToOwned::to_owned)), + cwd: Utf8Path::new("/path").to_owned(), + kind: project_json::RunnableKind::Flycheck, + }, + &FxHashMap::default(), + ) + .map(|command| { + command.get_args().map(|x| x.to_string_lossy()).collect_vec().join(" ") + }) + .map(|args| format!("build {}", args)) + } + } +} diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index e3a5ee221973..0a16b7a5614c 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -25,7 +25,9 @@ use load_cargo::{ProjectFolders, load_proc_macro}; use lsp_types::FileSystemWatcher; use paths::Utf8Path; use proc_macro_api::ProcMacroClient; -use project_model::{ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts}; +use project_model::{ + ManifestPath, ProjectWorkspace, ProjectWorkspaceKind, WorkspaceBuildScripts, project_json, +}; use stdx::{format_to, thread::ThreadIntent}; use triomphe::Arc; use vfs::{AbsPath, AbsPathBuf, ChangeKind}; @@ -875,6 +877,7 @@ impl GlobalState { generation.clone(), sender.clone(), config, + crate::flycheck::FlycheckConfigJson::default(), None, self.config.root_path().clone(), None, @@ -894,16 +897,25 @@ impl GlobalState { cargo: Some((cargo, _, _)), .. } => ( + crate::flycheck::FlycheckConfigJson::default(), cargo.workspace_root(), Some(cargo.manifest_path()), Some(cargo.target_directory()), ), ProjectWorkspaceKind::Json(project) => { + let config_json = crate::flycheck::FlycheckConfigJson { + single_template: project + .runnable_template(project_json::RunnableKind::Flycheck) + .cloned(), + }; // Enable flychecks for json projects if a custom flycheck command was supplied // in the workspace configuration. match config { + _ if config_json.any_configured() => { + (config_json, project.path(), None, None) + } FlycheckConfig::CustomCommand { .. } => { - (project.path(), None, None) + (config_json, project.path(), None, None) } _ => return None, } @@ -913,12 +925,13 @@ impl GlobalState { ws.sysroot.root().map(ToOwned::to_owned), )) }) - .map(|(id, (root, manifest_path, target_dir), sysroot_root)| { + .map(|(id, (config_json, root, manifest_path, target_dir), sysroot_root)| { FlycheckHandle::spawn( id, generation.clone(), sender.clone(), config.clone(), + config_json, sysroot_root, root.to_path_buf(), manifest_path.map(|it| it.to_path_buf()), From 7f608da06af36c0c21b6119959e47d9480ccb18a Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 020/319] flycheck: Support {label} in check_overrideCommand as well as $saved_file --- .../crates/rust-analyzer/src/flycheck.rs | 36 ++++++++----------- 1 file changed, 15 insertions(+), 21 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 1b1e3344e25f..cf4ab29b8649 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -872,29 +872,23 @@ impl FlycheckActor { &*self.root } }; - let mut cmd = toolchain::command(command, root, extra_env); + let runnable = project_json::Runnable { + program: command.clone(), + cwd: Utf8Path::to_owned(root.as_ref()), + args: args.clone(), + kind: project_json::RunnableKind::Flycheck, + }; - // If the custom command has a $saved_file placeholder, and - // we're saving a file, replace the placeholder in the arguments. - if let Some(saved_file) = saved_file { - for arg in args { - if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { - cmd.arg(saved_file); - } else { - cmd.arg(arg); - } - } - } else { - for arg in args { - if arg == SAVED_FILE_PLACEHOLDER_DOLLAR { - // The custom command has a $saved_file placeholder, - // but we had an IDE event that wasn't a file save. Do nothing. - return None; - } + let label = match scope { + FlycheckScope::Workspace => None, + // We support substituting both build labels (e.g. buck, bazel) and cargo package ids. + // With cargo package ids, you get `cargo check -p path+file:///path/to/rust-analyzer/crates/hir#0.0.0`. + // That does work! + FlycheckScope::Package { package, .. } => Some(package.as_str()), + }; - cmd.arg(arg); - } - } + let subs = Substitutions { label, saved_file: saved_file.map(|x| x.as_str()) }; + let cmd = subs.substitute(&runnable, extra_env)?; Some(cmd) } From 4e61c6052124c2e71403973cc3adb34c4ee5454d Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 021/319] flycheck: Always flycheck single crate if there is a build label from rust-project.json This requires us to add $saved_file / {saved_file} interpolation back to restart_for_package. Otherwise we break existing users of $saved_file. No grand reason why we can't delete saved_file later, although I would just leave it because sometimes a build system might really know better which target(s) to build, including multiple targets. --- .../rust-analyzer/crates/rust-analyzer/src/flycheck.rs | 3 ++- .../crates/rust-analyzer/src/handlers/notification.rs | 9 ++++++--- 2 files changed, 8 insertions(+), 4 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 cf4ab29b8649..57ad774b1850 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -219,13 +219,14 @@ impl FlycheckHandle { package: PackageSpecifier, target: Option, workspace_deps: Option>, + saved_file: Option, ) { let generation = self.generation.fetch_add(1, Ordering::Relaxed) + 1; self.sender .send(StateChange::Restart { generation, scope: FlycheckScope::Package { package, workspace_deps }, - saved_file: None, + saved_file, target, }) .unwrap(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs index 57adbbfe72a7..d95601043330 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs @@ -328,6 +328,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { } InvocationStrategy::PerWorkspace => { Box::new(move || { + let saved_file = vfs_path.as_path().map(ToOwned::to_owned); let target = TargetSpec::for_file(&world, file_id)?.map(|it| { let tgt_kind = it.target_kind(); let (tgt_name, root, package) = match it { @@ -362,8 +363,10 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { if let Some((target, root, package)) = target { // trigger a package check if we have a non-library target as that can't affect // anything else in the workspace OR if we're not allowed to check the workspace as - // the user opted into package checks then - let package_check_allowed = target.is_some() || !may_flycheck_workspace; + // the user opted into package checks then OR if this is not cargo. + let package_check_allowed = target.is_some() + || !may_flycheck_workspace + || matches!(package, PackageSpecifier::BuildInfo { .. }); if package_check_allowed { package_workspace_idx = world.workspaces.iter().position(|ws| match &ws.kind { @@ -390,6 +393,7 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { package, target, workspace_deps, + saved_file.clone(), ); } } @@ -460,7 +464,6 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool { ws_contains_file && !is_pkg_ws }); - let saved_file = vfs_path.as_path().map(ToOwned::to_owned); let mut workspace_check_triggered = false; // Find and trigger corresponding flychecks 'flychecks: for flycheck in world.flycheck.iter() { From 778de45547f9a584894fad295c86539e7c57aa9d Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 022/319] flycheck: Add display_command to pretty-print flycheck command being run in a notification --- .../crates/rust-analyzer/src/flycheck.rs | 82 +++++++++++++++++++ .../rust-analyzer/crates/toolchain/src/lib.rs | 3 + 2 files changed, 85 insertions(+) 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 57ad774b1850..7f814121e909 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -22,6 +22,7 @@ use serde_derive::Deserialize; pub(crate) use cargo_metadata::diagnostic::{ Applicability, Diagnostic, DiagnosticCode, DiagnosticLevel, DiagnosticSpan, }; +use toolchain::DISPLAY_COMMAND_IGNORE_ENVS; use toolchain::Tool; use triomphe::Arc; @@ -954,6 +955,54 @@ enum JsonMessage { Rustc(Diagnostic), } +/// Not good enough to execute in a shell, but good enough to show the user without all the noisy +/// quotes +/// +/// Pass implicit_cwd if there is one regarded as the obvious by the user, so we can skip showing it. +/// Compactness is the aim of the game, the output typically gets truncated quite a lot. +fn display_command(c: &Command, implicit_cwd: Option<&std::path::Path>) -> String { + let mut o = String::new(); + use std::fmt::Write; + let lossy = std::ffi::OsStr::to_string_lossy; + if let Some(dir) = c.get_current_dir() { + if Some(dir) == implicit_cwd.map(std::path::Path::new) { + // pass + } else if dir.to_string_lossy().contains(" ") { + write!(o, "cd {:?} && ", dir).unwrap(); + } else { + write!(o, "cd {} && ", dir.display()).unwrap(); + } + } + for (env, val) in c.get_envs() { + let (env, val) = (lossy(env), val.map(lossy).unwrap_or(std::borrow::Cow::Borrowed(""))); + if DISPLAY_COMMAND_IGNORE_ENVS.contains(&env.as_ref()) { + continue; + } + if env.contains(" ") { + write!(o, "\"{}={}\" ", env, val).unwrap(); + } else if val.contains(" ") { + write!(o, "{}=\"{}\" ", env, val).unwrap(); + } else { + write!(o, "{}={} ", env, val).unwrap(); + } + } + let prog = lossy(c.get_program()); + if prog.contains(" ") { + write!(o, "{:?}", prog).unwrap(); + } else { + write!(o, "{}", prog).unwrap(); + } + for arg in c.get_args() { + let arg = lossy(arg); + if arg.contains(" ") { + write!(o, " \"{}\"", arg).unwrap(); + } else { + write!(o, " {}", arg).unwrap(); + } + } + o +} + #[cfg(test)] mod tests { use ide_db::FxHashMap; @@ -962,6 +1011,7 @@ mod tests { use project_model::project_json; use crate::flycheck::Substitutions; + use crate::flycheck::display_command; #[test] fn test_substitutions() { @@ -1049,4 +1099,36 @@ mod tests { .map(|args| format!("build {}", args)) } } + + #[test] + fn test_display_command() { + use std::path::Path; + let workdir = Path::new("workdir"); + let mut cmd = toolchain::command("command", workdir, &FxHashMap::default()); + assert_eq!(display_command(cmd.arg("--arg"), Some(workdir)), "command --arg"); + assert_eq!( + display_command(cmd.arg("spaced arg"), Some(workdir)), + "command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.env("ENVIRON", "yeah"), Some(workdir)), + "ENVIRON=yeah command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.env("OTHER", "spaced env"), Some(workdir)), + "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp"), Some(workdir)), + "cd /tmp && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp and/thing"), Some(workdir)), + "cd \"/tmp and/thing\" && ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + assert_eq!( + display_command(cmd.current_dir("/tmp and/thing"), Some(Path::new("/tmp and/thing"))), + "ENVIRON=yeah OTHER=\"spaced env\" command --arg \"spaced arg\"" + ); + } } diff --git a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs index 39319886cfe4..1a1726983870 100644 --- a/src/tools/rust-analyzer/crates/toolchain/src/lib.rs +++ b/src/tools/rust-analyzer/crates/toolchain/src/lib.rs @@ -74,6 +74,9 @@ impl Tool { // Prevent rustup from automatically installing toolchains, see https://github.com/rust-lang/rust-analyzer/issues/20719. pub const NO_RUSTUP_AUTO_INSTALL_ENV: (&str, &str) = ("RUSTUP_AUTO_INSTALL", "0"); +// These get ignored when displaying what command is running in LSP status messages. +pub const DISPLAY_COMMAND_IGNORE_ENVS: &[&str] = &[NO_RUSTUP_AUTO_INSTALL_ENV.0]; + #[allow(clippy::disallowed_types)] /* generic parameter allows for FxHashMap */ pub fn command( cmd: impl AsRef, From 53a371c505a00be220aca18dfba091ee2b2d8f31 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 4 Sep 2024 13:52:59 +1000 Subject: [PATCH 023/319] flycheck: notifications show full command when configured in a rust-project.json runnable For JSON / override users, pretty-print the custom flycheck command with fewer quote characters Better debug logging in flycheck --- .../crates/rust-analyzer/src/flycheck.rs | 52 ++++++++++++++----- .../crates/rust-analyzer/src/global_state.rs | 2 + .../crates/rust-analyzer/src/main_loop.rs | 27 +++++++--- .../crates/rust-analyzer/src/reload.rs | 1 + 4 files changed, 62 insertions(+), 20 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 7f814121e909..6dcae76c9354 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -309,13 +309,18 @@ impl fmt::Debug for FlycheckMessage { #[derive(Debug)] pub(crate) enum Progress { - DidStart, + DidStart { + /// The user sees this in VSCode, etc. May be a shortened version of the command we actually + /// executed, otherwise it is way too long. + user_facing_command: String, + }, DidCheckCrate(String), DidFinish(io::Result<()>), DidCancel, DidFailToRestart(String), } +#[derive(Debug, Clone)] enum FlycheckScope { Workspace, Package { @@ -346,6 +351,16 @@ impl PackageSpecifier { } } +#[derive(Debug)] +enum FlycheckCommandOrigin { + /// Regular cargo invocation + Cargo, + /// Configured via check_overrideCommand + CheckOverrideCommand, + /// From a runnable with [project_json::RunnableKind::Flycheck] + ProjectJsonRunnable, +} + enum StateChange { Restart { generation: DiagnosticsGeneration, @@ -529,16 +544,28 @@ impl FlycheckActor { } let command = self.check_command(&scope, saved_file.as_deref(), target); - self.scope = scope; + self.scope = scope.clone(); self.generation = generation; - let Some(command) = command else { + let Some((command, origin)) = command else { + tracing::debug!(?scope, "failed to build flycheck command"); continue; }; - let formatted_command = format!("{command:?}"); + let debug_command = format!("{command:?}"); + let user_facing_command = match origin { + // Don't show all the --format=json-with-blah-blah args, just the simple + // version + FlycheckCommandOrigin::Cargo => self.config.to_string(), + // show them the full command but pretty printed. advanced user + FlycheckCommandOrigin::ProjectJsonRunnable + | FlycheckCommandOrigin::CheckOverrideCommand => display_command( + &command, + Some(std::path::Path::new(self.root.as_path())), + ), + }; - tracing::debug!(?command, "will restart flycheck"); + tracing::debug!(?origin, ?command, "will restart flycheck"); let (sender, receiver) = unbounded(); match CommandHandle::spawn( command, @@ -575,14 +602,14 @@ impl FlycheckActor { }, ) { Ok(command_handle) => { - tracing::debug!(command = formatted_command, "did restart flycheck"); + tracing::debug!(?origin, command = %debug_command, "did restart flycheck"); self.command_handle = Some(command_handle); self.command_receiver = Some(receiver); - self.report_progress(Progress::DidStart); + self.report_progress(Progress::DidStart { user_facing_command }); } Err(error) => { self.report_progress(Progress::DidFailToRestart(format!( - "Failed to run the following command: {formatted_command} error={error}" + "Failed to run the following command: {debug_command} origin={origin:?} error={error}" ))); } } @@ -789,7 +816,7 @@ impl FlycheckActor { scope: &FlycheckScope, saved_file: Option<&AbsPath>, target: Option, - ) -> Option { + ) -> Option<(Command, FlycheckCommandOrigin)> { match &self.config { FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { // Only use the rust-project.json's flycheck config when no check_overrideCommand @@ -803,7 +830,8 @@ impl FlycheckActor { // Completely handle according to rust-project.json. // We don't consider this to be "using cargo" so we will not apply any of the // CargoOptions to the command. - return self.explicit_check_command(scope, saved_file); + let cmd = self.explicit_check_command(scope, saved_file)?; + return Some((cmd, FlycheckCommandOrigin::ProjectJsonRunnable)); } let mut cmd = @@ -864,7 +892,7 @@ impl FlycheckActor { self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), ); cmd.args(&options.extra_args); - Some(cmd) + Some((cmd, FlycheckCommandOrigin::Cargo)) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { let root = match invocation_strategy { @@ -892,7 +920,7 @@ impl FlycheckActor { let subs = Substitutions { label, saved_file: saved_file.map(|x| x.as_str()) }; let cmd = subs.substitute(&runnable, extra_env)?; - Some(cmd) + Some((cmd, FlycheckCommandOrigin::CheckOverrideCommand)) } } } 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 0cfd0a141bae..39b4aaa64738 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 @@ -112,6 +112,7 @@ pub(crate) struct GlobalState { pub(crate) flycheck_sender: Sender, pub(crate) flycheck_receiver: Receiver, pub(crate) last_flycheck_error: Option, + pub(crate) flycheck_formatted_commands: Vec, // Test explorer pub(crate) test_run_session: Option>, @@ -288,6 +289,7 @@ impl GlobalState { flycheck_sender, flycheck_receiver, last_flycheck_error: None, + flycheck_formatted_commands: vec![], test_run_session: None, test_run_sender, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index dd0813c14454..62a3b3a17bdf 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -1179,8 +1179,24 @@ impl GlobalState { kind: ClearDiagnosticsKind::OlderThan(generation, ClearScope::Package(package_id)), } => self.diagnostics.clear_check_older_than_for_package(id, package_id, generation), FlycheckMessage::Progress { id, progress } => { + let format_with_id = |user_facing_command: String| { + if self.flycheck.len() == 1 { + user_facing_command + } else { + format!("{user_facing_command} (#{})", id + 1) + } + }; + + self.flycheck_formatted_commands + .resize_with(self.flycheck.len().max(id + 1), || { + format_with_id(self.config.flycheck(None).to_string()) + }); + let (state, message) = match progress { - flycheck::Progress::DidStart => (Progress::Begin, None), + flycheck::Progress::DidStart { user_facing_command } => { + self.flycheck_formatted_commands[id] = format_with_id(user_facing_command); + (Progress::Begin, None) + } flycheck::Progress::DidCheckCrate(target) => (Progress::Report, Some(target)), flycheck::Progress::DidCancel => { self.last_flycheck_error = None; @@ -1200,13 +1216,8 @@ impl GlobalState { } }; - // When we're running multiple flychecks, we have to include a disambiguator in - // the title, or the editor complains. Note that this is a user-facing string. - let title = if self.flycheck.len() == 1 { - format!("{}", self.config.flycheck(None)) - } else { - format!("{} (#{})", self.config.flycheck(None), id + 1) - }; + // Clone because we &mut self for report_progress + let title = self.flycheck_formatted_commands[id].clone(); self.report_progress( &title, state, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs index 0a16b7a5614c..ccafbd7b30b9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs @@ -942,6 +942,7 @@ impl GlobalState { } } .into(); + self.flycheck_formatted_commands = vec![]; } } From 3fdb78cba695824cba5be894a032779c9b4a4ac3 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 11:34:58 +1100 Subject: [PATCH 024/319] flycheck: Rename FlycheckConfig::CargoCommand to Automatic Because (1) it is what we use when there is no relevant config (2) we automatically use either rust-project.json's flycheck, or cargo This also puts check_command config into CargoOptions. It's a cargo option, after all. --- .../crates/rust-analyzer/src/config.rs | 20 ++++++----- .../crates/rust-analyzer/src/flycheck.rs | 36 ++++++++++++------- .../crates/rust-analyzer/src/test_runner.rs | 2 +- 3 files changed, 35 insertions(+), 23 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index e39569e108de..c2f7ada8c8ca 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -2431,6 +2431,8 @@ impl Config { pub(crate) fn cargo_test_options(&self, source_root: Option) -> CargoOptions { CargoOptions { + // Might be nice to allow users to specify test_command = "nextest" + subcommand: "test".into(), target_tuples: self.cargo_target(source_root).clone().into_iter().collect(), all_targets: false, no_default_features: *self.cargo_noDefaultFeatures(source_root), @@ -2464,9 +2466,9 @@ impl Config { }, } } - Some(_) | None => FlycheckConfig::CargoCommand { - command: self.check_command(source_root).clone(), - options: CargoOptions { + Some(_) | None => FlycheckConfig::Automatic { + cargo_options: CargoOptions { + subcommand: self.check_command(source_root).clone(), target_tuples: self .check_targets(source_root) .clone() @@ -4171,8 +4173,8 @@ mod tests { assert_eq!(config.cargo_targetDir(None), &None); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config: TargetDirectoryConfig::None, .. }, .. } )); @@ -4195,8 +4197,8 @@ mod tests { Utf8PathBuf::from(std::env::var("CARGO_TARGET_DIR").unwrap_or("target".to_owned())); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config, .. }, .. } if target_dir_config.target_dir(Some(&ws_target_dir)).map(Cow::into_owned) == Some(ws_target_dir.join("rust-analyzer")) @@ -4221,8 +4223,8 @@ mod tests { ); assert!(matches!( config.flycheck(None), - FlycheckConfig::CargoCommand { - options: CargoOptions { target_dir_config, .. }, + FlycheckConfig::Automatic { + cargo_options: CargoOptions { target_dir_config, .. }, .. } if target_dir_config.target_dir(None).map(Cow::into_owned) == Some(Utf8PathBuf::from("other_folder")) 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 6dcae76c9354..512c231990cb 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/flycheck.rs @@ -38,8 +38,11 @@ pub(crate) enum InvocationStrategy { PerWorkspace, } +/// Data needed to construct a `cargo` command invocation, e.g. for flycheck or running a test. #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct CargoOptions { + /// The cargo subcommand to run, e.g. "check" or "clippy" + pub(crate) subcommand: String, pub(crate) target_tuples: Vec, pub(crate) all_targets: bool, pub(crate) set_test: bool, @@ -111,11 +114,16 @@ impl FlycheckConfigJson { /// #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) enum FlycheckConfig { - CargoCommand { - command: String, - options: CargoOptions, + /// Automatically use rust-project.json's flycheck runnable or just use cargo (the common case) + /// + /// We can't have a variant for ProjectJson because that is configured on the fly during + /// discoverConfig. We only know what we can read at config time. + Automatic { + /// If we do use cargo, how to build the check command + cargo_options: CargoOptions, ansi_color_output: bool, }, + /// check_overrideCommand. This overrides both cargo and rust-project.json's flycheck runnable. CustomCommand { command: String, args: Vec, @@ -127,7 +135,7 @@ pub(crate) enum FlycheckConfig { impl FlycheckConfig { pub(crate) fn invocation_strategy(&self) -> InvocationStrategy { match self { - FlycheckConfig::CargoCommand { .. } => InvocationStrategy::PerWorkspace, + FlycheckConfig::Automatic { .. } => InvocationStrategy::PerWorkspace, FlycheckConfig::CustomCommand { invocation_strategy, .. } => { invocation_strategy.clone() } @@ -138,7 +146,9 @@ impl FlycheckConfig { impl fmt::Display for FlycheckConfig { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FlycheckConfig::CargoCommand { command, .. } => write!(f, "cargo {command}"), + FlycheckConfig::Automatic { cargo_options, .. } => { + write!(f, "cargo {}", cargo_options.subcommand) + } FlycheckConfig::CustomCommand { command, args, .. } => { // Don't show `my_custom_check --foo $saved_file` literally to the user, as it // looks like we've forgotten to substitute $saved_file. @@ -572,11 +582,11 @@ impl FlycheckActor { CargoCheckParser, sender, match &self.config { - FlycheckConfig::CargoCommand { options, .. } => { + FlycheckConfig::Automatic { cargo_options, .. } => { let ws_target_dir = self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path); let target_dir = - options.target_dir_config.target_dir(ws_target_dir); + cargo_options.target_dir_config.target_dir(ws_target_dir); // If `"rust-analyzer.cargo.targetDir": null`, we should use // workspace's target dir instead of hard-coded fallback. @@ -818,7 +828,7 @@ impl FlycheckActor { target: Option, ) -> Option<(Command, FlycheckCommandOrigin)> { match &self.config { - FlycheckConfig::CargoCommand { command, options, ansi_color_output } => { + FlycheckConfig::Automatic { cargo_options, ansi_color_output } => { // Only use the rust-project.json's flycheck config when no check_overrideCommand // is configured. In the FlycheckConcig::CustomCommand branch we will still do // label substitution, but on the overrideCommand instead. @@ -835,15 +845,15 @@ impl FlycheckActor { } let mut cmd = - toolchain::command(Tool::Cargo.path(), &*self.root, &options.extra_env); + toolchain::command(Tool::Cargo.path(), &*self.root, &cargo_options.extra_env); if let Some(sysroot_root) = &self.sysroot_root - && !options.extra_env.contains_key("RUSTUP_TOOLCHAIN") + && !cargo_options.extra_env.contains_key("RUSTUP_TOOLCHAIN") && std::env::var_os("RUSTUP_TOOLCHAIN").is_none() { cmd.env("RUSTUP_TOOLCHAIN", AsRef::::as_ref(sysroot_root)); } cmd.env("CARGO_LOG", "cargo::core::compiler::fingerprint=info"); - cmd.arg(command); + cmd.arg(&cargo_options.subcommand); match scope { FlycheckScope::Workspace => cmd.arg("--workspace"), @@ -887,11 +897,11 @@ impl FlycheckActor { cmd.arg("--keep-going"); - options.apply_on_command( + cargo_options.apply_on_command( &mut cmd, self.ws_target_dir.as_ref().map(Utf8PathBuf::as_path), ); - cmd.args(&options.extra_args); + cmd.args(&cargo_options.extra_args); Some((cmd, FlycheckCommandOrigin::Cargo)) } FlycheckConfig::CustomCommand { command, args, extra_env, invocation_strategy } => { diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs index 7111a15d0246..f0020f9088e3 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs @@ -105,7 +105,7 @@ impl CargoTestHandle { let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env); cmd.env("RUSTC_BOOTSTRAP", "1"); cmd.arg("--color=always"); - cmd.arg("test"); + cmd.arg(&options.subcommand); // test, usually cmd.arg("--package"); cmd.arg(&test_target.package); From 2d581773fed793f9d62b190e56374065d37291d7 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 12:03:05 +1100 Subject: [PATCH 025/319] Fix RunnableKind::Run label interpolation It was pretty useless without this. Previously: Parsing target pattern `{label}` Caused by: Invalid target name `{label}`. (...) Build ID: 6dab5942-d81c-4430-83b0-5ba523999050 Network: Up: 0B Down: 0B Command: run. Time elapsed: 0.3s BUILD FAILED * The terminal process "buck2 'run', '{label}'" terminated with exit code: 3. --- .../crates/rust-analyzer/src/target_spec.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs index 8452b6493e87..b8d9acc02a32 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/target_spec.rs @@ -77,7 +77,16 @@ impl ProjectJsonTargetSpec { RunnableKind::Bin => { for runnable in &self.shell_runnables { if matches!(runnable.kind, project_model::project_json::RunnableKind::Run) { - return Some(runnable.clone()); + let mut runnable = runnable.clone(); + + let replaced_args: Vec<_> = runnable + .args + .iter() + .map(|arg| arg.replace("{label}", &self.label)) + .collect(); + runnable.args = replaced_args; + + return Some(runnable); } } From 71e2ded9fb196bc9b4a1736759f9465f5d4d9619 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 13:32:03 +1100 Subject: [PATCH 026/319] doc: Update docs for runnables to include run/flycheck --- .../crates/project-model/src/project_json.rs | 3 +++ .../docs/book/src/non_cargo_based_projects.md | 19 ++++++++++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs index 536f170e1192..6938010cbd70 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/project_json.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/project_json.rs @@ -357,9 +357,12 @@ pub enum RunnableKind { Check, /// Can run a binary. + /// May include {label} which will get the label from the `build` section of a crate. Run, /// Run a single test. + /// May include {label} which will get the label from the `build` section of a crate. + /// May include {test_id} which will get the test clicked on by the user. TestOne, /// Template for checking a target, emitting rustc JSON diagnostics. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index e7df4a5d7668..d8be9a82d0c9 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -204,16 +204,25 @@ interface Runnable { args: string[]; /// The current working directory of the runnable. cwd: string; - /// Used to decide what code lens to offer. + /// Maps a runnable to a piece of rust-analyzer functionality. /// - /// `testOne`: This runnable will be used when the user clicks the 'Run Test' - /// CodeLens above a test. + /// - `testOne`: This runnable will be used when the user clicks the 'Run Test' + /// CodeLens above a test. + /// - `run`: This runnable will be used when the user clicks the 'Run' CodeLens + /// above a main function or triggers a run command. + /// - `flycheck`: This is run to provide check-on-save diagnostics when the user + /// saves a file. It must emit rustc JSON diagnostics that rust-analyzer can + /// parse. If this runnable is not specified, we may try to use `cargo check -p`. + /// This is only run for a single crate that the user saved a file in. The + /// {label} syntax is replaced with `BuildInfo::label`. + /// Alternatively, you may use `{saved_file}` and figure out which crate + /// to produce diagnostics for based on that. /// /// The args for testOne can contain two template strings: /// `{label}` and `{test_id}`. `{label}` will be replaced - /// with the `Build::label` and `{test_id}` will be replaced + /// with the `BuildInfo::label` and `{test_id}` will be replaced /// with the test name. - kind: 'testOne' | string; + kind: 'testOne' | 'run' | 'flycheck' | string; } ``` From 422597f76395060d968fcb5c9e7de311bf1dc9a4 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 14:44:40 +1100 Subject: [PATCH 027/319] doc: make example for workspace.discoverConfig actually work rust-project requires {arg} these days. No good giving people bad information even if it's not crucial to documenting this. --- .../rust-analyzer/crates/rust-analyzer/src/config.rs | 9 +++++---- .../docs/book/src/configuration_generated.md | 5 +++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index c2f7ada8c8ca..2b7ade6c26ef 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -480,8 +480,8 @@ config_data! { /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. /// - /// [`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. - /// `progress_label` is used for the title in progress indicators, whereas `files_to_watch` + /// [`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`. + /// `progressLabel` is used for the title in progress indicators, whereas `filesToWatch` /// is used to determine which build system-specific files should be watched in order to /// reload rust-analyzer. /// @@ -490,9 +490,10 @@ config_data! { /// "rust-analyzer.workspace.discoverConfig": { /// "command": [ /// "rust-project", - /// "develop-json" + /// "develop-json", + /// "{arg}" /// ], - /// "progressLabel": "rust-analyzer", + /// "progressLabel": "buck2/rust-project", /// "filesToWatch": [ /// "BUCK" /// ] diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index 58b636334527..a0738ca0e179 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -1623,9 +1623,10 @@ Below is an example of a valid configuration: "rust-analyzer.workspace.discoverConfig": { "command": [ "rust-project", - "develop-json" + "develop-json", + "{arg}" ], - "progressLabel": "rust-analyzer", + "progressLabel": "buck2/rust-project", "filesToWatch": [ "BUCK" ] From f06a6b9fdcb9db492a364b48a08f70afa98da182 Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Wed, 3 Dec 2025 17:11:16 +1100 Subject: [PATCH 028/319] doc: overhaul non-cargo build system docs --- .../crates/rust-analyzer/src/config.rs | 18 ++- .../docs/book/src/configuration_generated.md | 22 ++-- .../docs/book/src/non_cargo_based_projects.md | 105 +++++++++++++++--- .../rust-analyzer/editors/code/package.json | 4 +- 4 files changed, 120 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs index 2b7ade6c26ef..28ac94e4deb6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs @@ -500,7 +500,7 @@ config_data! { /// } /// ``` /// - /// ## On `DiscoverWorkspaceConfig::command` + /// ## Workspace Discovery Protocol /// /// **Warning**: This format is provisional and subject to change. /// @@ -871,10 +871,18 @@ config_data! { /// (i.e., the folder containing the `Cargo.toml`). This can be overwritten /// by changing `#rust-analyzer.check.invocationStrategy#`. /// - /// If `$saved_file` is part of the command, rust-analyzer will pass - /// the absolute path of the saved file to the provided command. This is - /// intended to be used with non-Cargo build systems. - /// Note that `$saved_file` is experimental and may be removed in the future. + /// It supports two interpolation syntaxes, both mainly intended to be used with + /// [non-Cargo build systems](./non_cargo_based_projects.md): + /// + /// - If `{saved_file}` is part of the command, rust-analyzer will pass + /// the absolute path of the saved file to the provided command. + /// (A previous version, `$saved_file`, also works.) + /// - If `{label}` is part of the command, rust-analyzer will pass the + /// Cargo package ID, which can be used with `cargo check -p`, or a build label from + /// `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like + /// [`"rust-analyzer.check.workspace": false`](#check.workspace). + /// + /// /// /// An example command would be: /// diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md index a0738ca0e179..c4124aaae075 100644 --- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md +++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md @@ -323,10 +323,18 @@ each of them, with the working directory being the workspace root (i.e., the folder containing the `Cargo.toml`). This can be overwritten by changing `#rust-analyzer.check.invocationStrategy#`. -If `$saved_file` is part of the command, rust-analyzer will pass -the absolute path of the saved file to the provided command. This is -intended to be used with non-Cargo build systems. -Note that `$saved_file` is experimental and may be removed in the future. +It supports two interpolation syntaxes, both mainly intended to be used with +[non-Cargo build systems](./non_cargo_based_projects.md): + +- If `{saved_file}` is part of the command, rust-analyzer will pass + the absolute path of the saved file to the provided command. + (A previous version, `$saved_file`, also works.) +- If `{label}` is part of the command, rust-analyzer will pass the + Cargo package ID, which can be used with `cargo check -p`, or a build label from + `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like + [`"rust-analyzer.check.workspace": false`](#check.workspace). + + An example command would be: @@ -1613,8 +1621,8 @@ Default: `null` Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`]. -[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`. -`progress_label` is used for the title in progress indicators, whereas `files_to_watch` +[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`. +`progressLabel` is used for the title in progress indicators, whereas `filesToWatch` is used to determine which build system-specific files should be watched in order to reload rust-analyzer. @@ -1633,7 +1641,7 @@ Below is an example of a valid configuration: } ``` -## On `DiscoverWorkspaceConfig::command` +## Workspace Discovery Protocol **Warning**: This format is provisional and subject to change. diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md index d8be9a82d0c9..a48b025c7b3a 100644 --- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md +++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md @@ -229,7 +229,15 @@ interface Runnable { This format is provisional and subject to change. Specifically, the `roots` setup will be different eventually. -There are three ways to feed `rust-project.json` to rust-analyzer: +### Providing a JSON project to rust-analyzer + +There are four ways to feed `rust-project.json` to rust-analyzer: + +- Use + [`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig) + to specify a workspace discovery command to generate project descriptions + on-the-fly. Please note that the command output is message-oriented and must + follow [the discovery protocol](./configuration.md#workspace-discovery-protocol). - Place `rust-project.json` file at the root of the project, and rust-analyzer will discover it. @@ -249,19 +257,86 @@ location or (for inline JSON) relative to `rootUri`. You can set the `RA_LOG` environment variable to `rust_analyzer=info` to inspect how rust-analyzer handles config and project loading. -Note that calls to `cargo check` are disabled when using -`rust-project.json` by default, so compilation errors and warnings will -no longer be sent to your LSP client. To enable these compilation errors -you will need to specify explicitly what command rust-analyzer should -run to perform the checks using the -`rust-analyzer.check.overrideCommand` configuration. As an example, the -following configuration explicitly sets `cargo check` as the `check` -command. +### Flycheck support - { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } +Rust-analyzer has functionality to run an actual build of a crate when the user saves a file, to +fill in diagnostics it does not implement natively. This is known as "flycheck". -`check.overrideCommand` requires the command specified to output json -error messages for rust-analyzer to consume. The `--message-format=json` -flag does this for `cargo check` so whichever command you use must also -output errors in this format. See the [Configuration](#_configuration) -section for more information. +**Flycheck is disabled when using `rust-project.json` unless explicitly configured**, so compilation +errors and warnings will no longer be sent to your LSP client by default. To enable these +compilation errors you will need to specify explicitly what command rust-analyzer should run to +perform the checks. There are two ways to do this: + +- `rust-project.json` may contain a `runnables` field. The `flycheck` runnable may be used to + configure a check command. See above for documentation. + +- Using the [`rust-analyzer.check.overrideCommand`](./configuration.md#check.overrideCommand) + configuration. This will also override anything in `rust-project.json`. As an example, the + following configuration explicitly sets `cargo check` as the `check` command. + + ```json + { "rust-analyzer.check.overrideCommand": ["cargo", "check", "--message-format=json"] } + ``` + + Note also that this works with cargo projects. + +Either option requires the command specified to output JSON error messages for rust-analyzer to +consume. The `--message-format=json` flag does this for `cargo check` so whichever command you use +must also output errors in this format. + +Either option also supports two syntaxes within each argument: + +- `{label}` will be replaced with the `BuildInfo::label` of the crate + containing a saved file, if `BuildInfo` is provided. In the case of `check.overrideCommand` being + used in a Cargo project, this will be the cargo package ID, which can be used with `cargo check -p`. +- `{saved_file}` will be replaced with an absolute path to the saved file. This can be queried against a + build system to find targets that include the file. + +For example: + +```json +{ "rust-analyzer.check.overrideCommand": ["custom_crate_checker", "{label}"] } +``` + +If you do use `{label}` or `{saved_file}`, the command will not be run unless the relevant value can +be substituted. + + +#### Flycheck considerations + +##### Diagnostic output on error + +A flycheck command using a complex build orchestrator like `"bazel", "build", "{label}"`, even with +a tweak to return JSON messages, is often insufficient. Such a command will typically succeed if +there are warnings, but if there are errors, it might "fail to compile" the diagnostics and not +produce any output. You must build a package in such a way that the build succeeds even if `rustc` +exits with an error, and prints the JSON build messages in every case. + +##### Diagnostics for upstream crates + +`cargo check -p` re-prints any errors and warnings in crates higher up in the dependency graph +than the one requested. We do clear all diagnostics when flychecking, so if you manage to +replicate this behaviour, diagnostics for crates other than the one being checked will show up in +the editor. If you do not, then users may be confused that diagnostics are "stuck" or disappear +entirely when there is a build error in an upstream crate. + +##### Compiler options + +`cargo check` invokes rustc differently from `cargo build`. It turns off codegen (with `rustc +--emit=metadata`), which results in lower latency to get to diagnostics. If your build system can +configure this, it is recommended. + +If your build tool can configure rustc for incremental compiles, this is also recommended. + +##### Locking and pre-emption + +In any good build system, including Cargo, build commands sometimes block each other. Running a +flycheck will (by default) frequently block you from running other build commands. Generally this is +undesirable. Users will have to (unintuitively) press save again in the editor to cancel a +flycheck, so that some other command may proceed. + +If your build system has the ability to isolate any rust-analyzer-driven flychecks and prevent lock +contention, for example a separate build output directory and/or daemon instance, this is +recommended. Alternatively, consider using a feature if available that can set the priority of +various build invocations and automatically cancel lower-priority ones when needed. Flychecks should +be set to a lower priority than general direct build invocations. diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json index 2157cbd48653..0d91378706a4 100644 --- a/src/tools/rust-analyzer/editors/code/package.json +++ b/src/tools/rust-analyzer/editors/code/package.json @@ -1213,7 +1213,7 @@ "title": "Check", "properties": { "rust-analyzer.check.overrideCommand": { - "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", + "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIt supports two interpolation syntaxes, both mainly intended to be used with\n[non-Cargo build systems](./non_cargo_based_projects.md):\n\n- If `{saved_file}` is part of the command, rust-analyzer will pass\n the absolute path of the saved file to the provided command.\n (A previous version, `$saved_file`, also works.)\n- If `{label}` is part of the command, rust-analyzer will pass the\n Cargo package ID, which can be used with `cargo check -p`, or a build label from\n `rust-project.json`. If `{label}` is included, rust-analyzer behaves much like\n [`\"rust-analyzer.check.workspace\": false`](#check.workspace).\n\n\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.", "default": null, "type": [ "null", @@ -3135,7 +3135,7 @@ "title": "Workspace", "properties": { "rust-analyzer.workspace.discoverConfig": { - "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progress_label` and `files_to_watch`.\n`progress_label` is used for the title in progress indicators, whereas `files_to_watch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\"\n ],\n \"progressLabel\": \"rust-analyzer\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## On `DiscoverWorkspaceConfig::command`\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: .", + "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.\n`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n \"command\": [\n \"rust-project\",\n \"develop-json\",\n \"{arg}\"\n ],\n \"progressLabel\": \"buck2/rust-project\",\n \"filesToWatch\": [\n \"BUCK\"\n ]\n}\n```\n\n## Workspace Discovery Protocol\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n Error { error: String, source: Option },\n Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n // the internally-tagged representation of the enum.\n \"kind\": \"finished\",\n // the file used by a non-Cargo build system to define\n // a package or target.\n \"buildfile\": \"rust-analyzer/BUILD\",\n // the contents of a rust-project.json, elided for brevity\n \"project\": {\n \"sysroot\": \"foo\",\n \"crates\": []\n }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n Path(AbsPathBuf),\n Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: .", "default": null, "anyOf": [ { From b02e9756f2d0b0367cdedcba16016fc5e867999c Mon Sep 17 00:00:00 2001 From: Cormac Relf Date: Thu, 8 Jan 2026 09:53:00 +1100 Subject: [PATCH 029/319] Fix hir-ty clippy issue I am not familiar with this code at allso just doing what I can to unblock. --- .../crates/hir-ty/src/next_solver/infer/traits.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs index 14df42dc2aeb..dde623483642 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/traits.rs @@ -55,6 +55,13 @@ impl ObligationCause { } } +impl Default for ObligationCause { + #[inline] + fn default() -> Self { + Self::new() + } +} + /// An `Obligation` represents some trait reference (e.g., `i32: Eq`) for /// which the "impl_source" must be found. The process of finding an "impl_source" is /// called "resolving" the `Obligation`. This process consists of From 6a9de224c463f54dc66dec6e3dbe3e244d2d7014 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Thu, 8 Jan 2026 04:20:55 +0000 Subject: [PATCH 030/319] Prepare for merging from rust-lang/rust This updates the rust-version file to 548e586795f6b6fe089d8329aa5edbf0f5202646. --- 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 5ffe95a0b54f..4b08b0884ca8 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -e7d44143a12a526488e4f0c0d7ea8e62a4fe9354 +548e586795f6b6fe089d8329aa5edbf0f5202646 From 5d8a7daf2ab5d13330030880af021cf1cf418a7e Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 8 Jan 2026 09:25:28 +0200 Subject: [PATCH 031/319] Fixes for builtin derive expansions - Do not store the `MacroCallId` of the "real" expansion anywhere, so that the IDE layer could not expand it by mistake - Fix a stupid bug where we used the directive of the `derive` itself instead of of the macro, leading us to re-expand it again and again. --- .../crates/hir-def/src/builtin_derive.rs | 22 ++++- .../crates/hir-def/src/dyn_map.rs | 10 ++- .../crates/hir-def/src/item_scope.rs | 19 ++-- .../crates/hir-def/src/lang_item.rs | 54 ++++++++++-- .../crates/hir-def/src/nameres.rs | 10 ++- .../crates/hir-def/src/nameres/collector.rs | 88 ++++++++++++------- .../rust-analyzer/crates/hir/src/semantics.rs | 31 +++++-- .../crates/hir/src/semantics/source_to_def.rs | 17 ++-- .../crates/ide/src/expand_macro.rs | 60 +++++-------- .../crates/intern/src/symbol/symbols.rs | 1 + 10 files changed, 211 insertions(+), 101 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs b/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs index 32385516ab58..946f08ec3682 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/builtin_derive.rs @@ -8,7 +8,8 @@ use intern::{Symbol, sym}; use tt::TextRange; use crate::{ - AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, db::DefDatabase, + AdtId, BuiltinDeriveImplId, BuiltinDeriveImplLoc, FunctionId, HasModule, MacroId, + db::DefDatabase, lang_item::LangItems, }; macro_rules! declare_enum { @@ -86,6 +87,25 @@ declare_enum!( DispatchFromDyn => [], ); +impl BuiltinDeriveImplTrait { + pub fn derive_macro(self, lang_items: &LangItems) -> Option { + match self { + BuiltinDeriveImplTrait::Copy => lang_items.CopyDerive, + BuiltinDeriveImplTrait::Clone => lang_items.CloneDerive, + BuiltinDeriveImplTrait::Default => lang_items.DefaultDerive, + BuiltinDeriveImplTrait::Debug => lang_items.DebugDerive, + BuiltinDeriveImplTrait::Hash => lang_items.HashDerive, + BuiltinDeriveImplTrait::Ord => lang_items.OrdDerive, + BuiltinDeriveImplTrait::PartialOrd => lang_items.PartialOrdDerive, + BuiltinDeriveImplTrait::Eq => lang_items.EqDerive, + BuiltinDeriveImplTrait::PartialEq => lang_items.PartialEqDerive, + BuiltinDeriveImplTrait::CoerceUnsized | BuiltinDeriveImplTrait::DispatchFromDyn => { + lang_items.CoercePointeeDerive + } + } + } +} + impl BuiltinDeriveImplMethod { pub fn trait_method( self, diff --git a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs index 7d3a94b03833..4308d0ef1c29 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/dyn_map.rs @@ -27,14 +27,15 @@ pub mod keys { use std::marker::PhantomData; + use either::Either; use hir_expand::{MacroCallId, attrs::AttrId}; use rustc_hash::FxHashMap; use syntax::{AstNode, AstPtr, ast}; use crate::{ - BlockId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, - ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, StaticId, StructId, TraitId, - TypeAliasId, TypeOrConstParamId, UnionId, UseId, + BlockId, BuiltinDeriveImplId, ConstId, EnumId, EnumVariantId, ExternBlockId, ExternCrateId, + FieldId, FunctionId, ImplId, LifetimeParamId, Macro2Id, MacroRulesId, ProcMacroId, + StaticId, StructId, TraitId, TypeAliasId, TypeOrConstParamId, UnionId, UseId, dyn_map::{DynMap, Policy}, }; @@ -71,7 +72,8 @@ pub mod keys { ( AttrId, /* derive() */ MacroCallId, - /* actual derive macros */ Box<[Option]>, + /* actual derive macros */ + Box<[Option>]>, ), > = Key::new(); 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 a3278dd76c86..9e1efb977786 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 @@ -4,6 +4,7 @@ use std::{fmt, sync::LazyLock}; use base_db::Crate; +use either::Either; use hir_expand::{AstId, MacroCallId, attrs::AttrId, name::Name}; use indexmap::map::Entry; use itertools::Itertools; @@ -199,7 +200,7 @@ struct DeriveMacroInvocation { attr_id: AttrId, /// The `#[derive]` call attr_call_id: MacroCallId, - derive_call_ids: SmallVec<[Option; 4]>, + derive_call_ids: SmallVec<[Option>; 4]>, } pub(crate) static BUILTIN_SCOPE: LazyLock> = LazyLock::new(|| { @@ -345,7 +346,9 @@ impl ItemScope { pub fn all_macro_calls(&self) -> impl Iterator + '_ { self.macro_invocations.values().copied().chain(self.attr_macros.values().copied()).chain( self.derive_macros.values().flat_map(|it| { - it.iter().flat_map(|it| it.derive_call_ids.iter().copied().flatten()) + it.iter().flat_map(|it| { + it.derive_call_ids.iter().copied().flatten().flat_map(|it| it.left()) + }) }), ) } @@ -379,6 +382,10 @@ impl ItemScope { self.types.get(name).map(|item| (item.def, item.vis)) } + pub(crate) fn makro(&self, name: &Name) -> Option { + self.macros.get(name).map(|item| item.def) + } + /// XXX: this is O(N) rather than O(1), try to not introduce new usages. pub(crate) fn name_of(&self, item: ItemInNs) -> Option<(&Name, Visibility, /*declared*/ bool)> { match item { @@ -519,7 +526,7 @@ impl ItemScope { pub(crate) fn set_derive_macro_invoc( &mut self, adt: AstId, - call: MacroCallId, + call: Either, id: AttrId, idx: usize, ) { @@ -539,7 +546,7 @@ impl ItemScope { adt: AstId, attr_id: AttrId, attr_call_id: MacroCallId, - mut derive_call_ids: SmallVec<[Option; 4]>, + mut derive_call_ids: SmallVec<[Option>; 4]>, ) { derive_call_ids.shrink_to_fit(); self.derive_macros.entry(adt).or_default().push(DeriveMacroInvocation { @@ -554,7 +561,9 @@ impl ItemScope { ) -> impl Iterator< Item = ( AstId, - impl Iterator])>, + impl Iterator< + Item = (AttrId, MacroCallId, &[Option>]), + >, ), > + '_ { self.derive_macros.iter().map(|(k, v)| { diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs index eba4d87ec9f8..092ff6e48671 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs @@ -7,8 +7,8 @@ use intern::{Symbol, sym}; use stdx::impl_from; use crate::{ - AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, ModuleDefId, - StaticId, StructId, TraitId, TypeAliasId, UnionId, + AdtId, AssocItemId, AttrDefId, Crate, EnumId, EnumVariantId, FunctionId, ImplId, MacroId, + ModuleDefId, StaticId, StructId, TraitId, TypeAliasId, UnionId, attrs::AttrFlags, db::DefDatabase, nameres::{DefMap, assoc::TraitItems, crate_def_map, crate_local_def_map}, @@ -99,7 +99,7 @@ pub fn crate_lang_items(db: &dyn DefDatabase, krate: Crate) -> Option Option { + let mut current = &core_def_map[core_def_map.root]; + for module in modules { + let Some((ModuleDefId::ModuleId(cur), _)) = + current.scope.type_(&Name::new_symbol_root(module.clone())) + else { + return None; + }; + if cur.krate(db) != core_def_map.krate() || cur.block(db) != core_def_map.block_id() { + return None; + } + current = &core_def_map[cur]; + } + current.scope.makro(&Name::new_symbol_root(name)) +} + #[salsa::tracked(returns(as_deref))] pub(crate) fn crate_notable_traits(db: &dyn DefDatabase, krate: Crate) -> Option> { let mut traits = Vec::new(); @@ -195,7 +216,11 @@ macro_rules! language_item_table { @non_lang_core_traits: - $( core::$($non_lang_module:ident)::*, $non_lang_trait:ident; )* + $( core::$($non_lang_trait_module:ident)::*, $non_lang_trait:ident; )* + + @non_lang_core_macros: + + $( core::$($non_lang_macro_module:ident)::*, $non_lang_macro:ident, $non_lang_macro_field:ident; )* ) => { #[allow(non_snake_case)] // FIXME: Should we remove this? #[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] @@ -207,6 +232,9 @@ macro_rules! language_item_table { $( pub $non_lang_trait: Option, )* + $( + pub $non_lang_macro_field: Option, + )* } impl LangItems { @@ -218,6 +246,7 @@ macro_rules! language_item_table { fn merge_prefer_self(&mut self, other: &Self) { $( self.$lang_item = self.$lang_item.or(other.$lang_item); )* $( self.$non_lang_trait = self.$non_lang_trait.or(other.$non_lang_trait); )* + $( self.$non_lang_macro_field = self.$non_lang_macro_field.or(other.$non_lang_macro_field); )* } fn assign_lang_item(&mut self, name: Symbol, target: LangItemTarget) { @@ -233,8 +262,9 @@ macro_rules! language_item_table { } } - fn fill_non_lang_core_traits(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) { - $( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_module),* ], sym::$non_lang_trait); )* + fn fill_non_lang_core_items(&mut self, db: &dyn DefDatabase, core_def_map: &DefMap) { + $( self.$non_lang_trait = resolve_core_trait(db, core_def_map, &[ $(sym::$non_lang_trait_module),* ], sym::$non_lang_trait); )* + $( self.$non_lang_macro_field = resolve_core_macro(db, core_def_map, &[ $(sym::$non_lang_macro_module),* ], sym::$non_lang_macro); )* } } @@ -479,4 +509,16 @@ language_item_table! { LangItems => core::hash, Hash; core::cmp, Ord; core::cmp, Eq; + + @non_lang_core_macros: + core::default, Default, DefaultDerive; + core::fmt, Debug, DebugDerive; + core::hash, Hash, HashDerive; + core::cmp, PartialOrd, PartialOrdDerive; + core::cmp, Ord, OrdDerive; + core::cmp, PartialEq, PartialEqDerive; + core::cmp, Eq, EqDerive; + core::marker, CoercePointee, CoercePointeeDerive; + core::marker, Copy, CopyDerive; + core::clone, Clone, CloneDerive; } 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 5f05cdb1e2ba..150372f1a0d9 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -61,6 +61,7 @@ mod tests; use std::ops::{Deref, DerefMut, Index, IndexMut}; use base_db::Crate; +use either::Either; use hir_expand::{ EditionedFileId, ErasedAstId, HirFileId, InFile, MacroCallId, mod_path::ModPath, name::Name, proc_macro::ProcMacroKind, @@ -75,8 +76,8 @@ use triomphe::Arc; use tt::TextRange; use crate::{ - AstId, BlockId, BlockLoc, ExternCrateId, FunctionId, FxIndexMap, Lookup, MacroCallStyles, - MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, + AstId, BlockId, BlockLoc, BuiltinDeriveImplId, ExternCrateId, FunctionId, FxIndexMap, Lookup, + MacroCallStyles, MacroExpander, MacroId, ModuleId, ModuleIdLt, ProcMacroId, UseId, db::DefDatabase, item_scope::{BuiltinShadowMode, ItemScope}, item_tree::TreeId, @@ -192,7 +193,8 @@ pub struct DefMap { /// Tracks which custom derives are in scope for an item, to allow resolution of derive helper /// attributes. // FIXME: Figure out a better way for the IDE layer to resolve these? - derive_helpers_in_scope: FxHashMap, Vec<(Name, MacroId, MacroCallId)>>, + derive_helpers_in_scope: + FxHashMap, Vec<(Name, MacroId, Either)>>, /// A mapping from [`hir_expand::MacroDefId`] to [`crate::MacroId`]. pub macro_def_to_macro_id: FxHashMap, @@ -540,7 +542,7 @@ impl DefMap { pub fn derive_helpers_in_scope( &self, id: AstId, - ) -> Option<&[(Name, MacroId, MacroCallId)]> { + ) -> Option<&[(Name, MacroId, Either)]> { self.derive_helpers_in_scope.get(&id.map(|it| it.upcast())).map(Deref::deref) } 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 87ade0651762..323060f61d15 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 @@ -3,7 +3,7 @@ //! `DefCollector::collect` contains the fixed-point iteration loop which //! resolves imports and expands macros. -use std::{iter, mem}; +use std::{iter, mem, ops::Range}; use base_db::{BuiltDependency, Crate, CrateOrigin, LangCrateOrigin}; use cfg::{CfgAtom, CfgExpr, CfgOptions}; @@ -226,6 +226,7 @@ struct DeferredBuiltinDerive { container: ItemContainerId, derive_attr_id: AttrId, derive_index: u32, + helpers_range: Range, } /// Walks the tree of module recursively @@ -1354,7 +1355,7 @@ impl<'db> DefCollector<'db> { if let Ok((macro_id, def_id, call_id)) = id { self.def_map.modules[directive.module_id].scope.set_derive_macro_invoc( ast_id.ast_id, - call_id, + Either::Left(call_id), *derive_attr, *derive_pos, ); @@ -1369,7 +1370,7 @@ impl<'db> DefCollector<'db> { .extend(izip!( helpers.iter().cloned(), iter::repeat(macro_id), - iter::repeat(call_id), + iter::repeat(Either::Left(call_id)), )); } } @@ -1492,6 +1493,8 @@ impl<'db> DefCollector<'db> { Interned::new(path), ); + derive_call_ids.push(None); + // Try to resolve the derive immediately. If we succeed, we can also use the fast path // for builtin derives. If not, we cannot use it, as it can cause the ADT to become // interned while the derive is still unresolved, which will cause it to get forgotten. @@ -1506,23 +1509,42 @@ impl<'db> DefCollector<'db> { call_id, ); + let ast_id_without_path = ast_id.ast_id; + let directive = MacroDirective { + module_id: directive.module_id, + depth: directive.depth + 1, + kind: MacroDirectiveKind::Derive { + ast_id, + derive_attr: *attr_id, + derive_pos: idx, + ctxt: call_site.ctx, + derive_macro_id: call_id, + }, + container: directive.container, + }; + if let Ok((macro_id, def_id, call_id)) = id { - derive_call_ids.push(Some(call_id)); + let (mut helpers_start, mut helpers_end) = (0, 0); // Record its helper attributes. if def_id.krate != self.def_map.krate { let def_map = crate_def_map(self.db, def_id.krate); if let Some(helpers) = def_map.data.exported_derives.get(¯o_id) { - self.def_map + let derive_helpers = self + .def_map .derive_helpers_in_scope - .entry(ast_id.ast_id.map(|it| it.upcast())) - .or_default() - .extend(izip!( - helpers.iter().cloned(), - iter::repeat(macro_id), - iter::repeat(call_id), - )); + .entry( + ast_id_without_path.map(|it| it.upcast()), + ) + .or_default(); + helpers_start = derive_helpers.len(); + derive_helpers.extend(izip!( + helpers.iter().cloned(), + iter::repeat(macro_id), + iter::repeat(Either::Left(call_id)), + )); + helpers_end = derive_helpers.len(); } } @@ -1531,7 +1553,7 @@ impl<'db> DefCollector<'db> { def_id.kind { self.deferred_builtin_derives - .entry(ast_id.ast_id.upcast()) + .entry(ast_id_without_path.upcast()) .or_default() .push(DeferredBuiltinDerive { call_id, @@ -1541,24 +1563,15 @@ impl<'db> DefCollector<'db> { depth: directive.depth, derive_attr_id: *attr_id, derive_index: idx as u32, + helpers_range: helpers_start..helpers_end, }); } else { - push_resolved(&mut resolved, directive, call_id); + push_resolved(&mut resolved, &directive, call_id); + *derive_call_ids.last_mut().unwrap() = + Some(Either::Left(call_id)); } } else { - derive_call_ids.push(None); - self.unresolved_macros.push(MacroDirective { - module_id: directive.module_id, - depth: directive.depth + 1, - kind: MacroDirectiveKind::Derive { - ast_id, - derive_attr: *attr_id, - derive_pos: idx, - ctxt: call_site.ctx, - derive_macro_id: call_id, - }, - container: directive.container, - }); + self.unresolved_macros.push(directive); } } @@ -1858,9 +1871,8 @@ impl ModCollector<'_, '_> { ast_id: FileAstId, id: AdtId, def_map: &mut DefMap| { - let Some(deferred_derives) = - deferred_derives.remove(&InFile::new(file_id, ast_id.upcast())) - else { + let ast_id = InFile::new(file_id, ast_id.upcast()); + let Some(deferred_derives) = deferred_derives.remove(&ast_id.upcast()) else { return; }; let module = &mut def_map.modules[module_id]; @@ -1876,6 +1888,22 @@ impl ModCollector<'_, '_> { }, ); module.scope.define_builtin_derive_impl(impl_id); + module.scope.set_derive_macro_invoc( + ast_id, + Either::Right(impl_id), + deferred_derive.derive_attr_id, + deferred_derive.derive_index as usize, + ); + // Change its helper attributes to the new id. + if let Some(derive_helpers) = + def_map.derive_helpers_in_scope.get_mut(&ast_id.map(|it| it.upcast())) + { + for (_, _, call_id) in + &mut derive_helpers[deferred_derive.helpers_range.clone()] + { + *call_id = Either::Right(impl_id); + } + } }); } }; diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs index f4c42537de93..e55b693ef018 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs @@ -13,7 +13,7 @@ use std::{ use base_db::FxIndexSet; use either::Either; use hir_def::{ - DefWithBodyId, MacroId, StructId, TraitId, VariantId, + BuiltinDeriveImplId, DefWithBodyId, HasModule, MacroId, StructId, TraitId, VariantId, attrs::parse_extra_crate_attrs, expr_store::{Body, ExprOrPatSource, HygieneId, path::Path}, hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat}, @@ -622,7 +622,20 @@ impl<'db> SemanticsImpl<'db> { Some( calls .into_iter() - .map(|call| macro_call_to_macro_id(ctx, call?).map(|id| Macro { id })) + .map(|call| { + let call = call?; + match call { + Either::Left(call) => { + macro_call_to_macro_id(ctx, call).map(|id| Macro { id }) + } + Either::Right(call) => { + let call = call.loc(self.db); + let krate = call.krate(self.db); + let lang_items = hir_def::lang_item::lang_items(self.db, krate); + call.trait_.derive_macro(lang_items).map(|id| Macro { id }) + } + } + }) .collect(), ) }) @@ -633,7 +646,7 @@ impl<'db> SemanticsImpl<'db> { .derive_macro_calls(attr)? .into_iter() .flat_map(|call| { - let file_id = call?; + let file_id = call?.left()?; let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id); let root_node = value.0.syntax_node(); self.cache(root_node.clone(), file_id.into()); @@ -643,7 +656,10 @@ impl<'db> SemanticsImpl<'db> { Some(res) } - fn derive_macro_calls(&self, attr: &ast::Attr) -> Option>> { + fn derive_macro_calls( + &self, + attr: &ast::Attr, + ) -> Option>>> { let adt = attr.syntax().parent().and_then(ast::Adt::cast)?; let file_id = self.find_file(adt.syntax()).file_id; let adt = InFile::new(file_id, &adt); @@ -690,8 +706,9 @@ impl<'db> SemanticsImpl<'db> { .derive_helpers_in_scope(InFile::new(sa.file_id, id))? .iter() .filter(|&(name, _, _)| *name == attr_name) - .map(|&(_, macro_, call)| (macro_.into(), call)) + .filter_map(|&(_, macro_, call)| Some((macro_.into(), call.left()?))) .collect(); + // FIXME: We filter our builtin derive "fake" expansions, is this correct? Should we still expose them somehow? res.is_empty().not().then_some(res) } @@ -1338,6 +1355,7 @@ impl<'db> SemanticsImpl<'db> { // FIXME: We need to call `f` for all of them as well though! process_expansion_for_token(ctx, &mut stack, derive_attr); for derive in derives.into_iter().flatten() { + let Either::Left(derive) = derive else { continue }; process_expansion_for_token(ctx, &mut stack, derive); } } @@ -1467,11 +1485,12 @@ impl<'db> SemanticsImpl<'db> { for (.., derive) in helpers.iter().filter(|(helper, ..)| *helper == attr_name) { + let Either::Left(derive) = *derive else { continue }; // as there may be multiple derives registering the same helper // name, we gotta make sure to call this for all of them! // FIXME: We need to call `f` for all of them as well though! res = res - .or(process_expansion_for_token(ctx, &mut stack, *derive)); + .or(process_expansion_for_token(ctx, &mut stack, derive)); } res }) diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs index 257405992731..d222c3dc7ed1 100644 --- a/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs +++ b/src/tools/rust-analyzer/crates/hir/src/semantics/source_to_def.rs @@ -87,10 +87,10 @@ use either::Either; use hir_def::{ - AdtId, BlockId, ConstId, ConstParamId, DefWithBodyId, EnumId, EnumVariantId, ExternBlockId, - ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, ImplId, LifetimeParamId, - Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, - UseId, VariantId, + AdtId, BlockId, BuiltinDeriveImplId, ConstId, ConstParamId, DefWithBodyId, EnumId, + EnumVariantId, ExternBlockId, ExternCrateId, FieldId, FunctionId, GenericDefId, GenericParamId, + ImplId, LifetimeParamId, Lookup, MacroId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, + TypeParamId, UnionId, UseId, VariantId, dyn_map::{ DynMap, keys::{self, Key}, @@ -394,7 +394,7 @@ impl SourceToDefCtx<'_, '_> { &mut self, item: InFile<&ast::Adt>, src: InFile, - ) -> Option<(AttrId, MacroCallId, &[Option])> { + ) -> Option<(AttrId, MacroCallId, &[Option>])> { let map = self.dyn_map(item)?; map[keys::DERIVE_MACRO_CALL] .get(&AstPtr::new(&src.value)) @@ -409,8 +409,11 @@ impl SourceToDefCtx<'_, '_> { pub(super) fn derive_macro_calls<'slf>( &'slf mut self, adt: InFile<&ast::Adt>, - ) -> Option])> + use<'slf>> - { + ) -> Option< + impl Iterator< + Item = (AttrId, MacroCallId, &'slf [Option>]), + > + use<'slf>, + > { self.dyn_map(adt).as_ref().map(|&map| { let dyn_map = &map[keys::DERIVE_MACRO_CALL]; adt.value diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs index 7d02b8091890..ba8b3aa9cafe 100644 --- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs +++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs @@ -583,26 +583,16 @@ fn main() { fn macro_expand_derive() { check( r#" -//- proc_macros: identity -//- minicore: clone, derive +//- proc_macros: identity, derive_identity +//- minicore: derive #[proc_macros::identity] -#[derive(C$0lone)] +#[derive(proc_macros::DeriveIde$0ntity)] struct Foo {} "#, expect![[r#" - Clone - impl <>core::clone::Clone for Foo< >where { - fn clone(&self) -> Self { - match self { - Foo{} - => Foo{} - , - - } - } - - }"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); } @@ -610,15 +600,17 @@ struct Foo {} fn macro_expand_derive2() { check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Cop$0y)] -#[derive(Clone)] +#[derive(proc_macros::$0DeriveIdentity)] +#[derive(proc_macros::DeriveIdentity)] struct Foo {} "#, expect![[r#" - Copy - impl <>core::marker::Copy for Foo< >where{}"#]], + proc_macros::DeriveIdentity + #[derive(proc_macros::DeriveIdentity)] + struct Foo{}"#]], ); } @@ -626,35 +618,27 @@ struct Foo {} fn macro_expand_derive_multi() { check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Cop$0y, Clone)] +#[derive(proc_macros::DeriveIdent$0ity, proc_macros::DeriveIdentity)] struct Foo {} "#, expect![[r#" - Copy - impl <>core::marker::Copy for Foo< >where{}"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); check( r#" -//- minicore: copy, clone, derive +//- proc_macros: derive_identity +//- minicore: derive -#[derive(Copy, Cl$0one)] +#[derive(proc_macros::DeriveIdentity, proc_macros::De$0riveIdentity)] struct Foo {} "#, expect![[r#" - Clone - impl <>core::clone::Clone for Foo< >where { - fn clone(&self) -> Self { - match self { - Foo{} - => Foo{} - , - - } - } - - }"#]], + proc_macros::DeriveIdentity + struct Foo{}"#]], ); } diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs index b6efc599f181..3fadca29d118 100644 --- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs +++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs @@ -532,4 +532,5 @@ define_symbols! { CoerceUnsized, DispatchFromDyn, define_opaque, + marker, } From 459d77e863f0607f4f0fdc25be19db6f49fdc9c0 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Thu, 8 Jan 2026 22:23:16 +0200 Subject: [PATCH 032/319] Publish smol_str v0.3.5 --- src/tools/rust-analyzer/Cargo.lock | 2 +- src/tools/rust-analyzer/lib/smol_str/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 8188fbf96064..5bdde7c7c3e6 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -2629,7 +2629,7 @@ dependencies = [ [[package]] name = "smol_str" -version = "0.3.4" +version = "0.3.5" dependencies = [ "arbitrary", "borsh", diff --git a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml index 118b25993ffe..4e7844b49e19 100644 --- a/src/tools/rust-analyzer/lib/smol_str/Cargo.toml +++ b/src/tools/rust-analyzer/lib/smol_str/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "smol_str" -version = "0.3.4" +version = "0.3.5" description = "small-string optimized string type with O(1) clone" license = "MIT OR Apache-2.0" repository = "https://github.com/rust-lang/rust-analyzer/tree/master/lib/smol_str" From 26be33ae18b2aaa376eba9e755b54bc11e104a7b Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Thu, 8 Jan 2026 15:07:24 +0800 Subject: [PATCH 033/319] Fix not disable string escape highlights Example --- with config `strings: false` ```rust fn main() { format_args!("foo\nbar\invalid"); } ``` **Before this PR** ```rust fn main() { format_args!("foo\nbar\invalid"); // ^^ EscapeSequence // ^^ InvalidEscapeSequence } ``` **After this PR** ```rust fn main() { format_args!("foo\nbar\invalid"); } ``` --- .../crates/ide/src/syntax_highlighting.rs | 16 ++++--- .../ide/src/syntax_highlighting/escape.rs | 43 ++++++++++++----- .../ide/src/syntax_highlighting/highlights.rs | 8 +++- .../test_data/highlight_strings_disabled.html | 47 +++++++++++++++++++ .../ide/src/syntax_highlighting/tests.rs | 17 +++++++ 5 files changed, 113 insertions(+), 18 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs index e7c5f95a250e..e64fd6488f2a 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting.rs @@ -513,21 +513,21 @@ fn string_injections( ); if !string.is_raw() { - highlight_escape_string(hl, &string); + highlight_escape_string(hl, config, &string); } } } else if let Some(byte_string) = ast::ByteString::cast(token.clone()) { if !byte_string.is_raw() { - highlight_escape_string(hl, &byte_string); + highlight_escape_string(hl, config, &byte_string); } } else if let Some(c_string) = ast::CString::cast(token.clone()) { if !c_string.is_raw() { - highlight_escape_string(hl, &c_string); + highlight_escape_string(hl, config, &c_string); } } else if let Some(char) = ast::Char::cast(token.clone()) { - highlight_escape_char(hl, &char) + highlight_escape_char(hl, config, &char) } else if let Some(byte) = ast::Byte::cast(token) { - highlight_escape_byte(hl, &byte) + highlight_escape_byte(hl, config, &byte) } ControlFlow::Continue(()) } @@ -586,7 +586,11 @@ fn descend_token( fn filter_by_config(highlight: &mut Highlight, config: &HighlightConfig<'_>) -> bool { match &mut highlight.tag { - HlTag::StringLiteral if !config.strings => return false, + HlTag::StringLiteral | HlTag::EscapeSequence | HlTag::InvalidEscapeSequence + if !config.strings => + { + return false; + } HlTag::Comment if !config.comments => return false, // If punctuation is disabled, make the macro bang part of the macro call again. tag @ HlTag::Punctuation(HlPunct::MacroBang) => { diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs index 094f88f3a864..4da69cc43d9e 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/escape.rs @@ -1,10 +1,14 @@ //! Syntax highlighting for escape sequences use crate::syntax_highlighting::highlights::Highlights; -use crate::{HlRange, HlTag}; +use crate::{HighlightConfig, HlRange, HlTag}; use syntax::ast::{Byte, Char, IsString}; use syntax::{AstToken, TextRange, TextSize}; -pub(super) fn highlight_escape_string(stack: &mut Highlights, string: &T) { +pub(super) fn highlight_escape_string( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + string: &T, +) { let text = string.text(); let start = string.syntax().text_range().start(); string.escaped_char_ranges(&mut |piece_range, char| { @@ -13,16 +17,23 @@ pub(super) fn highlight_escape_string(stack: &mut Highlights, strin Ok(_) => HlTag::EscapeSequence, Err(_) => HlTag::InvalidEscapeSequence, }; - stack.add(HlRange { - range: piece_range + start, - highlight: highlight.into(), - binding_hash: None, - }); + stack.add_with( + config, + HlRange { + range: piece_range + start, + highlight: highlight.into(), + binding_hash: None, + }, + ); } }); } -pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char) { +pub(super) fn highlight_escape_char( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + char: &Char, +) { if char.value().is_err() { // We do not emit invalid escapes highlighting here. The lexer would likely be in a bad // state and this token contains junk, since `'` is not a reliable delimiter (consider @@ -43,10 +54,17 @@ pub(super) fn highlight_escape_char(stack: &mut Highlights, char: &Char) { char.syntax().text_range().start() + TextSize::from(1), TextSize::from(text.len() as u32), ); - stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) + stack.add_with( + config, + HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }, + ) } -pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte) { +pub(super) fn highlight_escape_byte( + stack: &mut Highlights, + config: &HighlightConfig<'_>, + byte: &Byte, +) { if byte.value().is_err() { // See `highlight_escape_char` for why no error highlighting here. return; @@ -65,5 +83,8 @@ pub(super) fn highlight_escape_byte(stack: &mut Highlights, byte: &Byte) { byte.syntax().text_range().start() + TextSize::from(2), TextSize::from(text.len() as u32), ); - stack.add(HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }) + stack.add_with( + config, + HlRange { range, highlight: HlTag::EscapeSequence.into(), binding_hash: None }, + ) } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs index 340290eafedb..6fe4d0844338 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlights.rs @@ -4,7 +4,7 @@ use std::iter; use stdx::equal_range_by; use syntax::TextRange; -use crate::{HlRange, HlTag}; +use crate::{HighlightConfig, HlRange, HlTag}; pub(super) struct Highlights { root: Node, @@ -26,6 +26,12 @@ impl Highlights { self.root.add(hl_range); } + pub(super) fn add_with(&mut self, config: &HighlightConfig<'_>, mut hl_range: HlRange) { + if super::filter_by_config(&mut hl_range.highlight, config) { + self.root.add(hl_range); + } + } + pub(super) fn to_vec(&self) -> Vec { let mut res = Vec::new(); self.root.flatten(&mut res); diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html new file mode 100644 index 000000000000..344d0c2ff03b --- /dev/null +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings_disabled.html @@ -0,0 +1,47 @@ + + +
fn main() {
+    format_args!("foo\nbar");
+    format_args!("foo\invalid");
+}
\ No newline at end of file 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 89a5e434f90c..8b529cf10f7f 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 @@ -1498,6 +1498,23 @@ fn main() { ); } +#[test] +fn test_strings_highlighting_disabled() { + // Test that comments are not highlighted when disabled + check_highlighting_with_config( + r#" +//- minicore: fmt +fn main() { + format_args!("foo\nbar"); + format_args!("foo\invalid"); +} +"#, + HighlightConfig { strings: false, ..HL_CONFIG }, + expect_file!["./test_data/highlight_strings_disabled.html"], + false, + ); +} + #[test] fn regression_20952() { check_highlighting( From e80fbd4bca604211f810fc207f33089730a3e9e1 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 9 Jan 2026 13:47:13 +0200 Subject: [PATCH 034/319] Fix lifetimes len diagnostics for fn pointers --- .../rust-analyzer/crates/hir-ty/src/lower.rs | 51 ++++++++++--------- .../crates/hir-ty/src/lower/path.rs | 8 +-- .../src/handlers/missing_lifetime.rs | 15 ++++++ 3 files changed, 47 insertions(+), 27 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs index a97d7687162e..9befca11b3e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs @@ -27,8 +27,8 @@ use hir_def::{ resolver::{HasResolver, LifetimeNs, Resolver, TypeNs, ValueNs}, signatures::{FunctionSignature, TraitFlags, TypeAliasFlags}, type_ref::{ - ConstRef, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, TypeBound, - TypeRef, TypeRefId, + ConstRef, FnType, LifetimeRefId, PathId, TraitBoundModifier, TraitRef as HirTraitRef, + TypeBound, TypeRef, TypeRefId, }, }; use hir_expand::name::Name; @@ -98,7 +98,7 @@ impl ImplTraitLoweringState { } } -#[derive(Debug, Clone)] +#[derive(Debug, Clone, Copy)] pub enum LifetimeElisionKind<'db> { /// Create a new anonymous lifetime parameter and reference it. /// @@ -437,26 +437,7 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { Ty::new_ref(interner, lifetime, inner_ty, lower_mutability(ref_.mutability)) } TypeRef::Placeholder => Ty::new_error(interner, ErrorGuaranteed), - TypeRef::Fn(fn_) => { - let substs = self.with_shifted_in( - DebruijnIndex::from_u32(1), - |ctx: &mut TyLoweringContext<'_, '_>| { - Tys::new_from_iter( - interner, - fn_.params.iter().map(|&(_, tr)| ctx.lower_ty(tr)), - ) - }, - ); - Ty::new_fn_ptr( - interner, - Binder::dummy(FnSig { - abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), - safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, - c_variadic: fn_.is_varargs, - inputs_and_output: substs, - }), - ) - } + TypeRef::Fn(fn_) => self.lower_fn_ptr(fn_), TypeRef::DynTrait(bounds) => self.lower_dyn_trait(bounds), TypeRef::ImplTrait(bounds) => { match self.impl_trait_mode.mode { @@ -517,6 +498,30 @@ impl<'db, 'a> TyLoweringContext<'db, 'a> { (ty, res) } + fn lower_fn_ptr(&mut self, fn_: &FnType) -> Ty<'db> { + let interner = self.interner; + let (params, ret_ty) = fn_.split_params_and_ret(); + let old_lifetime_elision = self.lifetime_elision; + let mut args = Vec::with_capacity(fn_.params.len()); + self.with_shifted_in(DebruijnIndex::from_u32(1), |ctx: &mut TyLoweringContext<'_, '_>| { + ctx.lifetime_elision = + LifetimeElisionKind::AnonymousCreateParameter { report_in_path: false }; + args.extend(params.iter().map(|&(_, tr)| ctx.lower_ty(tr))); + ctx.lifetime_elision = LifetimeElisionKind::for_fn_ret(interner); + args.push(ctx.lower_ty(ret_ty)); + }); + self.lifetime_elision = old_lifetime_elision; + Ty::new_fn_ptr( + interner, + Binder::dummy(FnSig { + abi: fn_.abi.as_ref().map_or(FnAbi::Rust, FnAbi::from_symbol), + safety: if fn_.is_unsafe { Safety::Unsafe } else { Safety::Safe }, + c_variadic: fn_.is_varargs, + inputs_and_output: Tys::new_from_slice(&args), + }), + ) + } + /// This is only for `generic_predicates_for_param`, where we can't just /// lower the self types of the predicates since that could lead to cycles. /// So we just check here if the `type_ref` resolves to a generic param, and which. 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 b77aeab62d15..f3d0de12275e 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 @@ -599,7 +599,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { explicit_self_ty: Option>, lowering_assoc_type_generics: bool, ) -> GenericArgs<'db> { - let old_lifetime_elision = self.ctx.lifetime_elision.clone(); + let old_lifetime_elision = self.ctx.lifetime_elision; if let Some(args) = self.current_or_prev_segment.args_and_bindings && args.parenthesized != GenericArgsParentheses::No @@ -640,7 +640,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { explicit_self_ty, PathGenericsSource::Segment(self.current_segment_u32()), lowering_assoc_type_generics, - self.ctx.lifetime_elision.clone(), + self.ctx.lifetime_elision, ); self.ctx.lifetime_elision = old_lifetime_elision; result @@ -884,7 +884,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { assoc_type: binding_idx as u32, }, false, - this.ctx.lifetime_elision.clone(), + this.ctx.lifetime_elision, ) }); let args = GenericArgs::new_from_iter( @@ -902,7 +902,7 @@ impl<'a, 'b, 'db> PathLoweringContext<'a, 'b, 'db> { // `Fn()`-style generics are elided like functions. This is `Output` (we lower to it in hir-def). LifetimeElisionKind::for_fn_ret(self.ctx.interner) } else { - self.ctx.lifetime_elision.clone() + self.ctx.lifetime_elision }; self.with_lifetime_elision(lifetime_elision, |this| { match (&this.ctx.store[type_ref], this.ctx.impl_trait_mode.mode) { diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs index b07f9e68f634..5cb710b66b5f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_lifetime.rs @@ -100,4 +100,19 @@ fn foo WithLifetime>() {} "#, ); } + + #[test] + fn regression_21430() { + check_diagnostics( + r#" +struct S { + f: fn(A<()>), +} + +struct A<'a, T> { + a: &'a T, +} + "#, + ); + } } From e52695c3fca6f9b973c98103407d83d1c963cbdf Mon Sep 17 00:00:00 2001 From: Wilfred Hughes Date: Fri, 9 Jan 2026 11:58:16 +0000 Subject: [PATCH 035/319] internal: Include private definitions in generated rustdoc rust-analyzer has handy prebuilt `cargo doc` output at https://rust-lang.github.io/rust-analyzer/ide/ However, it doesn't include private definitions, which makes it less useful when trying to learn unfamiliar parts of the codebase. Instead, pass `--document-private-items` so the HTML includes information on private types and modules too. rustdoc renders these with a padlock icon, so it's still clear that they're private. This change also exposes some more rustdoc warnings, which I've fixed. --- src/tools/rust-analyzer/.github/workflows/rustdoc.yaml | 2 +- src/tools/rust-analyzer/crates/hir-def/src/nameres.rs | 2 +- .../crates/hir-expand/src/builtin/attr_macro.rs | 2 +- .../rust-analyzer/crates/hir-expand/src/cfg_process.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/infer/closure.rs | 2 +- .../rust-analyzer/crates/hir-ty/src/method_resolution.rs | 4 ++-- .../crates/hir-ty/src/method_resolution/probe.rs | 2 +- .../crates/hir-ty/src/next_solver/infer/mod.rs | 2 +- src/tools/rust-analyzer/crates/hir/src/term_search.rs | 2 +- .../crates/ide-assists/src/handlers/inline_type_alias.rs | 6 +++++- .../rust-analyzer/crates/ide-completion/src/context.rs | 2 +- src/tools/rust-analyzer/crates/parser/src/grammar.rs | 2 +- .../crates/project-model/src/cargo_workspace.rs | 2 +- .../crates/rust-analyzer/src/config/patch_old_style.rs | 2 +- .../rust-analyzer/crates/rust-analyzer/src/discover.rs | 4 ++-- .../crates/rust-analyzer/src/global_state.rs | 2 +- .../crates/rust-analyzer/src/handlers/dispatch.rs | 6 +++--- .../rust-analyzer/crates/rust-analyzer/src/task_pool.rs | 2 +- src/tools/rust-analyzer/crates/span/src/hygiene.rs | 8 ++++---- .../crates/syntax/src/syntax_editor/mapping.rs | 2 +- src/tools/rust-analyzer/lib/line-index/src/lib.rs | 2 +- 21 files changed, 32 insertions(+), 28 deletions(-) diff --git a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml index 9cc18fc69ede..0cc7ce77ddb6 100644 --- a/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml +++ b/src/tools/rust-analyzer/.github/workflows/rustdoc.yaml @@ -24,7 +24,7 @@ jobs: run: rustup update --no-self-update stable - name: Build Documentation - run: cargo doc --all --no-deps + run: cargo doc --all --no-deps --document-private-items - name: Deploy Docs uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0 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 150372f1a0d9..1e3ea50c5a0f 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres.rs @@ -216,7 +216,7 @@ struct DefMapCrateData { registered_tools: Vec, /// Unstable features of Rust enabled with `#![feature(A, B)]`. unstable_features: FxHashSet, - /// #[rustc_coherence_is_core] + /// `#[rustc_coherence_is_core]` rustc_coherence_is_core: bool, no_core: bool, no_std: bool, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs index 06b9b5418e37..c94663ca0cbc 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/builtin/attr_macro.rs @@ -115,7 +115,7 @@ fn dummy_gate_test_expand( /// wasting a lot of memory, and it would also require some way to use a path in a way that makes it /// always resolve as a derive without nameres recollecting them. /// So this hacky approach is a lot more friendly for us, though it does require a bit of support in -/// [`hir::Semantics`] to make this work. +/// hir::Semantics to make this work. fn derive_expand( db: &dyn ExpandDatabase, id: MacroCallId, diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs index a0de36548e9f..ccef9168ac3a 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/cfg_process.rs @@ -1,4 +1,4 @@ -//! Processes out #[cfg] and #[cfg_attr] attributes from the input for the derive macro +//! Processes out `#[cfg]` and `#[cfg_attr]` attributes from the input for the derive macro use std::{cell::OnceCell, ops::ControlFlow}; use ::tt::TextRange; diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs index d1391ad24e4d..ce99016470c1 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure.rs @@ -466,7 +466,7 @@ impl<'db> InferenceContext<'_, 'db> { } /// Given an `FnOnce::Output` or `AsyncFn::Output` projection, extract the args - /// and return type to infer a [`ty::PolyFnSig`] for the closure. + /// and return type to infer a `PolyFnSig` for the closure. fn extract_sig_from_projection( &self, projection: PolyProjectionPredicate<'db>, diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs index e4681b464fec..ad4d79e68a9f 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution.rs @@ -206,11 +206,11 @@ impl<'a, 'db> InferenceContext<'a, 'db> { } } -/// Used by [FnCtxt::lookup_method_for_operator] with `-Znext-solver`. +/// Used by `FnCtxt::lookup_method_for_operator` with `-Znext-solver`. /// /// With `AsRigid` we error on `impl Opaque: NotInItemBounds` while /// `AsInfer` just treats it as ambiguous and succeeds. This is necessary -/// as we want [FnCtxt::check_expr_call] to treat not-yet-defined opaque +/// as we want `FnCtxt::check_expr_call` to treat not-yet-defined opaque /// types as rigid to support `impl Deref` and /// `Box`. /// diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs index 4a7c7d93539e..42a590e8b4cb 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs @@ -1740,7 +1740,7 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> { /// We want to only accept trait methods if they were hold even if the /// opaque types were rigid. To handle this, we both check that for trait /// candidates the goal were to hold even when treating opaques as rigid, - /// see [OpaqueTypesJank](rustc_trait_selection::solve::OpaqueTypesJank). + /// see `rustc_trait_selection::solve::OpaqueTypesJank`. /// /// We also check that all opaque types encountered as self types in the /// autoderef chain don't get constrained when applying the candidate. diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs index 7d291f7ddbed..21baacb11693 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/mod.rs @@ -140,7 +140,7 @@ pub struct InferCtxtInner<'db> { /// /// Before running `resolve_regions_and_report_errors`, the creator /// of the inference context is expected to invoke - /// [`InferCtxt::process_registered_region_obligations`] + /// `InferCtxt::process_registered_region_obligations` /// for each body-id in this map, which will process the /// obligations within. This is expected to be done 'late enough' /// that all type inference variables have been bound and so forth. diff --git a/src/tools/rust-analyzer/crates/hir/src/term_search.rs b/src/tools/rust-analyzer/crates/hir/src/term_search.rs index e4089218305c..f2dc1ce798ad 100644 --- a/src/tools/rust-analyzer/crates/hir/src/term_search.rs +++ b/src/tools/rust-analyzer/crates/hir/src/term_search.rs @@ -172,7 +172,7 @@ impl<'db> LookupTable<'db> { /// Insert new type trees for type /// /// Note that the types have to be the same, unification is not enough as unification is not - /// transitive. For example Vec and FxHashSet both unify with Iterator, + /// transitive. For example `Vec` and `FxHashSet` both unify with `Iterator`, /// but they clearly do not unify themselves. fn insert(&mut self, ty: Type<'db>, exprs: impl Iterator>) { match self.data.get_mut(&ty) { diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs index ae8d130df23c..c7a48f3261a9 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/inline_type_alias.rs @@ -290,19 +290,23 @@ impl ConstAndTypeMap { /// ^ alias generic params /// let a: A<100>; /// ^ instance generic args -/// ``` /// /// generic['a] = '_ due to omission /// generic[N] = 100 due to the instance arg /// generic[T] = u64 due to the default param +/// ``` /// /// 2. Copy the concrete type and substitute in each found mapping: /// +/// ```ignore /// &'_ [u64; 100] +/// ``` /// /// 3. Remove wildcard lifetimes entirely: /// +/// ```ignore /// &[u64; 100] +/// ``` fn create_replacement( lifetime_map: &LifetimeMap, const_and_type_map: &ConstAndTypeMap, 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 d116f665adbd..cab8bced88df 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/context.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/context.rs @@ -628,7 +628,7 @@ impl CompletionContext<'_> { } /// A version of [`SemanticsScope::process_all_names`] that filters out `#[doc(hidden)]` items and - /// passes all doc-aliases along, to funnel it into [`Completions::add_path_resolution`]. + /// passes all doc-aliases along, to funnel it into `Completions::add_path_resolution`. pub(crate) fn process_all_names(&self, f: &mut dyn FnMut(Name, ScopeDef, Vec)) { let _p = tracing::info_span!("CompletionContext::process_all_names").entered(); self.scope.process_all_names(&mut |name, def| { diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar.rs b/src/tools/rust-analyzer/crates/parser/src/grammar.rs index bf8430294110..e481bbe9bc4a 100644 --- a/src/tools/rust-analyzer/crates/parser/src/grammar.rs +++ b/src/tools/rust-analyzer/crates/parser/src/grammar.rs @@ -6,7 +6,7 @@ //! each submodule starts with `use super::*` import and exports //! "public" productions via `pub(super)`. //! -//! See docs for [`Parser`](super::parser::Parser) to learn about API, +//! See docs for [`Parser`] to learn about API, //! available to the grammar, and see docs for [`Event`](super::event::Event) //! to learn how this actually manages to produce parse trees. //! diff --git a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs index 6e1a3f37ff1c..483ab2845045 100644 --- a/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs +++ b/src/tools/rust-analyzer/crates/project-model/src/cargo_workspace.rs @@ -640,7 +640,7 @@ impl FetchMetadata { /// Builds a command to fetch metadata for the given `cargo_toml` manifest. /// /// Performs a lightweight pre-fetch using the `--no-deps` option, - /// available via [`FetchMetadata::no_deps_metadata`], to gather basic + /// available via `FetchMetadata::no_deps_metadata`, to gather basic /// information such as the `target-dir`. /// /// The provided sysroot is used to set the `RUSTUP_TOOLCHAIN` diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs index 389bb7848c01..5dc463eccce4 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config/patch_old_style.rs @@ -3,7 +3,7 @@ use serde_json::{Value, json}; /// This function patches the json config to the new expected keys. /// That is we try to load old known config keys here and convert them to the new ones. -/// See https://github.com/rust-lang/rust-analyzer/pull/12010 +/// See /// /// We already have an alias system for simple cases, but if we make structural changes /// the alias infra fails down. diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs index 4aef5b0b7f3d..f129f156a030 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs @@ -42,7 +42,7 @@ impl DiscoverCommand { Self { sender, command } } - /// Spawn the command inside [Discover] and report progress, if any. + /// Spawn the command inside `DiscoverCommand` and report progress, if any. pub(crate) fn spawn( &self, discover_arg: DiscoverArgument, @@ -73,7 +73,7 @@ impl DiscoverCommand { } } -/// A handle to a spawned [Discover]. +/// A handle to a spawned `DiscoverCommand`. #[derive(Debug)] pub(crate) struct DiscoverHandle { pub(crate) handle: CommandHandle, 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 39b4aaa64738..afd4162de622 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 @@ -188,7 +188,7 @@ pub(crate) struct GlobalState { /// been called. pub(crate) deferred_task_queue: DeferredTaskQueue, - /// HACK: Workaround for https://github.com/rust-lang/rust-analyzer/issues/19709 + /// HACK: Workaround for /// This is marked true if we failed to load a crate root file at crate graph creation, /// which will usually end up causing a bunch of incorrect diagnostics on startup. pub(crate) incomplete_crate_graph: bool, 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 10bbb0bb31d9..90deae2d902e 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 @@ -101,7 +101,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a default constructed [`R::Result`]. + /// ready this will return a default constructed `R::Result`. pub(crate) fn on( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, @@ -128,7 +128,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a non-latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a `default` constructed [`R::Result`]. + /// ready this will return a `default` constructed `R::Result`. pub(crate) fn on_with_vfs_default( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, @@ -176,7 +176,7 @@ impl RequestDispatcher<'_> { } /// Dispatches a latency-sensitive request onto the thread pool. When the VFS is marked not - /// ready this will return a default constructed [`R::Result`]. + /// ready this will return a default constructed `R::Result`. pub(crate) fn on_latency_sensitive( &mut self, f: fn(GlobalStateSnapshot, R::Params) -> anyhow::Result, diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs index 8b8876b801cf..104cd3d2eae9 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/task_pool.rs @@ -52,7 +52,7 @@ impl TaskPool { /// `DeferredTaskQueue` holds deferred tasks. /// /// These are tasks that must be run after -/// [`GlobalState::process_changes`] has been called. +/// `GlobalState::process_changes` has been called. pub(crate) struct DeferredTaskQueue { pub(crate) sender: crossbeam_channel::Sender, pub(crate) receiver: crossbeam_channel::Receiver, diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index ea4f4c5efb42..92bf892ea529 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -8,9 +8,9 @@ //! //! # The Expansion Order Hierarchy //! -//! `ExpnData` in rustc, rust-analyzer's version is [`MacroCallLoc`]. Traversing the hierarchy -//! upwards can be achieved by walking up [`MacroCallLoc::kind`]'s contained file id, as -//! [`MacroFile`]s are interned [`MacroCallLoc`]s. +//! `ExpnData` in rustc, rust-analyzer's version is `MacroCallLoc`. Traversing the hierarchy +//! upwards can be achieved by walking up `MacroCallLoc::kind`'s contained file id, as +//! `MacroFile`s are interned `MacroCallLoc`s. //! //! # The Macro Definition Hierarchy //! @@ -18,7 +18,7 @@ //! //! # The Call-site Hierarchy //! -//! `ExpnData::call_site` in rustc, [`MacroCallLoc::call_site`] in rust-analyzer. +//! `ExpnData::call_site` in rustc, `MacroCallLoc::call_site` in rust-analyzer. use crate::Edition; use std::fmt; diff --git a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs index 1eaef03197c5..6257bf4e572e 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/syntax_editor/mapping.rs @@ -1,6 +1,6 @@ //! Maps syntax elements through disjoint syntax nodes. //! -//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a [`SyntaxEditor`] +//! [`SyntaxMappingBuilder`] should be used to create mappings to add to a `SyntaxEditor` use itertools::Itertools; use rustc_hash::FxHashMap; diff --git a/src/tools/rust-analyzer/lib/line-index/src/lib.rs b/src/tools/rust-analyzer/lib/line-index/src/lib.rs index 905da330e64b..d5f0584d988f 100644 --- a/src/tools/rust-analyzer/lib/line-index/src/lib.rs +++ b/src/tools/rust-analyzer/lib/line-index/src/lib.rs @@ -207,7 +207,7 @@ impl LineIndex { } } -/// This is adapted from the rustc_span crate, https://github.com/rust-lang/rust/blob/de59844c98f7925242a798a72c59dc3610dd0e2c/compiler/rustc_span/src/analyze_source_file.rs +/// This is adapted from the rustc_span crate, fn analyze_source_file(src: &str) -> (Vec, IntMap>) { assert!(src.len() < !0u32 as usize); let mut lines = vec![]; From 998a5ac623d9879cfba667d6220a8f7d228845e8 Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Fri, 9 Jan 2026 15:55:45 +0200 Subject: [PATCH 036/319] Remove code made redundant by method resolution rewrite Its job is now done elsewhere, and it's also wrong (not accounting for autoderef) --- .../crates/hir-ty/src/infer/expr.rs | 12 ++-- .../crates/hir-ty/src/tests/regression.rs | 55 +++++++++++++--- .../crates/hir-ty/src/tests/traits.rs | 66 +++++++++++-------- 3 files changed, 88 insertions(+), 45 deletions(-) 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 226e9f5cd667..62339779a562 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 @@ -1704,7 +1704,7 @@ impl<'db> InferenceContext<'_, 'db> { }); match resolved { Ok((func, _is_visible)) => { - self.check_method_call(tgt_expr, &[], func.sig, receiver_ty, expected) + self.check_method_call(tgt_expr, &[], func.sig, expected) } Err(_) => self.err_ty(), } @@ -1844,7 +1844,7 @@ impl<'db> InferenceContext<'_, 'db> { item: func.def_id.into(), }) } - self.check_method_call(tgt_expr, args, func.sig, receiver_ty, expected) + self.check_method_call(tgt_expr, args, func.sig, expected) } // Failed to resolve, report diagnostic and try to resolve as call to field access or // assoc function @@ -1934,16 +1934,14 @@ impl<'db> InferenceContext<'_, 'db> { tgt_expr: ExprId, args: &[ExprId], sig: FnSig<'db>, - receiver_ty: Ty<'db>, expected: &Expectation<'db>, ) -> Ty<'db> { - let (formal_receiver_ty, param_tys) = if !sig.inputs_and_output.inputs().is_empty() { - (sig.inputs_and_output.as_slice()[0], &sig.inputs_and_output.inputs()[1..]) + let param_tys = if !sig.inputs_and_output.inputs().is_empty() { + &sig.inputs_and_output.inputs()[1..] } else { - (self.types.types.error, &[] as _) + &[] }; let ret_ty = sig.output(); - self.table.unify(formal_receiver_ty, receiver_ty); self.check_call_arguments(tgt_expr, param_tys, ret_ty, expected, args, &[], sig.c_variadic); ret_ty 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 c805f030446c..df49d7999fee 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 @@ -891,13 +891,14 @@ use core::ops::Deref; struct BufWriter {} -struct Mutex {} -struct MutexGuard<'a, T> {} +struct Mutex(T); +struct MutexGuard<'a, T>(&'a T); impl Mutex { fn lock(&self) -> MutexGuard<'_, T> {} } impl<'a, T: 'a> Deref for MutexGuard<'a, T> { type Target = T; + fn deref(&self) -> &Self::Target { loop {} } } fn flush(&self) { let w: &Mutex; @@ -905,14 +906,18 @@ fn flush(&self) { } "#, expect![[r#" - 123..127 'self': &'? Mutex - 150..152 '{}': MutexGuard<'?, T> - 234..238 'self': &'? {unknown} - 240..290 '{ ...()); }': () - 250..251 'w': &'? Mutex - 276..287 '*(w.lock())': BufWriter - 278..279 'w': &'? Mutex - 278..286 'w.lock()': MutexGuard<'?, BufWriter> + 129..133 'self': &'? Mutex + 156..158 '{}': MutexGuard<'?, T> + 242..246 'self': &'? MutexGuard<'a, T> + 265..276 '{ loop {} }': &'? T + 267..274 'loop {}': ! + 272..274 '{}': () + 289..293 'self': &'? {unknown} + 295..345 '{ ...()); }': () + 305..306 'w': &'? Mutex + 331..342 '*(w.lock())': BufWriter + 333..334 'w': &'? Mutex + 333..341 'w.lock()': MutexGuard<'?, BufWriter> "#]], ); } @@ -2563,3 +2568,33 @@ fn main() { "#, ); } + +#[test] +fn regression_21429() { + check_no_mismatches( + r#" +trait DatabaseLike { + type ForeignKey: ForeignKeyLike; +} + +trait ForeignKeyLike { + type DB: DatabaseLike; + + fn host_columns(&self, database: &Self::DB); +} + +trait ColumnLike { + type DB: DatabaseLike; + + fn foo() -> &&<::DB as DatabaseLike>::ForeignKey { + loop {} + } + + fn foreign_keys(&self, database: &Self::DB) { + let fk = Self::foo(); + fk.host_columns(database); + } +} + "#, + ); +} diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs index 38591f486e97..b825a0a8f0e5 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs @@ -429,7 +429,7 @@ fn associated_type_shorthand_from_method_bound() { trait Iterable { type Item; } -struct S; +struct S(T); impl S { fn foo(self) -> T::Item where T: Iterable { loop {} } } @@ -1103,40 +1103,50 @@ fn test() { fn argument_impl_trait_type_args_2() { check_infer_with_mismatches( r#" -//- minicore: sized +//- minicore: sized, phantom_data +use core::marker::PhantomData; + trait Trait {} struct S; impl Trait for S {} -struct F; +struct F(PhantomData); impl F { fn foo(self, x: impl Trait) -> (T, U) { loop {} } } fn test() { - F.foo(S); - F::.foo(S); - F::.foo::(S); - F::.foo::(S); // extraneous argument should be ignored + F(PhantomData).foo(S); + F::(PhantomData).foo(S); + F::(PhantomData).foo::(S); + F::(PhantomData).foo::(S); // extraneous argument should be ignored }"#, expect![[r#" - 87..91 'self': F - 93..94 'x': impl Trait - 118..129 '{ loop {} }': (T, U) - 120..127 'loop {}': ! - 125..127 '{}': () - 143..283 '{ ...ored }': () - 149..150 'F': F<{unknown}> - 149..157 'F.foo(S)': ({unknown}, {unknown}) - 155..156 'S': S - 163..171 'F::': F - 163..178 'F::.foo(S)': (u32, {unknown}) - 176..177 'S': S - 184..192 'F::': F - 184..206 'F::(S)': (u32, i32) - 204..205 'S': S - 212..220 'F::': F - 212..239 'F::(S)': (u32, i32) - 237..238 'S': S + 135..139 'self': F + 141..142 'x': impl Trait + 166..177 '{ loop {} }': (T, U) + 168..175 'loop {}': ! + 173..175 '{}': () + 191..383 '{ ...ored }': () + 197..198 'F': fn F<{unknown}>(PhantomData<{unknown}>) -> F<{unknown}> + 197..211 'F(PhantomData)': F<{unknown}> + 197..218 'F(Phan...foo(S)': ({unknown}, {unknown}) + 199..210 'PhantomData': PhantomData<{unknown}> + 216..217 'S': S + 224..232 'F::': fn F(PhantomData) -> F + 224..245 'F:: + 224..252 'F:: + 250..251 'S': S + 258..266 'F::': fn F(PhantomData) -> F + 258..279 'F:: + 258..293 'F::(S)': (u32, i32) + 267..278 'PhantomData': PhantomData + 291..292 'S': S + 299..307 'F::': fn F(PhantomData) -> F + 299..320 'F:: + 299..339 'F::(S)': (u32, i32) + 308..319 'PhantomData': PhantomData + 337..338 'S': S "#]], ); } @@ -4012,7 +4022,7 @@ fn f() { fn dyn_map() { check_types( r#" -pub struct Key {} +pub struct Key(K, V, P); pub trait Policy { type K; @@ -4024,7 +4034,7 @@ impl Policy for (K, V) { type V = V; } -pub struct KeyMap {} +pub struct KeyMap(KEY); impl KeyMap> { pub fn get(&self, key: &P::K) -> P::V { @@ -5023,7 +5033,7 @@ fn main() { 278..280 '{}': () 290..291 '_': Box + '?> 294..298 'iter': Box + 'static> - 294..310 'iter.i...iter()': Box + 'static> + 294..310 'iter.i...iter()': Box + '?> 152..156 'self': &'? mut Box 177..208 '{ ... }': Option<::Item> 191..198 'loop {}': ! From bcf059c81eb056c39cb03958feb6baea289690b4 Mon Sep 17 00:00:00 2001 From: cry-inc Date: Fri, 9 Jan 2026 18:23:37 +0100 Subject: [PATCH 037/319] Fix issue with ignore attribute for tests where the attribute has a value with the reason --- src/tools/rust-analyzer/crates/hir-def/src/attrs.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs index 83df11f2d2a4..0b8f65687218 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/attrs.rs @@ -135,6 +135,7 @@ fn match_attr_flags(attr_flags: &mut AttrFlags, attr: Meta) -> ControlFlow match name.text() { "deprecated" => attr_flags.insert(AttrFlags::IS_DEPRECATED), + "ignore" => attr_flags.insert(AttrFlags::IS_IGNORE), "lang" => attr_flags.insert(AttrFlags::LANG_ITEM), "path" => attr_flags.insert(AttrFlags::HAS_PATH), "unstable" => attr_flags.insert(AttrFlags::IS_UNSTABLE), From c825a504ab7d284610adf43d496fbfe899ed90aa Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jan 2026 09:51:42 +0100 Subject: [PATCH 038/319] Cleanup --- .../rust-analyzer/crates/hir/src/symbols.rs | 145 +++--- .../crates/ide-db/src/symbol_index.rs | 185 ++------ .../ide-db/src/test_data/test_doc_alias.txt | 112 +++-- .../test_symbol_index_collection.txt | 424 +++++++++++------- .../test_symbols_exclude_imports.txt | 12 +- .../test_data/test_symbols_with_imports.txt | 24 +- .../crates/ide/src/navigation_target.rs | 4 +- .../crates/mbe/src/expander/matcher.rs | 5 +- .../rust-analyzer/tests/slow-tests/main.rs | 44 +- .../rust-analyzer/crates/syntax/src/ptr.rs | 2 +- 10 files changed, 507 insertions(+), 450 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs index 544c759ed3a7..f9002f31fd15 100644 --- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs +++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs @@ -21,12 +21,9 @@ use hir_ty::{ }; use intern::Symbol; use rustc_hash::FxHashMap; -use syntax::{ - AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, - ast::{HasModuleItem, HasName}, -}; +use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName}; -use crate::{Crate, HasCrate, Module, ModuleDef, Semantics}; +use crate::{HasCrate, Module, ModuleDef, Semantics}; /// The actual data that is stored in the index. It should be as compact as /// possible. @@ -44,14 +41,14 @@ pub struct FileSymbol<'db> { _marker: PhantomData<&'db ()>, } -#[derive(Debug, Clone, PartialEq, Eq, Hash)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct DeclarationLocation { /// The file id for both the `ptr` and `name_ptr`. pub hir_file_id: HirFileId, /// This points to the whole syntax node of the declaration. pub ptr: SyntaxNodePtr, /// This points to the [`syntax::ast::Name`] identifier of the declaration. - pub name_ptr: AstPtr>, + pub name_ptr: Option>>, } impl DeclarationLocation { @@ -61,70 +58,6 @@ impl DeclarationLocation { } } -impl<'db> FileSymbol<'db> { - /// Create a `FileSymbol` representing a crate's root module. - /// This is used for crate search queries like `::` or `::foo`. - pub fn for_crate_root(db: &'db dyn HirDatabase, krate: Crate) -> Option> { - let display_name = krate.display_name(db)?; - let crate_name = display_name.crate_name(); - let root_module = krate.root_module(db); - let def_map = crate_def_map(db, krate.into()); - let module_data = &def_map[root_module.into()]; - - // Get the definition source (the source file for crate roots) - let definition = module_data.origin.definition_source(db); - let hir_file_id = definition.file_id; - - // For a crate root, the "declaration" is the source file itself - // We use the entire file's syntax node as the location - let syntax_node = definition.value.node(); - let ptr = SyntaxNodePtr::new(&syntax_node); - - // For the name, we need to create a synthetic name pointer. - // We'll use the first token of the file as a placeholder since crate roots - // don't have an explicit name in the source. - // We create a name_ptr pointing to the start of the file. - let name_ptr = match &definition.value { - crate::ModuleSource::SourceFile(sf) => { - // Try to find the first item with a name as a reasonable location for focus - // This is a bit of a hack but works for navigation purposes - let first_item: Option = sf.items().next(); - if let Some(item) = first_item { - if let Some(name) = item.syntax().children().find_map(syntax::ast::Name::cast) { - AstPtr::new(&name).wrap_left() - } else { - // No name found, try to use a NameRef instead - if let Some(name_ref) = - item.syntax().descendants().find_map(syntax::ast::NameRef::cast) - { - AstPtr::new(&name_ref).wrap_right() - } else { - return None; - } - } - } else { - return None; - } - } - _ => return None, - }; - - let loc = DeclarationLocation { hir_file_id, ptr, name_ptr }; - - Some(FileSymbol { - name: Symbol::intern(crate_name.as_str()), - def: ModuleDef::Module(root_module), - loc, - container_name: None, - is_alias: false, - is_assoc: false, - is_import: false, - do_not_complete: Complete::Yes, - _marker: PhantomData, - }) - } -} - /// Represents an outstanding module that the symbol collector must collect symbols from. #[derive(Debug)] struct SymbolCollectorWork { @@ -167,6 +100,11 @@ impl<'a> SymbolCollector<'a> { let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered(); tracing::info!(?module, "SymbolCollector::collect"); + // If this is a crate root module, add a symbol for the crate itself + if module.is_crate_root(self.db) { + self.push_crate_root(module); + } + // The initial work is the root module we're collecting, additional work will // be populated as we traverse the module's definitions. self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None }); @@ -176,6 +114,51 @@ impl<'a> SymbolCollector<'a> { } } + /// Push a symbol for a crate's root module. + /// This allows crate roots to appear in the symbol index for queries like `::` or `::foo`. + fn push_crate_root(&mut self, module: Module) { + let krate = module.krate(self.db); + let Some(display_name) = krate.display_name(self.db) else { return }; + let crate_name = display_name.crate_name(); + let canonical_name = display_name.canonical_name(); + + let def_map = crate_def_map(self.db, krate.into()); + let module_data = &def_map[def_map.crate_root(self.db)]; + + let definition = module_data.origin.definition_source(self.db); + let hir_file_id = definition.file_id; + let syntax_node = definition.value.node(); + let ptr = SyntaxNodePtr::new(&syntax_node); + + let loc = DeclarationLocation { hir_file_id, ptr, name_ptr: None }; + + self.symbols.insert(FileSymbol { + name: crate_name.symbol().clone(), + def: ModuleDef::Module(module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }); + + if canonical_name != crate_name.symbol() { + self.symbols.insert(FileSymbol { + name: canonical_name.clone(), + def: ModuleDef::Module(module), + loc, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Complete::Yes, + _marker: PhantomData, + }); + } + } + pub fn finish(self) -> Box<[FileSymbol<'a>]> { self.symbols.into_iter().collect() } @@ -277,7 +260,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(use_tree_src.syntax()), - name_ptr: AstPtr::new(&name_syntax), + name_ptr: Some(AstPtr::new(&name_syntax)), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -312,7 +295,7 @@ impl<'a> SymbolCollector<'a> { let dec_loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_syntax), + name_ptr: Some(AstPtr::new(&name_syntax)), }; this.symbols.insert(FileSymbol { name: name.symbol().clone(), @@ -477,10 +460,10 @@ impl<'a> SymbolCollector<'a> { let source = loc.source(self.db); let Some(name_node) = source.value.name() else { return Complete::Yes }; let def = ModuleDef::from(id.into()); - let dec_loc = DeclarationLocation { + let loc = DeclarationLocation { hir_file_id: source.file_id, ptr: SyntaxNodePtr::new(source.value.syntax()), - name_ptr: AstPtr::new(&name_node).wrap_left(), + name_ptr: Some(AstPtr::new(&name_node).wrap_left()), }; let mut do_not_complete = Complete::Yes; @@ -495,7 +478,7 @@ impl<'a> SymbolCollector<'a> { self.symbols.insert(FileSymbol { name: alias.clone(), def, - loc: dec_loc.clone(), + loc, container_name: self.current_container_name.clone(), is_alias: true, is_assoc, @@ -510,7 +493,7 @@ impl<'a> SymbolCollector<'a> { name: name.symbol().clone(), def, container_name: self.current_container_name.clone(), - loc: dec_loc, + loc, is_alias: false, is_assoc, is_import: false, @@ -527,10 +510,10 @@ impl<'a> SymbolCollector<'a> { let Some(declaration) = module_data.origin.declaration() else { return }; let module = declaration.to_node(self.db); let Some(name_node) = module.name() else { return }; - let dec_loc = DeclarationLocation { + let loc = DeclarationLocation { hir_file_id: declaration.file_id, ptr: SyntaxNodePtr::new(module.syntax()), - name_ptr: AstPtr::new(&name_node).wrap_left(), + name_ptr: Some(AstPtr::new(&name_node).wrap_left()), }; let def = ModuleDef::Module(module_id.into()); @@ -543,7 +526,7 @@ impl<'a> SymbolCollector<'a> { self.symbols.insert(FileSymbol { name: alias.clone(), def, - loc: dec_loc.clone(), + loc, container_name: self.current_container_name.clone(), is_alias: true, is_assoc: false, @@ -558,7 +541,7 @@ impl<'a> SymbolCollector<'a> { name: name.symbol().clone(), def: ModuleDef::Module(module_id.into()), container_name: self.current_container_name.clone(), - loc: dec_loc, + loc, is_alias: false, is_assoc: false, is_import: false, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs index ca0d5ec1e5e6..05c3f360fa87 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs @@ -129,58 +129,19 @@ impl Query { /// - `anchor_to_crate`: Whether the first segment must be a crate name fn parse_path_query(query: &str) -> (Vec, String, bool) { // Check for leading :: (absolute path / crate search) - let anchor_to_crate = query.starts_with("::"); - let query = if anchor_to_crate { &query[2..] } else { query }; - - // Handle sole "::" - return all crates - if query.is_empty() && anchor_to_crate { - return (vec![], String::new(), true); - } - - // Check for trailing :: (module browsing - returns all items in module) - let return_all_in_module = query.ends_with("::"); - let query = if return_all_in_module { query.trim_end_matches("::") } else { query }; - - if !query.contains("::") { - // No path separator - single segment - if anchor_to_crate && !return_all_in_module { - // "::foo" - fuzzy search crate names only - return (vec![], query.to_string(), true); - } - if return_all_in_module { - // "foo::" - browse all items in module "foo" - // path_filter = ["foo"], query = "", anchor_to_crate = false/true - return (vec![query.to_string()], String::new(), anchor_to_crate); - } - // Plain "foo" - normal fuzzy search - return (vec![], query.to_string(), false); - } - - // Filter out empty segments (e.g., "foo::::bar" -> "foo::bar") - let segments: Vec<&str> = query.split("::").filter(|s| !s.is_empty()).collect(); - - if segments.is_empty() { - return (vec![], String::new(), anchor_to_crate); - } - - let path: Vec = - segments[..segments.len() - 1].iter().map(|s| s.to_string()).collect(); - let item = if return_all_in_module { - // All segments go to path, item is empty - let mut path = path; - path.push(segments.last().unwrap().to_string()); - return (path, String::new(), anchor_to_crate); - } else { - segments.last().unwrap_or(&"").to_string() + let (query, anchor_to_crate) = match query.strip_prefix("::") { + Some(q) => (q, true), + None => (query, false), }; - (path, item, anchor_to_crate) - } + let Some((prefix, query)) = query.rsplit_once("::") else { + return (vec![], query.to_owned(), anchor_to_crate); + }; - /// Returns true if this query should return all items in a module - /// (i.e., the original query ended with `::`) - fn is_module_browsing(&self) -> bool { - self.query.is_empty() && !self.path_filter.is_empty() + let prefix: Vec<_> = + prefix.split("::").filter(|s| !s.is_empty()).map(ToOwned::to_owned).collect(); + + (prefix, query.to_owned(), anchor_to_crate) } /// Returns true if this query is searching for crates @@ -245,11 +206,14 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_ // That is, `#` switches from "types" to all symbols, `*` switches from the current // workspace to dependencies. // -// Note that filtering does not currently work in VSCode due to the editor never -// sending the special symbols to the language server. Instead, you can configure -// the filtering via the `rust-analyzer.workspace.symbol.search.scope` and -// `rust-analyzer.workspace.symbol.search.kind` settings. Symbols prefixed -// with `__` are hidden from the search results unless configured otherwise. +// This also supports general Rust path syntax with the usual rules. +// +// Note that paths do not currently work in VSCode due to the editor never +// sending the special symbols to the language server. Some other editors might not support the # or +// * search either, instead, you can configure the filtering via the +// `rust-analyzer.workspace.symbol.search.scope` and `rust-analyzer.workspace.symbol.search.kind` +// settings. Symbols prefixed with `__` are hidden from the search results unless configured +// otherwise. // // | Editor | Shortcut | // |---------|-----------| @@ -257,12 +221,11 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { let _p = tracing::info_span!("world_symbols", query = ?query.query).entered(); - // Handle special case: "::" alone or "::foo" for crate search if query.is_crate_search() { return search_crates(db, &query); } - // If we have a path filter, resolve it to target modules first + // If we have a path filter, resolve it to target modules let indices: Vec<_> = if !query.path_filter.is_empty() { let target_modules = resolve_path_to_modules( db, @@ -272,13 +235,11 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { ); if target_modules.is_empty() { - return vec![]; // Path doesn't resolve to any module + return vec![]; } - // Get symbol indices only for the resolved modules target_modules.iter().map(|&module| SymbolIndex::module_symbols(db, module)).collect() } else if query.libs { - // Original behavior for non-path queries searching libs LibraryRoots::get(db) .roots(db) .par_iter() @@ -289,7 +250,6 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { .map(|&root| SymbolIndex::library_symbols(db, root)) .collect() } else { - // Original behavior for non-path queries searching local crates let mut crates = Vec::new(); for &root in LocalRoots::get(db).roots(db).iter() { @@ -303,23 +263,11 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> { let mut res = vec![]; - // For module browsing (empty query, non-empty path_filter), return all symbols - if query.is_module_browsing() { - for index in &indices { - for symbol in index.symbols.iter() { - // Apply existing filters (only_types, assoc_mode, exclude_imports, etc.) - if query.matches_symbol_filters(symbol) { - res.push(symbol.clone()); - } - } - } - } else { - // Normal search: use FST to match item name - query.search::<()>(&indices, |f| { - res.push(f.clone()); - ControlFlow::Continue(()) - }); - } + // Normal search: use FST to match item name + query.search::<()>(&indices, |f| { + res.push(f.clone()); + ControlFlow::Continue(()) + }); res } @@ -341,9 +289,15 @@ fn search_crates<'db>(db: &'db RootDatabase, query: &Query) -> Vec) -> bool { - // Check only_types filter - if self.only_types - && !matches!( - symbol.def, - hir::ModuleDef::Adt(..) - | hir::ModuleDef::TypeAlias(..) - | hir::ModuleDef::BuiltinType(..) - | hir::ModuleDef::Trait(..) - ) - { - return false; - } - - // Check assoc_mode filter - if !self.matches_assoc_mode(symbol.is_assoc) { - return false; - } - - // Check exclude_imports filter - if self.exclude_imports && symbol.is_import { - return false; - } - - // Check underscore prefix - let ignore_underscore_prefixed = !self.query.starts_with("__"); - if ignore_underscore_prefixed && symbol.name.as_str().starts_with("__") { - return false; - } - - true - } } #[cfg(test)] @@ -939,34 +858,6 @@ pub struct Foo; assert!(!anchor); } - #[test] - fn test_query_modes() { - // Test is_module_browsing - let query = Query::new("foo::".to_owned()); - assert!(query.is_module_browsing()); - assert!(!query.is_crate_search()); - - // Test is_crate_search with sole :: - let query = Query::new("::".to_owned()); - assert!(!query.is_module_browsing()); - assert!(query.is_crate_search()); - - // Test is_crate_search with ::foo - let query = Query::new("::foo".to_owned()); - assert!(!query.is_module_browsing()); - assert!(query.is_crate_search()); - - // Normal query should be neither - let query = Query::new("foo".to_owned()); - assert!(!query.is_module_browsing()); - assert!(!query.is_crate_search()); - - // Path query should be neither - let query = Query::new("foo::bar".to_owned()); - assert!(!query.is_module_browsing()); - assert!(!query.is_crate_search()); - } - #[test] fn test_path_search() { let (mut db, _) = RootDatabase::with_many_files( diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt index 5783d97564d0..71680699b739 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt @@ -27,11 +27,13 @@ kind: STRUCT, range: 83..119, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), ), }, container_name: None, @@ -62,11 +64,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -97,11 +101,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -132,11 +138,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -146,6 +154,34 @@ do_not_complete: Yes, _marker: PhantomData<&()>, }, + FileSymbol { + name: "ra_test_fixture", + def: Module( + Module { + id: ModuleIdLt { + [salsa id]: Id(3800), + }, + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + EditionedFileId( + Id(3000), + ), + ), + ptr: SyntaxNodePtr { + kind: SOURCE_FILE, + range: 0..128, + }, + name_ptr: None, + }, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Yes, + _marker: PhantomData<&()>, + }, FileSymbol { name: "s1", def: Adt( @@ -167,11 +203,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, @@ -202,11 +240,13 @@ kind: STRUCT, range: 83..119, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 109..118, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 109..118, + }, + ), ), }, container_name: None, @@ -237,11 +277,13 @@ kind: STRUCT, range: 0..81, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 74..80, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 74..80, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt index 7692a7d61abf..2d62a56fe22d 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt @@ -25,11 +25,13 @@ kind: VARIANT, range: 201..202, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 201..202, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 201..202, + }, + ), ), }, container_name: Some( @@ -60,11 +62,13 @@ kind: TYPE_ALIAS, range: 470..490, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 475..480, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 475..480, + }, + ), ), }, container_name: None, @@ -93,11 +97,13 @@ kind: VARIANT, range: 204..205, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 204..205, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 204..205, + }, + ), ), }, container_name: Some( @@ -128,11 +134,13 @@ kind: CONST, range: 413..434, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 419..424, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 419..424, + }, + ), ), }, container_name: None, @@ -161,11 +169,13 @@ kind: CONST, range: 593..665, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 599..615, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 599..615, + }, + ), ), }, container_name: None, @@ -196,11 +206,13 @@ kind: ENUM, range: 185..207, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 190..194, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 190..194, + }, + ), ), }, container_name: None, @@ -231,11 +243,13 @@ kind: USE_TREE, range: 727..749, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 736..749, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 736..749, + }, + ), ), }, container_name: None, @@ -266,11 +280,13 @@ kind: MACRO_DEF, range: 153..168, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 159..164, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 159..164, + }, + ), ), }, container_name: None, @@ -299,11 +315,13 @@ kind: STATIC, range: 435..469, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 442..448, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 442..448, + }, + ), ), }, container_name: None, @@ -334,11 +352,13 @@ kind: STRUCT, range: 170..184, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 177..183, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 177..183, + }, + ), ), }, container_name: None, @@ -369,11 +389,13 @@ kind: STRUCT, range: 0..22, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 6..21, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 6..21, + }, + ), ), }, container_name: None, @@ -404,11 +426,13 @@ kind: STRUCT, range: 391..409, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 398..408, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 398..408, + }, + ), ), }, container_name: Some( @@ -441,11 +465,13 @@ kind: STRUCT, range: 628..654, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 635..653, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 635..653, + }, + ), ), }, container_name: Some( @@ -478,11 +504,13 @@ kind: STRUCT, range: 552..580, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 559..579, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 559..579, + }, + ), ), }, container_name: None, @@ -513,11 +541,13 @@ kind: STRUCT, range: 261..279, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 268..275, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 268..275, + }, + ), ), }, container_name: None, @@ -546,11 +576,13 @@ kind: TRAIT, range: 334..373, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 340..345, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 340..345, + }, + ), ), }, container_name: None, @@ -581,11 +613,13 @@ kind: USE_TREE, range: 755..769, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 764..769, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 764..769, + }, + ), ), }, container_name: None, @@ -616,11 +650,13 @@ kind: UNION, range: 208..222, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 214..219, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 214..219, + }, + ), ), }, container_name: None, @@ -649,11 +685,13 @@ kind: MODULE, range: 492..530, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 496..501, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 496..501, + }, + ), ), }, container_name: None, @@ -682,11 +720,13 @@ kind: MODULE, range: 667..677, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 671..676, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 671..676, + }, + ), ), }, container_name: None, @@ -717,11 +757,13 @@ kind: MACRO_RULES, range: 51..131, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 64..77, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 64..77, + }, + ), ), }, container_name: None, @@ -750,11 +792,13 @@ kind: FN, range: 307..330, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 310..325, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 310..325, + }, + ), ), }, container_name: Some( @@ -785,11 +829,13 @@ kind: FN, range: 242..257, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 245..252, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 245..252, + }, + ), ), }, container_name: Some( @@ -822,11 +868,13 @@ kind: MACRO_RULES, range: 1..48, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 14..31, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 14..31, + }, + ), ), }, container_name: None, @@ -855,11 +903,13 @@ kind: FN, range: 375..411, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 378..382, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 378..382, + }, + ), ), }, container_name: None, @@ -869,6 +919,34 @@ do_not_complete: Yes, _marker: PhantomData<&()>, }, + FileSymbol { + name: "ra_test_fixture", + def: Module( + Module { + id: ModuleIdLt { + [salsa id]: Id(3800), + }, + }, + ), + loc: DeclarationLocation { + hir_file_id: FileId( + EditionedFileId( + Id(3000), + ), + ), + ptr: SyntaxNodePtr { + kind: SOURCE_FILE, + range: 0..793, + }, + name_ptr: None, + }, + container_name: None, + is_alias: false, + is_assoc: false, + is_import: false, + do_not_complete: Yes, + _marker: PhantomData<&()>, + }, FileSymbol { name: "really_define_struct", def: Macro( @@ -890,11 +968,13 @@ kind: USE_TREE, range: 684..721, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 701..721, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 701..721, + }, + ), ), }, container_name: None, @@ -923,11 +1003,13 @@ kind: FN, range: 352..371, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 355..363, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 355..363, + }, + ), ), }, container_name: Some( @@ -969,11 +1051,13 @@ kind: STRUCT, range: 508..528, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 515..527, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 515..527, + }, + ), ), }, container_name: None, @@ -1011,11 +1095,13 @@ kind: USE_TREE, range: 141..173, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 157..173, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 157..173, + }, + ), ), }, container_name: None, @@ -1046,11 +1132,13 @@ kind: USE_TREE, range: 141..173, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 157..173, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 157..173, + }, + ), ), }, container_name: None, @@ -1081,11 +1169,13 @@ kind: STRUCT, range: 0..20, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 7..19, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 7..19, + }, + ), ), }, container_name: None, @@ -1116,11 +1206,13 @@ kind: USE_TREE, range: 35..69, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 51..69, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 51..69, + }, + ), ), }, container_name: None, @@ -1151,11 +1243,13 @@ kind: USE_TREE, range: 85..125, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 115..125, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 115..125, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt index 6f5f8f889c7d..87f0c7d9a817 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_exclude_imports.txt @@ -20,11 +20,13 @@ kind: STRUCT, range: 0..15, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 11..14, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 11..14, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt index 5d3fe4d2658d..e96aa889ba06 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt +++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbols_with_imports.txt @@ -20,11 +20,13 @@ kind: STRUCT, range: 0..15, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME, - range: 11..14, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME, + range: 11..14, + }, + ), ), }, container_name: None, @@ -55,11 +57,13 @@ kind: USE_TREE, range: 17..25, }, - name_ptr: AstPtr( - SyntaxNodePtr { - kind: NAME_REF, - range: 22..25, - }, + name_ptr: Some( + AstPtr( + SyntaxNodePtr { + kind: NAME_REF, + range: 22..25, + }, + ), ), }, container_name: None, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index a271cac6fcd0..047df309eca6 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -19,7 +19,7 @@ use ide_db::{ }; use stdx::never; use syntax::{ - AstNode, SyntaxNode, TextRange, + AstNode, AstPtr, SyntaxNode, TextRange, ast::{self, HasName}, }; @@ -253,7 +253,7 @@ impl<'db> TryToNav for FileSymbol<'db> { db, self.loc.hir_file_id, self.loc.ptr.text_range(), - Some(self.loc.name_ptr.text_range()), + self.loc.name_ptr.map(AstPtr::text_range), ) .map(|(FileRange { file_id, range: full_range }, focus_range)| { NavigationTarget { diff --git a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs index 8f6627a60fe6..fe01fb1f1063 100644 --- a/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs +++ b/src/tools/rust-analyzer/crates/mbe/src/expander/matcher.rs @@ -414,8 +414,9 @@ fn match_loop_inner<'t>( } // Check if we need a separator. - if item.sep.is_some() && !item.sep_matched { - let sep = item.sep.as_ref().unwrap(); + if let Some(sep) = &item.sep + && !item.sep_matched + { let mut fork = src.clone(); if expect_separator(&mut fork, sep) { // HACK: here we use `meta_result` to pass `TtIter` back to caller because diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs index eb1b8c5dd0e6..9f3c6742d651 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs @@ -1447,7 +1447,27 @@ foo = { path = "../foo" } .server() .wait_until_workspace_is_loaded(); - server.request::(Default::default(), json!([])); + server.request::( + Default::default(), + json!([ + { + "name": "bar", + "kind": 2, + "location": { + "uri": "file:///[..]bar/src/lib.rs", + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + } + } + }]), + ); let server = Project::with_fixture( r#" @@ -1486,7 +1506,27 @@ version = "0.0.0" .server() .wait_until_workspace_is_loaded(); - server.request::(Default::default(), json!([])); + server.request::( + Default::default(), + json!([ + { + "name": "baz", + "kind": 2, + "location": { + "uri": "file:///[..]baz/src/lib.rs", + "range": { + "start": { + "line": 0, + "character": 0 + }, + "end": { + "line": 0, + "character": 0 + } + } + } + }]), + ); } #[test] diff --git a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs index 34c07598d200..c4979b8e3ae8 100644 --- a/src/tools/rust-analyzer/crates/syntax/src/ptr.rs +++ b/src/tools/rust-analyzer/crates/syntax/src/ptr.rs @@ -68,7 +68,7 @@ impl AstPtr { self.raw } - pub fn text_range(&self) -> TextRange { + pub fn text_range(self) -> TextRange { self.raw.text_range() } From 4e18f1dad2be32a53a43840a39cab9457262d1c1 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jan 2026 14:53:22 +0100 Subject: [PATCH 039/319] Abstract proc-macro-srv input and output away --- .../proc-macro-api/src/legacy_protocol.rs | 4 +- .../proc-macro-api/src/legacy_protocol/msg.rs | 4 +- .../crates/proc-macro-api/src/lib.rs | 41 +++++- .../crates/proc-macro-api/src/process.rs | 138 +++++++++++++----- .../crates/proc-macro-srv-cli/src/main.rs | 12 +- .../proc-macro-srv-cli/src/main_loop.rs | 72 +++++---- 6 files changed, 193 insertions(+), 78 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs index 22a7d9868e21..4524d1b66bfe 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs @@ -162,11 +162,11 @@ fn send_request( req: Request, buf: &mut P::Buf, ) -> Result, ServerError> { - req.write::<_, P>(&mut writer).map_err(|err| ServerError { + req.write::

(&mut writer).map_err(|err| ServerError { message: "failed to write request".into(), io: Some(Arc::new(err)), })?; - let res = Response::read::<_, P>(&mut reader, buf).map_err(|err| ServerError { + let res = Response::read::

(&mut reader, buf).map_err(|err| ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)), })?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index 4146b619ec0c..1b6590693354 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -155,13 +155,13 @@ impl ExpnGlobals { } pub trait Message: serde::Serialize + DeserializeOwned { - fn read(inp: &mut R, buf: &mut C::Buf) -> io::Result> { + fn read(inp: &mut dyn BufRead, buf: &mut C::Buf) -> io::Result> { Ok(match C::read(inp, buf)? { None => None, Some(buf) => Some(C::decode(buf)?), }) } - fn write(self, out: &mut W) -> io::Result<()> { + fn write(self, out: &mut dyn Write) -> io::Result<()> { let value = C::encode(&self)?; C::write(out, &value) } 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 f5fcc99f14a3..98ee6817c2d2 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 @@ -18,7 +18,7 @@ extern crate rustc_driver as _; pub mod bidirectional_protocol; pub mod legacy_protocol; -mod process; +pub mod process; pub mod transport; use paths::{AbsPath, AbsPathBuf}; @@ -44,6 +44,25 @@ pub mod version { pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; } +#[derive(Copy, Clone)] +pub enum ProtocolFormat { + JsonLegacy, + PostcardLegacy, + BidirectionalPostcardPrototype, +} + +impl fmt::Display for ProtocolFormat { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + ProtocolFormat::JsonLegacy => write!(f, "json-legacy"), + ProtocolFormat::PostcardLegacy => write!(f, "postcard-legacy"), + ProtocolFormat::BidirectionalPostcardPrototype => { + write!(f, "bidirectional-postcard-prototype") + } + } + } +} + /// Represents different kinds of procedural macros that can be expanded by the external server. #[derive(Copy, Clone, Eq, PartialEq, Debug, serde_derive::Serialize, serde_derive::Deserialize)] pub enum ProcMacroKind { @@ -132,7 +151,25 @@ impl ProcMacroClient { > + Clone, version: Option<&Version>, ) -> io::Result { - let process = ProcMacroServerProcess::run(process_path, env, version)?; + let process = ProcMacroServerProcess::spawn(process_path, env, version)?; + Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) + } + + /// Invokes `spawn` and returns a client connected to the resulting read and write handles. + /// + /// The `process_path` is used for `Self::server_path`. This function is mainly used for testing. + pub fn with_io_channels( + process_path: &AbsPath, + spawn: impl Fn( + Option, + ) -> io::Result<( + Box, + Box, + Box, + )>, + version: Option<&Version>, + ) -> io::Result { + let process = ProcMacroServerProcess::run(spawn, version, || "".to_owned())?; Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() }) } 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 f6a656e3ce3a..4f8762158790 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 @@ -13,14 +13,13 @@ use span::Span; use stdx::JodChild; use crate::{ - Codec, ProcMacro, ProcMacroKind, ServerError, + Codec, ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, bidirectional_protocol::{self, SubCallback, msg::BidirectionalMessage, reject_subrequests}, legacy_protocol::{self, SpanMode}, version, }; /// Represents a process handling proc-macro communication. -#[derive(Debug)] pub(crate) struct ProcMacroServerProcess { /// The state of the proc-macro server process, the protocol is currently strictly sequential /// hence the lock on the state. @@ -31,6 +30,16 @@ pub(crate) struct ProcMacroServerProcess { exited: OnceLock>, } +impl std::fmt::Debug for ProcMacroServerProcess { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("ProcMacroServerProcess") + .field("version", &self.version) + .field("protocol", &self.protocol) + .field("exited", &self.exited) + .finish() + } +} + #[derive(Debug, Clone)] pub(crate) enum Protocol { LegacyJson { mode: SpanMode }, @@ -38,22 +47,83 @@ pub(crate) enum Protocol { BidirectionalPostcardPrototype { mode: SpanMode }, } +pub trait ProcessExit: Send + Sync { + fn exit_err(&mut self) -> Option; +} + +impl ProcessExit for Process { + fn exit_err(&mut self) -> Option { + match self.child.try_wait() { + Ok(None) | Err(_) => None, + Ok(Some(status)) => { + let mut msg = String::new(); + if !status.success() + && let Some(stderr) = self.child.stderr.as_mut() + { + _ = stderr.read_to_string(&mut msg); + } + Some(ServerError { + message: format!( + "proc-macro server exited with {status}{}{msg}", + if msg.is_empty() { "" } else { ": " } + ), + io: None, + }) + } + } + } +} + /// Maintains the state of the proc-macro server process. -#[derive(Debug)] struct ProcessSrvState { - process: Process, - stdin: ChildStdin, - stdout: BufReader, + process: Box, + stdin: Box, + stdout: Box, } impl ProcMacroServerProcess { /// Starts the proc-macro server and performs a version check - pub(crate) fn run<'a>( + pub(crate) fn spawn<'a>( process_path: &AbsPath, env: impl IntoIterator< Item = (impl AsRef, &'a Option>), > + Clone, version: Option<&Version>, + ) -> io::Result { + Self::run( + |format| { + let mut process = Process::run( + process_path, + env.clone(), + format.map(|format| format.to_string()).as_deref(), + )?; + let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); + + Ok((Box::new(process), Box::new(stdin), Box::new(stdout))) + }, + version, + || { + #[expect(clippy::disallowed_methods)] + Command::new(process_path) + .arg("--version") + .output() + .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) + .unwrap_or_else(|_| "unknown version".to_owned()) + }, + ) + } + + /// Invokes `spawn` and performs a version check. + pub(crate) fn run( + spawn: impl Fn( + Option, + ) -> io::Result<( + Box, + Box, + Box, + )>, + version: Option<&Version>, + binary_server_version: impl Fn() -> String, ) -> io::Result { const VERSION: Version = Version::new(1, 93, 0); // we do `>` for nightly as this started working in the middle of the 1.93 nightly release, so we dont want to break on half of the nightlies @@ -65,27 +135,33 @@ impl ProcMacroServerProcess { && has_working_format_flag { &[ - ( - Some("bidirectional-postcard-prototype"), - Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id }, - ), - (Some("postcard-legacy"), Protocol::LegacyPostcard { mode: SpanMode::Id }), - (Some("json-legacy"), Protocol::LegacyJson { mode: SpanMode::Id }), + Some(ProtocolFormat::BidirectionalPostcardPrototype), + Some(ProtocolFormat::PostcardLegacy), + Some(ProtocolFormat::JsonLegacy), ] } else { - &[(None, Protocol::LegacyJson { mode: SpanMode::Id })] + &[None] }; let mut err = None; - for &(format, ref protocol) in formats { + for &format in formats { let create_srv = || { - let mut process = Process::run(process_path, env.clone(), format)?; - let (stdin, stdout) = process.stdio().expect("couldn't access child stdio"); + let (process, stdin, stdout) = spawn(format)?; io::Result::Ok(ProcMacroServerProcess { state: Mutex::new(ProcessSrvState { process, stdin, stdout }), version: 0, - protocol: protocol.clone(), + protocol: match format { + Some(ProtocolFormat::BidirectionalPostcardPrototype) => { + Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id } + } + Some(ProtocolFormat::PostcardLegacy) => { + Protocol::LegacyPostcard { mode: SpanMode::Id } + } + Some(ProtocolFormat::JsonLegacy) | None => { + Protocol::LegacyJson { mode: SpanMode::Id } + } + }, exited: OnceLock::new(), }) }; @@ -93,12 +169,7 @@ impl ProcMacroServerProcess { tracing::info!("sending proc-macro server version check"); match srv.version_check(Some(&mut reject_subrequests)) { Ok(v) if v > version::CURRENT_API_VERSION => { - #[allow(clippy::disallowed_methods)] - let process_version = Command::new(process_path) - .arg("--version") - .output() - .map(|output| String::from_utf8_lossy(&output.stdout).trim().to_owned()) - .unwrap_or_else(|_| "unknown version".to_owned()); + let process_version = binary_server_version(); err = Some(io::Error::other(format!( "Your installed proc-macro server is too new for your rust-analyzer. API version: {}, server version: {process_version}. \ This will prevent proc-macro expansion from working. Please consider updating your rust-analyzer to ensure compatibility with your current toolchain.", @@ -275,22 +346,9 @@ impl ProcMacroServerProcess { f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| { if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { - match state.process.child.try_wait() { - Ok(None) | Err(_) => e, - Ok(Some(status)) => { - let mut msg = String::new(); - if !status.success() - && let Some(stderr) = state.process.child.stderr.as_mut() - { - _ = stderr.read_to_string(&mut msg); - } - let server_error = ServerError { - message: format!( - "proc-macro server exited with {status}{}{msg}", - if msg.is_empty() { "" } else { ": " } - ), - io: None, - }; + match state.process.exit_err() { + None => e, + Some(server_error) => { self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone() } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index bdfdb50002e1..189a1eea5c19 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -45,7 +45,11 @@ fn main() -> std::io::Result<()> { } let &format = matches.get_one::("format").expect("format value should always be present"); - run(format) + + let mut stdin = std::io::BufReader::new(std::io::stdin()); + let mut stdout = std::io::stdout(); + + run(&mut stdin, &mut stdout, format) } #[derive(Copy, Clone)] @@ -88,7 +92,11 @@ impl ValueEnum for ProtocolFormat { } #[cfg(not(feature = "sysroot-abi"))] -fn run(_: ProtocolFormat) -> std::io::Result<()> { +fn run( + _: &mut std::io::BufReader, + _: &mut std::io::Stdout, + _: ProtocolFormat, +) -> std::io::Result<()> { Err(std::io::Error::new( std::io::ErrorKind::Unsupported, "proc-macro-srv-cli needs to be compiled with the `sysroot-abi` feature to function" diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 22536a4e52b1..0c651d22b41b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -6,7 +6,7 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; -use std::io; +use std::io::{self, BufRead, Write}; use legacy::Message; @@ -32,15 +32,24 @@ impl legacy::SpanTransformer for SpanTrans { } } -pub(crate) fn run(format: ProtocolFormat) -> io::Result<()> { +pub(crate) fn run( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), + format: ProtocolFormat, +) -> io::Result<()> { match format { - ProtocolFormat::JsonLegacy => run_::(), - ProtocolFormat::PostcardLegacy => run_::(), - ProtocolFormat::BidirectionalPostcardPrototype => run_new::(), + ProtocolFormat::JsonLegacy => run_old::(stdin, stdout), + ProtocolFormat::PostcardLegacy => run_old::(stdin, stdout), + ProtocolFormat::BidirectionalPostcardPrototype => { + run_new::(stdin, stdout) + } } } -fn run_new() -> io::Result<()> { +fn run_new( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), +) -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -52,8 +61,6 @@ fn run_new() -> io::Result<()> { } let mut buf = C::Buf::default(); - let mut stdin = io::stdin(); - let mut stdout = io::stdout(); let env_snapshot = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env_snapshot); @@ -61,8 +68,7 @@ fn run_new() -> io::Result<()> { let mut span_mode = legacy::SpanMode::Id; 'outer: loop { - let req_opt = - bidirectional::BidirectionalMessage::read::<_, C>(&mut stdin.lock(), &mut buf)?; + let req_opt = bidirectional::BidirectionalMessage::read::(stdin, &mut buf)?; let Some(req) = req_opt else { break 'outer; }; @@ -77,22 +83,22 @@ fn run_new() -> io::Result<()> { .collect() }); - send_response::(&stdout, bidirectional::Response::ListMacros(res))?; + send_response::(stdout, bidirectional::Response::ListMacros(res))?; } bidirectional::Request::ApiVersionCheck {} => { send_response::( - &stdout, + stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), )?; } bidirectional::Request::SetConfig(config) => { span_mode = config.span_mode; - send_response::(&stdout, bidirectional::Response::SetConfig(config))?; + send_response::(stdout, bidirectional::Response::SetConfig(config))?; } bidirectional::Request::ExpandMacro(task) => { - handle_expand::(&srv, &mut stdin, &mut stdout, &mut buf, span_mode, *task)?; + handle_expand::(&srv, stdin, stdout, &mut buf, span_mode, *task)?; } }, _ => continue, @@ -104,8 +110,8 @@ fn run_new() -> io::Result<()> { fn handle_expand( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdin: &io::Stdin, - stdout: &io::Stdout, + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), buf: &mut C::Buf, span_mode: legacy::SpanMode, task: bidirectional::ExpandMacro, @@ -118,7 +124,7 @@ fn handle_expand( fn handle_expand_id( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdout: &io::Stdout, + stdout: &mut dyn Write, task: bidirectional::ExpandMacro, ) -> io::Result<()> { let bidirectional::ExpandMacro { lib, env, current_dir, data } = task; @@ -157,12 +163,12 @@ fn handle_expand_id( }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(&stdout, bidirectional::Response::ExpandMacro(res)) + send_response::(stdout, bidirectional::Response::ExpandMacro(res)) } struct ProcMacroClientHandle<'a, C: Codec> { - stdin: &'a io::Stdin, - stdout: &'a io::Stdout, + stdin: &'a mut (dyn BufRead + Send + Sync), + stdout: &'a mut (dyn Write + Send + Sync), buf: &'a mut C::Buf, } @@ -173,11 +179,11 @@ impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { ) -> Option { let msg = bidirectional::BidirectionalMessage::SubRequest(req); - if msg.write::<_, C>(&mut self.stdout.lock()).is_err() { + if msg.write::(&mut *self.stdout).is_err() { return None; } - match bidirectional::BidirectionalMessage::read::<_, C>(&mut self.stdin.lock(), self.buf) { + match bidirectional::BidirectionalMessage::read::(&mut *self.stdin, self.buf) { Ok(Some(msg)) => Some(msg), _ => None, } @@ -238,8 +244,8 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl fn handle_expand_ra( srv: &proc_macro_srv::ProcMacroSrv<'_>, - stdin: &io::Stdin, - stdout: &io::Stdout, + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), buf: &mut C::Buf, task: bidirectional::ExpandMacro, ) -> io::Result<()> { @@ -301,10 +307,13 @@ fn handle_expand_ra( .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(&stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response::(stdout, bidirectional::Response::ExpandMacroExtended(res)) } -fn run_() -> io::Result<()> { +fn run_old( + stdin: &mut (dyn BufRead + Send + Sync), + stdout: &mut (dyn Write + Send + Sync), +) -> io::Result<()> { fn macro_kind_to_api(kind: proc_macro_srv::ProcMacroKind) -> proc_macro_api::ProcMacroKind { match kind { proc_macro_srv::ProcMacroKind::CustomDerive => { @@ -316,8 +325,8 @@ fn run_() -> io::Result<()> { } let mut buf = C::Buf::default(); - let mut read_request = || legacy::Request::read::<_, C>(&mut io::stdin().lock(), &mut buf); - let write_response = |msg: legacy::Response| msg.write::<_, C>(&mut io::stdout().lock()); + let mut read_request = || legacy::Request::read::(stdin, &mut buf); + let mut write_response = |msg: legacy::Response| msg.write::(stdout); let env = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env); @@ -446,7 +455,10 @@ fn run_() -> io::Result<()> { Ok(()) } -fn send_response(stdout: &io::Stdout, resp: bidirectional::Response) -> io::Result<()> { +fn send_response( + stdout: &mut dyn Write, + resp: bidirectional::Response, +) -> io::Result<()> { let resp = bidirectional::BidirectionalMessage::Response(resp); - resp.write::<_, C>(&mut stdout.lock()) + resp.write::(stdout) } From 27fef0ccbe56e9e4494d19780cdd7c41660d764d Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sat, 10 Jan 2026 16:01:09 +0100 Subject: [PATCH 040/319] internal: Landing integration test infra for proc-macro-srv-cli --- src/tools/rust-analyzer/Cargo.lock | 6 + .../crates/proc-macro-api/src/lib.rs | 6 +- .../crates/proc-macro-srv-cli/Cargo.toml | 14 ++ .../crates/proc-macro-srv-cli/src/lib.rs | 6 + .../crates/proc-macro-srv-cli/src/main.rs | 43 ++-- .../proc-macro-srv-cli/src/main_loop.rs | 5 +- .../proc-macro-srv-cli/tests/common/utils.rs | 213 +++++++++++++++++ .../proc-macro-srv-cli/tests/legacy_json.rs | 224 ++++++++++++++++++ 8 files changed, 494 insertions(+), 23 deletions(-) create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index 5bdde7c7c3e6..d6c6250e13dc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1879,9 +1879,15 @@ name = "proc-macro-srv-cli" version = "0.0.0" dependencies = [ "clap", + "expect-test", + "intern", + "paths", "postcard", "proc-macro-api", "proc-macro-srv", + "proc-macro-test", + "span", + "tt", ] [[package]] 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 98ee6817c2d2..822809943a36 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 @@ -44,10 +44,14 @@ pub mod version { pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID; } -#[derive(Copy, Clone)] +/// Protocol format for communication between client and server. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum ProtocolFormat { + /// JSON-based legacy protocol (newline-delimited JSON). JsonLegacy, + /// Postcard-based legacy protocol (COBS-encoded postcard). PostcardLegacy, + /// Bidirectional postcard protocol with sub-request support. BidirectionalPostcardPrototype, } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index 6b2db0b269d5..a25e3b64ad42 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -10,12 +10,26 @@ license.workspace = true rust-version.workspace = true publish = false +[lib] +doctest = false + [dependencies] proc-macro-srv.workspace = true proc-macro-api.workspace = true postcard.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} +[dev-dependencies] +expect-test.workspace = true +paths.workspace = true +# span = {workspace = true, default-features = false} does not work +span = { path = "../span", default-features = false} +tt.workspace = true +intern.workspace = true + +# used as proc macro test target +proc-macro-test.path = "../proc-macro-srv/proc-macro-test" + [features] default = [] # default = ["sysroot-abi"] diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs new file mode 100644 index 000000000000..9e6f03bf4604 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs @@ -0,0 +1,6 @@ +//! Library interface for `proc-macro-srv-cli`. +//! +//! This module exposes the server main loop and protocol format for integration testing. + +#[cfg(feature = "sysroot-abi")] +pub mod main_loop; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs index 189a1eea5c19..a246d4d3f28f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs @@ -9,11 +9,11 @@ extern crate rustc_driver as _; mod version; -#[cfg(feature = "sysroot-abi")] -mod main_loop; use clap::{Command, ValueEnum}; +use proc_macro_api::ProtocolFormat; + #[cfg(feature = "sysroot-abi")] -use main_loop::run; +use proc_macro_srv_cli::main_loop::run; fn main() -> std::io::Result<()> { let v = std::env::var("RUST_ANALYZER_INTERNALS_DO_NOT_USE"); @@ -32,7 +32,7 @@ fn main() -> std::io::Result<()> { .long("format") .action(clap::ArgAction::Set) .default_value("json-legacy") - .value_parser(clap::builder::EnumValueParser::::new()), + .value_parser(clap::builder::EnumValueParser::::new()), clap::Arg::new("version") .long("version") .action(clap::ArgAction::SetTrue) @@ -43,33 +43,37 @@ fn main() -> std::io::Result<()> { println!("rust-analyzer-proc-macro-srv {}", version::version()); return Ok(()); } - let &format = - matches.get_one::("format").expect("format value should always be present"); + let &format = matches + .get_one::("format") + .expect("format value should always be present"); let mut stdin = std::io::BufReader::new(std::io::stdin()); let mut stdout = std::io::stdout(); - run(&mut stdin, &mut stdout, format) + run(&mut stdin, &mut stdout, format.into()) } +/// Wrapper for CLI argument parsing that implements `ValueEnum`. #[derive(Copy, Clone)] -enum ProtocolFormat { - JsonLegacy, - PostcardLegacy, - BidirectionalPostcardPrototype, +struct ProtocolFormatArg(ProtocolFormat); + +impl From for ProtocolFormat { + fn from(arg: ProtocolFormatArg) -> Self { + arg.0 + } } -impl ValueEnum for ProtocolFormat { +impl ValueEnum for ProtocolFormatArg { fn value_variants<'a>() -> &'a [Self] { &[ - ProtocolFormat::JsonLegacy, - ProtocolFormat::PostcardLegacy, - ProtocolFormat::BidirectionalPostcardPrototype, + ProtocolFormatArg(ProtocolFormat::JsonLegacy), + ProtocolFormatArg(ProtocolFormat::PostcardLegacy), + ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype), ] } fn to_possible_value(&self) -> Option { - match self { + match self.0 { ProtocolFormat::JsonLegacy => Some(clap::builder::PossibleValue::new("json-legacy")), ProtocolFormat::PostcardLegacy => { Some(clap::builder::PossibleValue::new("postcard-legacy")) @@ -79,12 +83,13 @@ impl ValueEnum for ProtocolFormat { } } } + fn from_str(input: &str, _ignore_case: bool) -> Result { match input { - "json-legacy" => Ok(ProtocolFormat::JsonLegacy), - "postcard-legacy" => Ok(ProtocolFormat::PostcardLegacy), + "json-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::JsonLegacy)), + "postcard-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::PostcardLegacy)), "bidirectional-postcard-prototype" => { - Ok(ProtocolFormat::BidirectionalPostcardPrototype) + Ok(ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype)) } _ => Err(format!("unknown protocol format: {input}")), } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 0c651d22b41b..b927eea46b58 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,6 +1,6 @@ //! The main loop of the proc-macro server. use proc_macro_api::{ - Codec, + Codec, ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, @@ -12,7 +12,6 @@ use legacy::Message; use proc_macro_srv::{EnvSnapshot, SpanId}; -use crate::ProtocolFormat; struct SpanTrans; impl legacy::SpanTransformer for SpanTrans { @@ -32,7 +31,7 @@ impl legacy::SpanTransformer for SpanTrans { } } -pub(crate) fn run( +pub fn run( stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), format: ProtocolFormat, diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs new file mode 100644 index 000000000000..722e92eec7e5 --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -0,0 +1,213 @@ +use std::{ + collections::VecDeque, + io::{self, BufRead, Read, Write}, + sync::{Arc, Condvar, Mutex}, + thread, +}; + +use paths::Utf8PathBuf; +use proc_macro_api::{ + legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap}, + transport::codec::json::JsonProtocol, +}; +use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange}; +use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder}; + +/// Shared state for an in-memory byte channel. +#[derive(Default)] +struct ChannelState { + buffer: VecDeque, + closed: bool, +} + +type InMemoryChannel = Arc<(Mutex, Condvar)>; + +/// Writer end of an in-memory channel. +pub(crate) struct ChannelWriter { + state: InMemoryChannel, +} + +impl Write for ChannelWriter { + fn write(&mut self, buf: &[u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + if state.closed { + return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed")); + } + state.buffer.extend(buf); + cvar.notify_all(); + Ok(buf.len()) + } + + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } +} + +impl Drop for ChannelWriter { + fn drop(&mut self) { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + state.closed = true; + cvar.notify_all(); + } +} + +/// Reader end of an in-memory channel. +pub(crate) struct ChannelReader { + state: InMemoryChannel, + internal_buf: Vec, +} + +impl Read for ChannelReader { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + if state.buffer.is_empty() && state.closed { + return Ok(0); + } + + let to_read = buf.len().min(state.buffer.len()); + for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) { + *dst = src; + } + Ok(to_read) + } +} + +impl BufRead for ChannelReader { + fn fill_buf(&mut self) -> io::Result<&[u8]> { + let (lock, cvar) = &*self.state; + let mut state = lock.lock().unwrap(); + + while state.buffer.is_empty() && !state.closed { + state = cvar.wait(state).unwrap(); + } + + self.internal_buf.clear(); + self.internal_buf.extend(&state.buffer); + Ok(&self.internal_buf) + } + + fn consume(&mut self, amt: usize) { + let (lock, _) = &*self.state; + let mut state = lock.lock().unwrap(); + let to_drain = amt.min(state.buffer.len()); + drop(state.buffer.drain(..to_drain)); + } +} + +/// Creates a connected pair of channels for bidirectional communication. +fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) { + // Channel for client -> server communication + let client_to_server = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + let client_writer = ChannelWriter { state: client_to_server.clone() }; + let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() }; + + // Channel for server -> client communication + let server_to_client = Arc::new(( + Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }), + Condvar::new(), + )); + + let server_writer = ChannelWriter { state: server_to_client.clone() }; + let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() }; + + (client_writer, client_reader, server_writer, server_reader) +} + +pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf { + let path = proc_macro_test::PROC_MACRO_TEST_LOCATION; + if path.is_empty() { + panic!("proc-macro-test dylib not available (requires nightly toolchain)"); + } + path.into() +} + +/// Runs a test with the server in a background thread. +pub(crate) fn with_server(test_fn: F) -> R +where + F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R, +{ + let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) = + create_channel_pair(); + + let server_handle = thread::spawn(move || { + proc_macro_srv_cli::main_loop::run( + &mut server_reader, + &mut server_writer, + proc_macro_api::ProtocolFormat::JsonLegacy, + ) + }); + + let result = test_fn(&mut client_writer, &mut client_reader); + + // Close the client writer to signal the server to stop + drop(client_writer); + + // Wait for server to finish + match server_handle.join() { + Ok(Ok(())) => {} + Ok(Err(e)) => { + // IO error from server is expected when client disconnects + if matches!( + e.kind(), + io::ErrorKind::BrokenPipe + | io::ErrorKind::UnexpectedEof + | io::ErrorKind::InvalidData + ) { + panic!("Server error: {e}"); + } + } + Err(e) => std::panic::resume_unwind(e), + } + + result +} + +/// Sends a request and reads the response using JSON protocol. +pub(crate) fn request( + writer: &mut dyn Write, + reader: &mut dyn BufRead, + request: Request, +) -> Response { + request.write::(writer).expect("failed to write request"); + + let mut buf = String::new(); + Response::read::(reader, &mut buf) + .expect("failed to read response") + .expect("no response received") +} + +/// Creates a simple empty token tree suitable for testing. +pub(crate) fn create_empty_token_tree( + version: u32, + span_data_table: &mut SpanDataIndexMap, +) -> FlatTree { + let anchor = SpanAnchor { + file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT), + ast_id: span::ROOT_ERASED_FILE_AST_ID, + }; + let span = Span { + range: TextRange::empty(0.into()), + anchor, + ctx: SyntaxContext::root(Edition::CURRENT), + }; + + let builder = TopSubtreeBuilder::new(Delimiter { + open: span, + close: span, + kind: DelimiterKind::Invisible, + }); + let tt = builder.build(); + + FlatTree::from_subtree(tt.view(), version, span_data_table) +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs new file mode 100644 index 000000000000..1fa886219a8a --- /dev/null +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs @@ -0,0 +1,224 @@ +//! Integration tests for the proc-macro-srv-cli main loop. +//! +//! These tests exercise the full client-server RPC procedure using in-memory +//! channels without needing to spawn the actual server and client processes. + +#![cfg(feature = "sysroot-abi")] + +mod common { + pub(crate) mod utils; +} + +use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server}; +use expect_test::expect; +use proc_macro_api::{ + legacy_protocol::msg::{ + ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig, + SpanDataIndexMap, SpanMode, + }, + version::CURRENT_API_VERSION, +}; + +#[test] +fn test_version_check() { + with_server(|writer, reader| { + let response = request(writer, reader, Request::ApiVersionCheck {}); + + match response { + Response::ApiVersionCheck(version) => { + assert_eq!(version, CURRENT_API_VERSION); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_list_macros() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + let response = request(writer, reader, Request::ListMacros { dylib_path }); + + let Response::ListMacros(Ok(macros)) = response else { + panic!("expected successful ListMacros response"); + }; + + let mut macro_list: Vec<_> = + macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect(); + macro_list.sort(); + let macro_list_str = macro_list.join("\n"); + + expect![[r#" + DeriveEmpty [CustomDerive] + DeriveError [CustomDerive] + DerivePanic [CustomDerive] + DeriveReemit [CustomDerive] + attr_error [Attr] + attr_noop [Attr] + attr_panic [Attr] + fn_like_clone_tokens [Bang] + fn_like_error [Bang] + fn_like_mk_idents [Bang] + fn_like_mk_literals [Bang] + fn_like_noop [Bang] + fn_like_panic [Bang] + fn_like_span_join [Bang] + fn_like_span_line_column [Bang] + fn_like_span_ops [Bang]"#]] + .assert_eq(¯o_list_str); + }); +} + +#[test] +fn test_list_macros_invalid_path() { + with_server(|writer, reader| { + let response = request( + writer, + reader, + Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() }, + ); + + match response { + Response::ListMacros(Err(e)) => assert!( + e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"), + "{e}" + ), + other => panic!("expected error response, got: {other:?}"), + } + }); +} + +#[test] +fn test_set_config() { + with_server(|writer, reader| { + let config = ServerConfig { span_mode: SpanMode::Id }; + let response = request(writer, reader, Request::SetConfig(config)); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::Id); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_set_config_rust_analyzer_mode() { + with_server(|writer, reader| { + let config = ServerConfig { span_mode: SpanMode::RustAnalyzer }; + let response = request(writer, reader, Request::SetConfig(config)); + + match response { + Response::SetConfig(returned_config) => { + assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_expand_macro_panic() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request(writer, reader, Request::ApiVersionCheck {}); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "fn_like_panic".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, expand_request); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + assert!(msg.contains("fn_like_panic"), "panic message should mention the macro"); + } + Response::ExpandMacro(Ok(_)) => { + panic!("expected panic, but macro succeeded"); + } + other => panic!("unexpected response: {other:?}"), + } + }); +} + +#[test] +fn test_basic_call_flow() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let response1 = request(writer, reader, Request::ApiVersionCheck {}); + assert!(matches!(response1, Response::ApiVersionCheck(_))); + + let response2 = + request(writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id })); + assert!(matches!(response2, Response::SetConfig(_))); + + let response3 = + request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }); + assert!(matches!(response3, Response::ListMacros(Ok(_)))); + }); +} + +#[test] +fn test_expand_nonexistent_macro() { + with_server(|writer, reader| { + let dylib_path = proc_macro_test_dylib_path(); + + let version_response = request(writer, reader, Request::ApiVersionCheck {}); + let Response::ApiVersionCheck(version) = version_response else { + panic!("expected version check response"); + }; + + let mut span_data_table = SpanDataIndexMap::default(); + let macro_body = create_empty_token_tree(version, &mut span_data_table); + + let expand_request = Request::ExpandMacro(Box::new(ExpandMacro { + lib: dylib_path, + env: vec![], + current_dir: None, + data: ExpandMacroData { + macro_body, + macro_name: "NonexistentMacro".to_owned(), + attributes: None, + has_global_spans: ExpnGlobals { + serialize: version >= 3, + def_site: 0, + call_site: 0, + mixed_site: 0, + }, + span_data_table: vec![], + }, + })); + + let response = request(writer, reader, expand_request); + + match response { + Response::ExpandMacro(Err(PanicMessage(msg))) => { + expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg) + } + other => panic!("expected error for nonexistent macro, got: {other:?}"), + } + }); +} From e40bd1cf6ece4bf9a2fb05474a20cd399694be93 Mon Sep 17 00:00:00 2001 From: A4-Tacks Date: Sun, 11 Jan 2026 15:06:38 +0800 Subject: [PATCH 041/319] Add inherit attributes for extract_function assist Example --- ```rust #[cfg(test)] fn foo() { foo($01 + 1$0); } ``` **Before this PR** ```rust #[cfg(test)] fn foo() { foo(fun_name()); } fn $0fun_name() -> i32 { 1 + 1 } ``` **After this PR** ```rust #[cfg(test)] fn foo() { foo(fun_name()); } #[cfg(test)] fn $0fun_name() -> i32 { 1 + 1 } ``` --- .../src/handlers/extract_function.rs | 69 ++++++++++++++++++- 1 file changed, 67 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs index 231df9b5b3e1..294e5f7da8b3 100644 --- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs +++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs @@ -25,7 +25,7 @@ use syntax::{ SyntaxKind::{self, COMMENT}, SyntaxNode, SyntaxToken, T, TextRange, TextSize, TokenAtOffset, WalkEvent, ast::{ - self, AstNode, AstToken, HasGenericParams, HasName, edit::IndentLevel, + self, AstNode, AstToken, HasAttrs, HasGenericParams, HasName, edit::IndentLevel, edit_in_place::Indent, }, match_ast, ted, @@ -375,6 +375,7 @@ struct ContainerInfo<'db> { ret_type: Option>, generic_param_lists: Vec, where_clauses: Vec, + attrs: Vec, edition: Edition, } @@ -911,6 +912,7 @@ impl FunctionBody { let parents = generic_parents(&parent); let generic_param_lists = parents.iter().filter_map(|it| it.generic_param_list()).collect(); let where_clauses = parents.iter().filter_map(|it| it.where_clause()).collect(); + let attrs = parents.iter().flat_map(|it| it.attrs()).filter(is_inherit_attr).collect(); Some(( ContainerInfo { @@ -919,6 +921,7 @@ impl FunctionBody { ret_type: ty, generic_param_lists, where_clauses, + attrs, edition, }, contains_tail_expr, @@ -1103,6 +1106,14 @@ impl GenericParent { GenericParent::Trait(trait_) => trait_.where_clause(), } } + + fn attrs(&self) -> impl Iterator { + match self { + GenericParent::Fn(fn_) => fn_.attrs(), + GenericParent::Impl(impl_) => impl_.attrs(), + GenericParent::Trait(trait_) => trait_.attrs(), + } + } } /// Search `parent`'s ancestors for items with potentially applicable generic parameters @@ -1578,7 +1589,7 @@ fn format_function( let (generic_params, where_clause) = make_generic_params_and_where_clause(ctx, fun); make::fn_( - None, + fun.mods.attrs.clone(), None, fun_name, generic_params, @@ -1958,6 +1969,11 @@ fn format_type(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) ty.display_source_code(ctx.db(), module.into(), true).ok().unwrap_or_else(|| "_".to_owned()) } +fn is_inherit_attr(attr: &ast::Attr) -> bool { + let Some(name) = attr.simple_name() else { return false }; + matches!(name.as_str(), "track_caller" | "cfg") +} + fn make_ty(ty: &hir::Type<'_>, ctx: &AssistContext<'_>, module: hir::Module) -> ast::Type { let ty_str = format_type(ty, ctx, module); make::ty(&ty_str) @@ -6372,6 +6388,55 @@ fn foo() { fn $0fun_name(mut a: i32, mut b: i32) { (a, b) = (b, a); } +"#, + ); + } + + #[test] + fn with_cfg_attr() { + check_assist( + extract_function, + r#" +//- /main.rs crate:main cfg:test +#[cfg(test)] +fn foo() { + foo($01 + 1$0); +} +"#, + r#" +#[cfg(test)] +fn foo() { + foo(fun_name()); +} + +#[cfg(test)] +fn $0fun_name() -> i32 { + 1 + 1 +} +"#, + ); + } + + #[test] + fn with_track_caller() { + check_assist( + extract_function, + r#" +#[track_caller] +fn foo() { + foo($01 + 1$0); +} +"#, + r#" +#[track_caller] +fn foo() { + foo(fun_name()); +} + +#[track_caller] +fn $0fun_name() -> i32 { + 1 + 1 +} "#, ); } From 8150413bf5c1327c0a58b544662b50e5ef4bedc2 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 7 Jan 2026 17:19:33 +0530 Subject: [PATCH 042/319] add byte range subrequest/response --- .../crates/proc-macro-api/src/bidirectional_protocol/msg.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index 0e3b700dcc5a..57e7b1ee8f68 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -1,5 +1,7 @@ //! Bidirectional protocol messages +use std::ops::Range; + use paths::Utf8PathBuf; use serde::{Deserialize, Serialize}; @@ -14,6 +16,7 @@ pub enum SubRequest { SourceText { file_id: u32, ast_id: u32, start: u32, end: u32 }, LocalFilePath { file_id: u32 }, LineColumn { file_id: u32, ast_id: u32, offset: u32 }, + ByteRange { file_id: u32, ast_id: u32, start: u32, end: u32 }, } #[derive(Debug, Serialize, Deserialize)] @@ -32,6 +35,9 @@ pub enum SubResponse { line: u32, column: u32, }, + ByteRangeResult { + range: Range, + }, } #[derive(Debug, Serialize, Deserialize)] From e909b4b28286d914c1d67f12f419cf1ed88daa8d Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 7 Jan 2026 17:20:06 +0530 Subject: [PATCH 043/319] update proc-macro-srv to include byte-range --- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 3 +++ .../proc-macro-srv/src/server_impl/rust_analyzer_span.rs | 5 ++++- .../rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs | 5 +++++ 3 files changed, 12 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index c1ef49a7176b..ac9f89352c2b 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -41,6 +41,7 @@ use std::{ env, ffi::OsString, fs, + ops::Range, path::{Path, PathBuf}, sync::{Arc, Mutex, PoisonError}, thread, @@ -100,6 +101,8 @@ pub trait ProcMacroClientInterface { fn local_file(&mut self, file_id: span::FileId) -> Option; /// Line and column are 1-based. fn line_column(&mut self, span: Span) -> Option<(u32, u32)>; + + fn byte_range(&mut self, span: Span) -> Range; } const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs index 3a25391b573b..9946608247c3 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs @@ -162,7 +162,10 @@ impl server::Span for RaSpanServer<'_> { span } fn byte_range(&mut self, span: Self::Span) -> Range { - // FIXME requires db to resolve the ast id, THIS IS NOT INCREMENTAL + if let Some(cb) = self.callback.as_mut() { + return cb.byte_range(span); + } + Range { start: span.range.start().into(), end: span.range.end().into() } } fn join(&mut self, first: Self::Span, second: Self::Span) -> Option { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs index 81ff1965d68b..b7c5c4fdd21f 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/tests/utils.rs @@ -4,6 +4,7 @@ use expect_test::Expect; use span::{ EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, }; +use std::ops::Range; use crate::{ EnvSnapshot, ProcMacroClientInterface, ProcMacroSrv, SpanId, dylib, proc_macro_test_dylib_path, @@ -137,6 +138,10 @@ impl ProcMacroClientInterface for MockCallback<'_> { // proc_macro uses 1-based line/column Some((line_col.line as u32 + 1, line_col.col as u32 + 1)) } + + fn byte_range(&mut self, span: Span) -> Range { + Range { start: span.range.start().into(), end: span.range.end().into() } + } } pub fn assert_expand_with_callback( From e68a654dca8b43e4f430485249fdef00ed9d85a7 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Wed, 7 Jan 2026 17:20:37 +0530 Subject: [PATCH 044/319] add byte range to main loop and direct the request via callback and define the callback on client side --- .../crates/load-cargo/src/lib.rs | 10 ++++++++++ .../proc-macro-srv-cli/src/main_loop.rs | 19 ++++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) 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 33468a5003c3..904f704221cf 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -591,6 +591,16 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::FilePathResult { name }) } + SubRequest::ByteRange { file_id, ast_id, start, end } => { + let range = resolve_sub_span( + db, + file_id, + ast_id, + TextRange::new(TextSize::from(start), TextSize::from(end)), + ); + + Ok(SubResponse::ByteRangeResult { range: range.range.into() }) + } }; match self.0.expand( subtree.view(), diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 0c651d22b41b..e35f832716dd 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -6,7 +6,7 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; -use std::io::{self, BufRead, Write}; +use std::{io, ops::Range}; use legacy::Message; @@ -240,6 +240,23 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl _ => None, } } + + fn byte_range( + &mut self, + proc_macro_srv::span::Span { range, anchor, ctx: _ }: proc_macro_srv::span::Span, + ) -> Range { + match self.roundtrip(bidirectional::SubRequest::ByteRange { + file_id: anchor.file_id.as_u32(), + ast_id: anchor.ast_id.into_raw(), + start: range.start().into(), + end: range.end().into(), + }) { + Some(bidirectional::BidirectionalMessage::SubResponse( + bidirectional::SubResponse::ByteRangeResult { range }, + )) => range, + _ => Range { start: range.start().into(), end: range.end().into() }, + } + } } fn handle_expand_ra( From 78d243c751e420c38c3a92f707aae78bafc8ad1e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Thu, 8 Jan 2026 22:43:03 +0530 Subject: [PATCH 045/319] add comment on incrementality of subrequest --- src/tools/rust-analyzer/crates/load-cargo/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) 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 904f704221cf..8342492a33a4 100644 --- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs +++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs @@ -553,6 +553,7 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::LocalFilePathResult { name }) } + // Not incremental: requires full file text. SubRequest::SourceText { file_id, ast_id, start, end } => { let range = resolve_sub_span( db, @@ -567,6 +568,7 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::SourceTextResult { text }) } + // Not incremental: requires building line index. SubRequest::LineColumn { file_id, ast_id, offset } => { let range = resolve_sub_span(db, file_id, ast_id, TextRange::empty(TextSize::from(offset))); @@ -591,6 +593,7 @@ impl ProcMacroExpander for Expander { Ok(SubResponse::FilePathResult { name }) } + // Not incremental: requires global span resolution. SubRequest::ByteRange { file_id, ast_id, start, end } => { let range = resolve_sub_span( db, From d30f7c9f7c8c375c2b806b770dd11f3d481979e9 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 15:51:33 +0530 Subject: [PATCH 046/319] add write read imports --- .../rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index e35f832716dd..3beaeb0697ec 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -6,7 +6,10 @@ use proc_macro_api::{ transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, version::CURRENT_API_VERSION, }; -use std::{io, ops::Range}; +use std::{ + io::{self, BufRead, Write}, + ops::Range, +}; use legacy::Message; From 33f0f80c1ac9dd1df22082cce77e7eedbb30b4f1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 21:33:57 +0530 Subject: [PATCH 047/319] add make corresponding constructor methods in SyntaxFactory --- .../src/ast/syntax_factory/constructors.rs | 38 +++++++++++++++++++ 1 file changed, 38 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 7cf9e2bf14f9..5fe419ad4eb7 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 @@ -1578,6 +1578,44 @@ impl SyntaxFactory { pub fn ident(&self, text: &str) -> SyntaxToken { make::tokens::ident(text) } + + pub fn mut_self_param(&self) -> ast::SelfParam { + let ast = make::mut_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 ret_type(&self, ty: ast::Type) -> ast::RetType { + let ast = make::ret_type(ty.clone()).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + builder.map_node(ty.syntax().clone(), ast.ty().unwrap().syntax().clone()); + builder.finish(&mut mapping); + } + ast + } + + pub fn ty_ref(&self, ty: ast::Type, is_mut: bool) -> ast::Type { + let ast = make::ty_ref(ty.clone(), is_mut).clone_for_update(); + + if let Some(mut mapping) = self.mappings() { + let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone()); + match &ast { + ast::Type::RefType(ref_ty) => { + builder.map_node(ty.syntax().clone(), ref_ty.ty().unwrap().syntax().clone()); + } + _ => unreachable!(), + } + builder.finish(&mut mapping); + } + ast + } } // `ext` constructors From 16d74e7b90fc5fe2aaa23377bf51093f0295910b Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 21:34:44 +0530 Subject: [PATCH 048/319] migrate generate_mut_trait_impl to new SyntaxEditor --- .../src/handlers/generate_mut_trait_impl.rs | 191 +++++++++++------- 1 file changed, 121 insertions(+), 70 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 ae1ae24d1ec1..53f6f4883f4d 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,8 +1,8 @@ use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait}; use syntax::{ - AstNode, T, - ast::{self, edit_in_place::Indent, make}, - ted, + AstNode, SyntaxElement, SyntaxNode, T, + ast::{self, edit::AstNodeEdit, edit_in_place::Indent, syntax_factory::SyntaxFactory}, + syntax_editor::{Element, Position, SyntaxEditor}, }; use crate::{AssistContext, AssistId, Assists}; @@ -45,12 +45,13 @@ 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::()?.clone_for_update(); - let indent = impl_def.indent_level(); + let impl_def = ctx.find_node_at_offset::()?; + let indent = Indent::indent_level(&impl_def); let ast::Type::PathType(path) = impl_def.trait_()? else { return None; }; + let trait_name = path.path()?.segment()?.name_ref()?; let scope = ctx.sema.scope(impl_def.trait_()?.syntax())?; @@ -59,75 +60,133 @@ pub(crate) fn generate_mut_trait_impl(acc: &mut Assists, ctx: &AssistContext<'_> let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?; let trait_new = get_trait_mut(&trait_, famous)?; - // Index -> IndexMut - ted::replace(trait_name.syntax(), make::name_ref(trait_new).clone_for_update().syntax()); - - // index -> index_mut - let (trait_method_name, new_trait_method_name) = impl_def - .syntax() - .descendants() - .filter_map(ast::Name::cast) - .find_map(process_method_name)?; - ted::replace( - trait_method_name.syntax(), - make::name(new_trait_method_name).clone_for_update().syntax(), - ); - - if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) { - ted::remove(type_alias.syntax()); - } - - // &self -> &mut self - let mut_self_param = make::mut_self_param(); - let self_param: ast::SelfParam = - impl_def.syntax().descendants().find_map(ast::SelfParam::cast)?; - ted::replace(self_param.syntax(), mut_self_param.clone_for_update().syntax()); - - // &Self::Output -> &mut Self::Output - let ret_type = impl_def.syntax().descendants().find_map(ast::RetType::cast)?; - let new_ret_type = process_ret_type(&ret_type)?; - ted::replace(ret_type.syntax(), make::ret_type(new_ret_type).clone_for_update().syntax()); - - let fn_ = impl_def.assoc_item_list()?.assoc_items().find_map(|it| match it { - ast::AssocItem::Fn(f) => Some(f), - _ => None, - })?; - let _ = process_ref_mut(&fn_); - - let assoc_list = make::assoc_item_list(None).clone_for_update(); - ted::replace(impl_def.assoc_item_list()?.syntax(), assoc_list.syntax()); - impl_def.get_or_create_assoc_item_list().add_item(syntax::ast::AssocItem::Fn(fn_)); - let target = impl_def.syntax().text_range(); + acc.add( AssistId::generate("generate_mut_trait_impl"), format!("Generate `{trait_new}` impl from this `{trait_name}` trait"), target, |edit| { - edit.insert( - target.start(), - if ctx.config.snippet_cap.is_some() { - format!("$0{impl_def}\n\n{indent}") - } else { - format!("{impl_def}\n\n{indent}") - }, + let impl_clone = impl_def.reset_indent().clone_subtree(); + let mut editor = SyntaxEditor::new(impl_clone.syntax().clone()); + let factory = SyntaxFactory::without_mappings(); + + apply_generate_mut_impl(&mut editor, &factory, &impl_clone, trait_new); + + let new_root = editor.finish(); + let new_root = new_root.new_root(); + + let new_impl = ast::Impl::cast(new_root.clone()).unwrap(); + + Indent::indent(&new_impl, indent); + + let mut editor = edit.make_editor(impl_def.syntax()); + editor.insert_all( + Position::before(impl_def.syntax()), + vec![ + new_impl.syntax().syntax_element(), + factory.whitespace(&format!("\n\n{indent}")).syntax_element(), + ], ); + + if let Some(cap) = ctx.config.snippet_cap { + let tabstop_before = edit.make_tabstop_before(cap); + editor.add_annotation(new_impl.syntax(), tabstop_before); + } + + edit.add_file_edits(ctx.vfs_file_id(), editor); }, ) } -fn process_ref_mut(fn_: &ast::Fn) -> Option<()> { - let expr = fn_.body()?.tail_expr()?; - match &expr { - ast::Expr::RefExpr(ref_expr) if ref_expr.mut_token().is_none() => { - ted::insert_all_raw( - ted::Position::after(ref_expr.amp_token()?), - vec![make::token(T![mut]).into(), make::tokens::whitespace(" ").into()], - ); - } - _ => {} +fn delete_with_trivia(editor: &mut SyntaxEditor, node: &SyntaxNode) { + let mut end: SyntaxElement = node.clone().into(); + + if let Some(next) = node.next_sibling_or_token() + && let SyntaxElement::Token(tok) = &next + && tok.kind().is_trivia() + { + end = next.clone(); } - None + + editor.delete_all(node.clone().into()..=end); +} + +fn apply_generate_mut_impl( + editor: &mut SyntaxEditor, + factory: &SyntaxFactory, + impl_def: &ast::Impl, + trait_new: &str, +) -> Option<()> { + let path = + impl_def.trait_().and_then(|t| t.syntax().descendants().find_map(ast::Path::cast))?; + let seg = path.segment()?; + let name_ref = seg.name_ref()?; + + let new_name_ref = factory.name_ref(trait_new); + editor.replace(name_ref.syntax(), new_name_ref.syntax()); + + if let Some((name, new_name)) = + impl_def.syntax().descendants().filter_map(ast::Name::cast).find_map(process_method_name) + { + let new_name_node = factory.name(new_name); + editor.replace(name.syntax(), new_name_node.syntax()); + } + + if let Some(type_alias) = impl_def.syntax().descendants().find_map(ast::TypeAlias::cast) { + delete_with_trivia(editor, type_alias.syntax()); + } + + if let Some(self_param) = impl_def.syntax().descendants().find_map(ast::SelfParam::cast) { + let mut_self = factory.mut_self_param(); + editor.replace(self_param.syntax(), mut_self.syntax()); + } + + if let Some(ret_type) = impl_def.syntax().descendants().find_map(ast::RetType::cast) + && let Some(new_ty) = process_ret_type(factory, &ret_type) + { + let new_ret = factory.ret_type(new_ty); + editor.replace(ret_type.syntax(), new_ret.syntax()) + } + + if let Some(fn_) = impl_def.assoc_item_list().and_then(|l| { + l.assoc_items().find_map(|it| match it { + ast::AssocItem::Fn(f) => Some(f), + _ => None, + }) + }) { + process_ref_mut(editor, factory, &fn_); + } + + Some(()) +} + +fn process_ref_mut(editor: &mut SyntaxEditor, factory: &SyntaxFactory, fn_: &ast::Fn) { + let Some(expr) = fn_.body().and_then(|b| b.tail_expr()) else { return }; + + let ast::Expr::RefExpr(ref_expr) = expr else { return }; + + if ref_expr.mut_token().is_some() { + return; + } + + let Some(amp) = ref_expr.amp_token() else { return }; + + let mut_kw = factory.token(T![mut]); + let space = factory.whitespace(" "); + + editor.insert(Position::after(amp.clone()), space.syntax_element()); + editor.insert(Position::after(amp), mut_kw.syntax_element()); +} + +fn process_ret_type(factory: &SyntaxFactory, ref_ty: &ast::RetType) -> Option { + let ty = ref_ty.ty()?; + let ast::Type::RefType(ref_type) = ty else { + return None; + }; + + let inner = ref_type.ty()?; + Some(factory.ty_ref(inner, true)) } fn get_trait_mut(apply_trait: &hir::Trait, famous: FamousDefs<'_, '_>) -> Option<&'static str> { @@ -158,14 +217,6 @@ fn process_method_name(name: ast::Name) -> Option<(ast::Name, &'static str)> { Some((name, new_name)) } -fn process_ret_type(ref_ty: &ast::RetType) -> Option { - let ty = ref_ty.ty()?; - let ast::Type::RefType(ref_type) = ty else { - return None; - }; - Some(make::ty_ref(ref_type.ty()?, true)) -} - #[cfg(test)] mod tests { use crate::{ From 594ca4b1bc47588a851265d51a5db05ed2f080b2 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 12 Jan 2026 04:26:31 +0000 Subject: [PATCH 049/319] Prepare for merging from rust-lang/rust This updates the rust-version file to 44a5b55557c26353f388400d7da95527256fe260. --- 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 4b08b0884ca8..b53a66c66751 100644 --- a/src/tools/rust-analyzer/rust-version +++ b/src/tools/rust-analyzer/rust-version @@ -1 +1 @@ -548e586795f6b6fe089d8329aa5edbf0f5202646 +44a5b55557c26353f388400d7da95527256fe260 From 80acef153ffa6ecc4c9458b8a1169b10d7d750c7 Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Mon, 12 Jan 2026 04:26:43 +0000 Subject: [PATCH 050/319] Format code --- src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs | 4 +++- src/tools/rust-analyzer/crates/span/src/hygiene.rs | 4 +--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs index d10e122a5deb..172641227599 100644 --- a/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs +++ b/src/tools/rust-analyzer/crates/hir-expand/src/declarative.rs @@ -101,7 +101,9 @@ impl DeclarativeMacroExpander { match &*value { "transparent" => ControlFlow::Break(Transparency::Transparent), // "semitransparent" is for old rustc versions. - "semiopaque" | "semitransparent" => ControlFlow::Break(Transparency::SemiOpaque), + "semiopaque" | "semitransparent" => { + ControlFlow::Break(Transparency::SemiOpaque) + } "opaque" => ControlFlow::Break(Transparency::Opaque), _ => ControlFlow::Continue(()), } diff --git a/src/tools/rust-analyzer/crates/span/src/hygiene.rs b/src/tools/rust-analyzer/crates/span/src/hygiene.rs index 9904c562a740..fe05ef946518 100644 --- a/src/tools/rust-analyzer/crates/span/src/hygiene.rs +++ b/src/tools/rust-analyzer/crates/span/src/hygiene.rs @@ -241,9 +241,7 @@ const _: () = { 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), - ), + opaque_and_semiopaque: opaque_and_semiopaque(zalsa_::FromId::from_id(id)), }, ) } From 294a0afd655f22f98201c5e34e9f3b57e42a537c Mon Sep 17 00:00:00 2001 From: Chayim Refael Friedman Date: Mon, 12 Jan 2026 08:51:42 +0200 Subject: [PATCH 051/319] Disable `unused_variables` and `unused_mut` warnings They suffer from an unacceptable amount of false positives after #21209. Another option to disable them is to include them in `rust-analyzer.diagnostics.disable` by default, but that will mean users could override that. --- .../src/handlers/mutability_errors.rs | 24 ++++++----- .../src/handlers/unused_variables.rs | 42 ++++++++++--------- 2 files changed, 37 insertions(+), 29 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index 2887a32825db..e3cfbdfb515f 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1,3 +1,5 @@ +#![expect(unused, reason = "diagnostics is temporarily disabled due to too many false positives")] + use hir::db::ExpandDatabase; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; @@ -88,16 +90,17 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - Some( - Diagnostic::new_with_syntax_node_ptr( - ctx, - DiagnosticCode::RustcLint("unused_mut"), - "variable does not need to be mutable", - ast, - ) - // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. - .with_fixes(fixes), - ) + // Some( + // Diagnostic::new_with_syntax_node_ptr( + // ctx, + // DiagnosticCode::RustcLint("unused_mut"), + // "variable does not need to be mutable", + // ast, + // ) + // // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. + // .with_fixes(fixes), + // ) + None } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { @@ -105,6 +108,7 @@ pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option Date: Mon, 12 Jan 2026 08:36:32 +0100 Subject: [PATCH 052/319] Create a new `SymbolKind::CrateRoot` --- .../src/completions/extern_crate.rs | 6 +++--- .../crates/ide-completion/src/item.rs | 1 + .../rust-analyzer/crates/ide-db/src/lib.rs | 8 ++++--- .../crates/ide/src/navigation_target.rs | 13 ++++-------- .../crates/ide/src/references.rs | 2 +- .../rust-analyzer/crates/ide/src/runnables.rs | 8 +++---- .../ide/src/syntax_highlighting/highlight.rs | 21 +++++++++---------- .../ide/src/syntax_highlighting/inject.rs | 12 +++++------ .../ide/src/syntax_highlighting/tags.rs | 1 + .../test_data/highlight_asm.html | 8 +++---- .../test_data/highlight_attributes.html | 2 +- .../test_data/highlight_crate_root.html | 16 +++++++------- .../test_data/highlight_default_library.html | 2 +- .../test_data/highlight_deprecated.html | 4 ++-- .../test_data/highlight_doctest.html | 6 +++--- .../test_data/highlight_extern_crate.html | 14 ++++++------- .../test_data/highlight_general.html | 12 +++++------ .../test_data/highlight_injection_2.html | 2 +- .../test_data/highlight_issue_18089.html | 2 +- .../test_data/highlight_keywords_2015.html | 8 +++---- .../test_data/highlight_keywords_2018.html | 8 +++---- .../test_data/highlight_keywords_2021.html | 8 +++---- .../test_data/highlight_keywords_2024.html | 8 +++---- .../test_data/highlight_keywords_macros.html | 4 ++-- .../test_data/highlight_macros.html | 2 +- .../test_data/highlight_strings.html | 2 +- .../test_data/highlight_unsafe.html | 4 ++-- .../crates/rust-analyzer/src/lsp/to_proto.rs | 8 ++++++- 28 files changed, 98 insertions(+), 94 deletions(-) diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs index 71a3e4eb4ed6..91202e8b32fc 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/extern_crate.rs @@ -17,7 +17,7 @@ pub(crate) fn complete_extern_crate(acc: &mut Completions, ctx: &CompletionConte } let mut item = CompletionItem::new( - CompletionItemKind::SymbolKind(SymbolKind::Module), + CompletionItemKind::SymbolKind(SymbolKind::CrateRoot), ctx.source_range(), name.display_no_db(ctx.edition).to_smolstr(), ctx.edition, @@ -48,7 +48,7 @@ mod other_mod {} let completion_list = completion_list_no_kw(case); - assert_eq!("md other_crate_a\n".to_owned(), completion_list); + assert_eq!("cr other_crate_a\n".to_owned(), completion_list); } #[test] @@ -68,6 +68,6 @@ mod other_mod {} let completion_list = completion_list_no_kw(case); - assert_eq!("md other_crate_a\n".to_owned(), completion_list); + assert_eq!("cr other_crate_a\n".to_owned(), completion_list); } } diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs index 71d32da74710..1a9139d8553b 100644 --- a/src/tools/rust-analyzer/crates/ide-completion/src/item.rs +++ b/src/tools/rust-analyzer/crates/ide-completion/src/item.rs @@ -381,6 +381,7 @@ impl CompletionItemKind { SymbolKind::BuiltinAttr => "ba", SymbolKind::Const => "ct", SymbolKind::ConstParam => "cp", + SymbolKind::CrateRoot => "cr", SymbolKind::Derive => "de", SymbolKind::DeriveHelper => "dh", SymbolKind::Enum => "en", diff --git a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs index 413b58bf7980..023b32b36195 100644 --- a/src/tools/rust-analyzer/crates/ide-db/src/lib.rs +++ b/src/tools/rust-analyzer/crates/ide-db/src/lib.rs @@ -65,7 +65,7 @@ use base_db::{ }; use hir::{ FilePositionWrapper, FileRangeWrapper, - db::{DefDatabase, ExpandDatabase}, + db::{DefDatabase, ExpandDatabase, HirDatabase}, }; use triomphe::Arc; @@ -269,6 +269,7 @@ pub enum SymbolKind { BuiltinAttr, Const, ConstParam, + CrateRoot, Derive, DeriveHelper, Enum, @@ -307,14 +308,15 @@ impl From for SymbolKind { } } -impl From for SymbolKind { - fn from(it: hir::ModuleDef) -> Self { +impl SymbolKind { + pub fn from_module_def(db: &dyn HirDatabase, it: hir::ModuleDef) -> Self { match it { hir::ModuleDef::Const(..) => SymbolKind::Const, hir::ModuleDef::Variant(..) => SymbolKind::Variant, hir::ModuleDef::Function(..) => SymbolKind::Function, hir::ModuleDef::Macro(mac) if mac.is_proc_macro() => SymbolKind::ProcMacro, hir::ModuleDef::Macro(..) => SymbolKind::Macro, + hir::ModuleDef::Module(m) if m.is_crate_root(db) => SymbolKind::CrateRoot, hir::ModuleDef::Module(..) => SymbolKind::Module, hir::ModuleDef::Static(..) => SymbolKind::Static, hir::ModuleDef::Adt(hir::Adt::Struct(..)) => SymbolKind::Struct, diff --git a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs index 047df309eca6..185df92e2d39 100644 --- a/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs +++ b/src/tools/rust-analyzer/crates/ide/src/navigation_target.rs @@ -264,7 +264,7 @@ impl<'db> TryToNav for FileSymbol<'db> { .flatten() .map_or_else(|| self.name.clone(), |it| it.symbol().clone()), alias: self.is_alias.then(|| self.name.clone()), - kind: Some(self.def.into()), + kind: Some(SymbolKind::from_module_def(db, self.def)), full_range, focus_range, container_name: self.container_name.clone(), @@ -480,16 +480,11 @@ impl ToNav for hir::Module { ModuleSource::Module(node) => (node.syntax(), node.name()), ModuleSource::BlockExpr(node) => (node.syntax(), None), }; + let kind = if self.is_crate_root(db) { SymbolKind::CrateRoot } else { SymbolKind::Module }; orig_range_with_focus(db, file_id, syntax, focus).map( |(FileRange { file_id, range: full_range }, focus_range)| { - NavigationTarget::from_syntax( - file_id, - name.clone(), - focus_range, - full_range, - SymbolKind::Module, - ) + NavigationTarget::from_syntax(file_id, name.clone(), focus_range, full_range, kind) }, ) } @@ -549,7 +544,7 @@ impl TryToNav for hir::ExternCrateDecl { self.alias_or_name(db).unwrap_or_else(|| self.name(db)).symbol().clone(), focus_range, full_range, - SymbolKind::Module, + SymbolKind::CrateRoot, ); res.docs = self.docs(db).map(Documentation::into_owned); diff --git a/src/tools/rust-analyzer/crates/ide/src/references.rs b/src/tools/rust-analyzer/crates/ide/src/references.rs index 4918fe4ff9a4..5443021988d4 100644 --- a/src/tools/rust-analyzer/crates/ide/src/references.rs +++ b/src/tools/rust-analyzer/crates/ide/src/references.rs @@ -1079,7 +1079,7 @@ use self$0; use self$0; "#, expect![[r#" - _ Module FileId(0) 0..10 + _ CrateRoot FileId(0) 0..10 FileId(0) 4..8 import "#]], diff --git a/src/tools/rust-analyzer/crates/ide/src/runnables.rs b/src/tools/rust-analyzer/crates/ide/src/runnables.rs index 6cec91250351..42efa7142b50 100644 --- a/src/tools/rust-analyzer/crates/ide/src/runnables.rs +++ b/src/tools/rust-analyzer/crates/ide/src/runnables.rs @@ -815,7 +815,7 @@ mod not_a_root { "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..331, name: \"_\", kind: CrateRoot })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 1..13, focus_range: 4..8, name: \"main\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 15..76, focus_range: 42..71, name: \"__cortex_m_rt_main_trampoline\", kind: Function })", "(Bin, NavigationTarget { file_id: FileId(0), full_range: 78..154, focus_range: 113..149, name: \"__cortex_m_rt_main_trampoline_unsafe\", kind: Function })", @@ -1136,7 +1136,7 @@ fn test_foo1() {} "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..51, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..51, name: \"_\", kind: CrateRoot })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 1..50, focus_range: 36..45, name: \"test_foo1\", kind: Function }, Atom(KeyValue { key: \"feature\", value: \"foo\" }))", ] "#]], @@ -1155,7 +1155,7 @@ fn test_foo1() {} "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..73, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..73, name: \"_\", kind: CrateRoot })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 1..72, focus_range: 58..67, name: \"test_foo1\", kind: Function }, All([Atom(KeyValue { key: \"feature\", value: \"foo\" }), Atom(KeyValue { key: \"feature\", value: \"bar\" })]))", ] "#]], @@ -1234,7 +1234,7 @@ generate_main!(); "#, expect![[r#" [ - "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"_\", kind: Module })", + "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 0..345, name: \"_\", kind: CrateRoot })", "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 282..312, focus_range: 286..291, name: \"tests\", kind: Module, description: \"mod tests\" })", "(Test, NavigationTarget { file_id: FileId(0), full_range: 298..307, name: \"foo_test\", kind: Function })", "(TestMod, NavigationTarget { file_id: FileId(0), full_range: 313..323, name: \"tests2\", kind: Module, description: \"mod tests2\" }, true)", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs index 044fd3f5ac9a..3795d3d4146d 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs @@ -399,7 +399,7 @@ fn highlight_name_ref( highlight_def(sema, krate, field_ref.into(), edition, true) } NameRefClass::ExternCrateShorthand { decl, krate: resolved_krate } => { - let mut h = HlTag::Symbol(SymbolKind::Module).into(); + let mut h = HlTag::Symbol(SymbolKind::CrateRoot).into(); if krate.as_ref().is_some_and(|krate| resolved_krate != *krate) { h |= HlMod::Library; @@ -417,7 +417,6 @@ fn highlight_name_ref( if is_deprecated { h |= HlMod::Deprecated; } - h |= HlMod::CrateRoot; h } }; @@ -496,15 +495,15 @@ pub(super) fn highlight_def( } Definition::TupleField(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Field)), None), Definition::Crate(krate) => ( - Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot, + Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into(), Some(krate.attrs(sema.db)), ), Definition::Module(module) => { - let mut h = Highlight::new(HlTag::Symbol(SymbolKind::Module)); - if module.is_crate_root(db) { - h |= HlMod::CrateRoot; - } - + let h = Highlight::new(HlTag::Symbol(if module.is_crate_root(db) { + SymbolKind::CrateRoot + } else { + SymbolKind::Module + })); (h, Some(module.attrs(sema.db))) } Definition::Function(func) => { @@ -662,8 +661,7 @@ pub(super) fn highlight_def( (h, None) } Definition::ExternCrateDecl(extern_crate) => { - let mut highlight = - Highlight::new(HlTag::Symbol(SymbolKind::Module)) | HlMod::CrateRoot; + let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into(); if extern_crate.alias(db).is_none() { highlight |= HlMod::Library; } @@ -805,6 +803,7 @@ fn highlight_name_by_syntax(name: ast::Name) -> Highlight { TYPE_PARAM => SymbolKind::TypeParam, RECORD_FIELD => SymbolKind::Field, MODULE => SymbolKind::Module, + EXTERN_CRATE => SymbolKind::CrateRoot, FN => SymbolKind::Function, CONST => SymbolKind::Const, STATIC => SymbolKind::Static, @@ -835,7 +834,7 @@ fn highlight_name_ref_by_syntax( }; match parent.kind() { - EXTERN_CRATE => HlTag::Symbol(SymbolKind::Module) | HlMod::CrateRoot, + EXTERN_CRATE => HlTag::Symbol(SymbolKind::CrateRoot).into(), METHOD_CALL_EXPR => ast::MethodCallExpr::cast(parent) .and_then(|it| highlight_method_call(sema, krate, &it, is_unsafe_node)) .unwrap_or_else(|| SymbolKind::Method.into()), diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs index 26d2bb5e0288..291333f09cf8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/inject.rs @@ -1,6 +1,6 @@ //! "Recursive" Syntax highlighting for code in doctests and fixtures. -use hir::{EditionedFileId, HirFileId, InFile, Semantics}; +use hir::{EditionedFileId, HirFileId, InFile, Semantics, db::HirDatabase}; use ide_db::{ SymbolKind, defs::Definition, documentation::Documentation, range_mapper::RangeMapper, rust_doc::is_rust_fence, @@ -109,7 +109,7 @@ pub(super) fn doc_comment( .for_each(|(range, def)| { hl.add(HlRange { range, - highlight: module_def_to_hl_tag(def) + highlight: module_def_to_hl_tag(sema.db, def) | HlMod::Documentation | HlMod::Injected | HlMod::IntraDocLink, @@ -200,11 +200,11 @@ pub(super) fn doc_comment( } } -fn module_def_to_hl_tag(def: Definition) -> HlTag { +fn module_def_to_hl_tag(db: &dyn HirDatabase, def: Definition) -> HlTag { let symbol = match def { - Definition::Module(_) | Definition::Crate(_) | Definition::ExternCrateDecl(_) => { - SymbolKind::Module - } + Definition::Crate(_) | Definition::ExternCrateDecl(_) => SymbolKind::CrateRoot, + Definition::Module(m) if m.is_crate_root(db) => SymbolKind::CrateRoot, + Definition::Module(_) => SymbolKind::Module, Definition::Function(_) => SymbolKind::Function, Definition::Adt(hir::Adt::Struct(_)) => SymbolKind::Struct, Definition::Adt(hir::Adt::Enum(_)) => SymbolKind::Enum, diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs index ca3c3e3aaace..0c64d3de1012 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/tags.rs @@ -144,6 +144,7 @@ impl HlTag { SymbolKind::BuiltinAttr => "builtin_attr", SymbolKind::Const => "constant", SymbolKind::ConstParam => "const_param", + SymbolKind::CrateRoot => "crate_root", SymbolKind::Derive => "derive", SymbolKind::DeriveHelper => "derive_helper", SymbolKind::Enum => "enum", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html index 100fdd2155a4..1228849c5bfd 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_asm.html @@ -45,7 +45,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd unsafe { let foo = 1; let mut o = 0; - core::arch::asm!( + core::arch::asm!( "%input = OpLoad _ {0}", concat!("%result = ", "bar", " _ %input"), "OpStore {1} %result", @@ -54,7 +54,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd ); let thread_id: usize; - core::arch::asm!(" + core::arch::asm!(" mov {0}, gs:[0x30] mov {0}, [{0}+0x48] ", out(reg) thread_id, options(pure, readonly, nostack)); @@ -64,7 +64,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd static VirtualFree: usize; const OffPtr: usize; const OffFn: usize; - core::arch::asm!(" + core::arch::asm!(" push {free_type} push {free_size} push {base} @@ -97,7 +97,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd // Ensure thumb mode is set. let rv = (rv as u32) | 1; let msp = msp as u32; - core::arch::asm!( + core::arch::asm!( "mrs {tmp}, CONTROL", "bics {tmp}, {spsel}", "msr CONTROL, {tmp}", diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html index b151ff42fc39..fa7f7b1cbafb 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_attributes.html @@ -43,7 +43,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd

#[allow(dead_code)]
 #[rustfmt::skip]
-#[proc_macros::identity]
+#[proc_macros::identity]
 #[derive(Default)]
 /// This is a doc comment
 // This is a normal comment
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
index a6e6b16bead5..0b32cedca5d8 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_crate_root.html
@@ -41,25 +41,25 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate foo;
-use core::iter;
+
extern crate foo;
+use core::iter;
 
 pub const NINETY_TWO: u8 = 92;
 
-use foo as foooo;
+use foo as foooo;
 
-pub(crate) fn main() {
+pub(crate) fn main() {
     let baz = iter::repeat(92);
 }
 
 mod bar {
-    pub(in super) const FORTY_TWO: u8 = 42;
+    pub(in super) const FORTY_TWO: u8 = 42;
 
     mod baz {
-        use super::super::NINETY_TWO;
-        use crate::foooo::Point;
+        use super::super::NINETY_TWO;
+        use crate::foooo::Point;
 
-        pub(in super::super) const TWENTY_NINE: u8 = 29;
+        pub(in super::super) const TWENTY_NINE: u8 = 29;
     }
 }
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html index 2f4a2004f1de..29f78959a54f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_default_library.html @@ -41,7 +41,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
use core::iter;
+
use core::iter;
 
 fn main() {
     let foo = Some(92);
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
index 41d3dff8ed9e..5287affbfc5c 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_deprecated.html
@@ -42,8 +42,8 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
 
#![deprecated]
-use crate as _;
-extern crate bar;
+use crate as _;
+extern crate bar;
 #[deprecated]
 macro_rules! macro_ {
     () => {};
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
index b5c3df6ee447..ce9ec7431a97 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_doctest.html
@@ -48,9 +48,9 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
 //! Syntactic name ref highlighting testing
 //! ```rust
-//! extern crate self;
-//! extern crate other as otter;
-//! extern crate core;
+//! extern crate self;
+//! extern crate other as otter;
+//! extern crate core;
 //! trait T { type Assoc; }
 //! fn f<Arg>() -> use<Arg> where (): T<Assoc = ()> {}
 //! ```
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
index 3a4518236883..8f7cbddd7ffb 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_extern_crate.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self as this;
-extern crate std;
-extern crate alloc as abc;
-extern crate unresolved as definitely_unresolved;
+
extern crate self as this;
+extern crate std;
+extern crate alloc as abc;
+extern crate unresolved as definitely_unresolved;
 extern crate unresolved as _;
-extern crate test as opt_in_crate;
-extern crate test as _;
-extern crate proc_macro;
+extern crate test as opt_in_crate;
+extern crate test as _;
+extern crate proc_macro;
 
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html index fd652f444ffd..c6dbc435c0e8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_general.html @@ -72,7 +72,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } } -use self::FooCopy::{self as BarCopy}; +use self::FooCopy::{self as BarCopy}; #[derive(Copy)] struct FooCopy { @@ -110,7 +110,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd FOO } -use core::ops::Fn; +use core::ops::Fn; fn baz<F: Fn() -> ()>(f: F) { f() } @@ -184,15 +184,15 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd } fn use_foo_items() { - let bob = foo::Person { + let bob = foo::Person { name: "Bob", - age: foo::consts::NUMBER, + age: foo::consts::NUMBER, }; - let control_flow = foo::identity(foo::ControlFlow::Continue); + let control_flow = foo::identity(foo::ControlFlow::Continue); if control_flow.should_die() { - foo::die!(); + foo::die!(); } } diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html index 5a5d9bd1f909..391a46f706c8 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_injection_2.html @@ -47,7 +47,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd fixture(r#" @@- /main.rs crate:main deps:other_crate fn test() { - let x = other_crate::foo::S::thing(); + let x = other_crate::foo::S::thing(); x; } //^ i128 diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html index b28818e679ff..fccf34083d7f 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_issue_18089.html @@ -45,5 +45,5 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd template!(template); } -#[proc_macros::issue_18089] +#[proc_macros::issue_18089] fn template() {}
\ No newline at end of file diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html index d2a53b2ff9e1..6366cba1bd03 100644 --- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html +++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2015.html @@ -41,12 +41,12 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; } .unresolved_reference { color: #FC5555; text-decoration: wavy underline; } -
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
index d309b4723238..a89e8190832e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2018.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
index d309b4723238..a89e8190832e 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2021.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
index 575c9a6b0aca..aa1500b8f85b 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_2024.html
@@ -41,12 +41,12 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
extern crate self;
+
extern crate self;
 
-use crate;
-use self;
+use crate;
+use self;
 mod __ {
-    use super::*;
+    use super::*;
 }
 
 macro_rules! void {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
index caf66ace7a68..484afd81ead2 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_keywords_macros.html
@@ -41,6 +41,6 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 .invalid_escape_sequence { color: #FC5555; text-decoration: wavy underline; }
 .unresolved_reference    { color: #FC5555; text-decoration: wavy underline; }
 
-
lib2015::void_2015!(try async await gen);
-lib2024::void_2024!(try async await gen);
+
lib2015::void_2015!(try async await gen);
+lib2024::void_2024!(try async await gen);
 
\ No newline at end of file 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 b63d5cedc825..59612634fda3 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,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd .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};
 
 mirror! {
     {
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
index e178782c79c4..4e3822c3d31f 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_strings.html
@@ -165,7 +165,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
     toho!("{}fmt", 0);
     let i: u64 = 3;
     let o: u64;
-    core::arch::asm!(
+    core::arch::asm!(
         "mov {0}, {1}",
         "add {0}, 5",
         out(reg) o,
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
index 93513f5b575d..008987d409ad 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/test_data/highlight_unsafe.html
@@ -91,7 +91,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
 
         // unsafe fn and method calls
         unsafe_fn();
-        self::unsafe_fn();
+        self::unsafe_fn();
         (unsafe_fn as unsafe fn())();
         Struct { field: 0 }.unsafe_method();
 
@@ -120,7 +120,7 @@ pre                 { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
         &EXTERN_STATIC;
         &raw const EXTERN_STATIC;
 
-        core::arch::asm!(
+        core::arch::asm!(
             "push {base}",
             base = const 0
         );
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
index 6f0f57725fc7..e5b983dcbf85 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/lsp/to_proto.rs
@@ -70,6 +70,7 @@ pub(crate) fn symbol_kind(symbol_kind: SymbolKind) -> lsp_types::SymbolKind {
         | SymbolKind::Attribute
         | SymbolKind::Derive
         | SymbolKind::DeriveHelper => lsp_types::SymbolKind::FUNCTION,
+        SymbolKind::CrateRoot => lsp_types::SymbolKind::PACKAGE,
         SymbolKind::Module | SymbolKind::ToolModule => lsp_types::SymbolKind::MODULE,
         SymbolKind::TypeAlias | SymbolKind::TypeParam | SymbolKind::SelfType => {
             lsp_types::SymbolKind::TYPE_PARAMETER
@@ -141,6 +142,7 @@ pub(crate) fn completion_item_kind(
             SymbolKind::Method => lsp_types::CompletionItemKind::METHOD,
             SymbolKind::Const => lsp_types::CompletionItemKind::CONSTANT,
             SymbolKind::ConstParam => lsp_types::CompletionItemKind::TYPE_PARAMETER,
+            SymbolKind::CrateRoot => lsp_types::CompletionItemKind::MODULE,
             SymbolKind::Derive => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::DeriveHelper => lsp_types::CompletionItemKind::FUNCTION,
             SymbolKind::Enum => lsp_types::CompletionItemKind::ENUM,
@@ -803,11 +805,16 @@ fn semantic_token_type_and_modifiers(
 ) -> (lsp_types::SemanticTokenType, semantic_tokens::ModifierSet) {
     use semantic_tokens::{modifiers as mods, types};
 
+    let mut mods = semantic_tokens::ModifierSet::default();
     let ty = match highlight.tag {
         HlTag::Symbol(symbol) => match symbol {
             SymbolKind::Attribute => types::DECORATOR,
             SymbolKind::Derive => types::DERIVE,
             SymbolKind::DeriveHelper => types::DERIVE_HELPER,
+            SymbolKind::CrateRoot => {
+                mods |= mods::CRATE_ROOT;
+                types::NAMESPACE
+            }
             SymbolKind::Module => types::NAMESPACE,
             SymbolKind::Impl => types::TYPE_ALIAS,
             SymbolKind::Field => types::PROPERTY,
@@ -870,7 +877,6 @@ fn semantic_token_type_and_modifiers(
         },
     };
 
-    let mut mods = semantic_tokens::ModifierSet::default();
     for modifier in highlight.mods.iter() {
         let modifier = match modifier {
             HlMod::Associated => mods::ASSOCIATED,

From 5f6d3852ee494e167eaa8128b1d5f81a0f03234d Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Mon, 12 Jan 2026 08:46:20 +0100
Subject: [PATCH 053/319] Fix crate root search in world symbols duplicating
 root entries

---
 .../rust-analyzer/crates/hir/src/symbols.rs   |  15 +--
 .../crates/ide-db/src/items_locator.rs        |   4 +-
 .../crates/ide-db/src/symbol_index.rs         | 102 +++++++++---------
 .../ide-db/src/test_data/test_doc_alias.txt   |  28 -----
 .../test_symbol_index_collection.txt          |  28 -----
 .../ide/src/syntax_highlighting/highlight.rs  |   9 +-
 .../rust-analyzer/tests/slow-tests/main.rs    |   4 +-
 7 files changed, 67 insertions(+), 123 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index f9002f31fd15..4461659f5c4e 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -23,7 +23,7 @@ use intern::Symbol;
 use rustc_hash::FxHashMap;
 use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ToSmolStr, ast::HasName};
 
-use crate::{HasCrate, Module, ModuleDef, Semantics};
+use crate::{Crate, HasCrate, Module, ModuleDef, Semantics};
 
 /// The actual data that is stored in the index. It should be as compact as
 /// possible.
@@ -100,11 +100,6 @@ impl<'a> SymbolCollector<'a> {
         let _p = tracing::info_span!("SymbolCollector::collect", ?module).entered();
         tracing::info!(?module, "SymbolCollector::collect");
 
-        // If this is a crate root module, add a symbol for the crate itself
-        if module.is_crate_root(self.db) {
-            self.push_crate_root(module);
-        }
-
         // The initial work is the root module we're collecting, additional work will
         // be populated as we traverse the module's definitions.
         self.work.push(SymbolCollectorWork { module_id: module.into(), parent: None });
@@ -116,8 +111,7 @@ impl<'a> SymbolCollector<'a> {
 
     /// Push a symbol for a crate's root module.
     /// This allows crate roots to appear in the symbol index for queries like `::` or `::foo`.
-    fn push_crate_root(&mut self, module: Module) {
-        let krate = module.krate(self.db);
+    pub fn push_crate_root(&mut self, krate: Crate) {
         let Some(display_name) = krate.display_name(self.db) else { return };
         let crate_name = display_name.crate_name();
         let canonical_name = display_name.canonical_name();
@@ -131,10 +125,11 @@ impl<'a> SymbolCollector<'a> {
         let ptr = SyntaxNodePtr::new(&syntax_node);
 
         let loc = DeclarationLocation { hir_file_id, ptr, name_ptr: None };
+        let root_module = krate.root_module(self.db);
 
         self.symbols.insert(FileSymbol {
             name: crate_name.symbol().clone(),
-            def: ModuleDef::Module(module),
+            def: ModuleDef::Module(root_module),
             loc,
             container_name: None,
             is_alias: false,
@@ -147,7 +142,7 @@ impl<'a> SymbolCollector<'a> {
         if canonical_name != crate_name.symbol() {
             self.symbols.insert(FileSymbol {
                 name: canonical_name.clone(),
-                def: ModuleDef::Module(module),
+                def: ModuleDef::Module(root_module),
                 loc,
                 container_name: None,
                 is_alias: false,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
index 0d305530d925..af0c69c6856d 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/items_locator.rs
@@ -110,7 +110,7 @@ pub fn items_with_name_in_module(
             local_query
         }
     };
-    local_query.search(&[SymbolIndex::module_symbols(db, module)], |local_candidate| {
+    local_query.search(db, &[SymbolIndex::module_symbols(db, module)], |local_candidate| {
         cb(match local_candidate.def {
             hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
             def => ItemInNs::from(def),
@@ -140,7 +140,7 @@ fn find_items(
 
     // Query the local crate using the symbol index.
     let mut local_results = Vec::new();
-    local_query.search(&symbol_index::crate_symbols(db, krate), |local_candidate| {
+    local_query.search(db, &symbol_index::crate_symbols(db, krate), |local_candidate| {
         let def = match local_candidate.def {
             hir::ModuleDef::Macro(macro_def) => ItemInNs::Macros(macro_def),
             def => ItemInNs::from(def),
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index 05c3f360fa87..c95b541748ec 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -218,15 +218,18 @@ pub fn crate_symbols(db: &dyn HirDatabase, krate: Crate) -> Box<[&SymbolIndex<'_
 // | Editor  | Shortcut |
 // |---------|-----------|
 // | VS Code | Ctrl+T
-pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> {
+pub fn world_symbols(db: &RootDatabase, mut query: Query) -> Vec> {
     let _p = tracing::info_span!("world_symbols", query = ?query.query).entered();
 
-    if query.is_crate_search() {
-        return search_crates(db, &query);
-    }
-
-    // If we have a path filter, resolve it to target modules
-    let indices: Vec<_> = if !query.path_filter.is_empty() {
+    // Search for crates by name (handles "::" and "::foo" queries)
+    let indices: Vec<_> = if query.is_crate_search() {
+        query.only_types = false;
+        query.libs = true;
+        vec![SymbolIndex::extern_prelude_symbols(db)]
+        // If we have a path filter, resolve it to target modules
+    } else if !query.path_filter.is_empty() {
+        query.only_types = false;
+        query.libs = true;
         let target_modules = resolve_path_to_modules(
             db,
             &query.path_filter,
@@ -258,13 +261,17 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> {
         crates
             .par_iter()
             .for_each_with(db.clone(), |snap, &krate| _ = crate_symbols(snap, krate.into()));
-        crates.into_iter().flat_map(|krate| Vec::from(crate_symbols(db, krate.into()))).collect()
+        crates
+            .into_iter()
+            .flat_map(|krate| Vec::from(crate_symbols(db, krate.into())))
+            .chain(std::iter::once(SymbolIndex::extern_prelude_symbols(db)))
+            .collect()
     };
 
     let mut res = vec![];
 
     // Normal search: use FST to match item name
-    query.search::<()>(&indices, |f| {
+    query.search::<()>(db, &indices, |f| {
         res.push(f.clone());
         ControlFlow::Continue(())
     });
@@ -272,39 +279,6 @@ pub fn world_symbols(db: &RootDatabase, query: Query) -> Vec> {
     res
 }
 
-/// Search for crates by name (handles "::" and "::foo" queries)
-fn search_crates<'db>(db: &'db RootDatabase, query: &Query) -> Vec> {
-    let mut res = vec![];
-
-    for krate in Crate::all(db) {
-        let Some(display_name) = krate.display_name(db) else { continue };
-        let crate_name = display_name.crate_name().as_str();
-
-        // If query is empty (sole "::"), return all crates
-        // Otherwise, fuzzy match the crate name
-        let matches = if query.query.is_empty() {
-            true
-        } else {
-            query.mode.check(&query.query, query.case_sensitive, crate_name)
-        };
-
-        if matches {
-            // Get the crate root module's symbol index and find the root module symbol
-            let root_module = krate.root_module(db);
-            let index = SymbolIndex::module_symbols(db, root_module);
-            // Find the module symbol itself (representing the crate)
-            for symbol in index.symbols.iter() {
-                if matches!(symbol.def, hir::ModuleDef::Module(m) if m == root_module) {
-                    res.push(symbol.clone());
-                    break;
-                }
-            }
-        }
-    }
-
-    res
-}
-
 /// Resolve a path filter to the target module(s) it points to.
 /// Returns the modules whose symbol indices should be searched.
 ///
@@ -452,6 +426,33 @@ impl<'db> SymbolIndex<'db> {
 
         module_symbols(db, InternedModuleId::new(db, hir::ModuleId::from(module)))
     }
+
+    /// The symbol index for all extern prelude crates.
+    pub fn extern_prelude_symbols(db: &dyn HirDatabase) -> &SymbolIndex<'_> {
+        #[salsa::tracked(returns(ref))]
+        fn extern_prelude_symbols<'db>(db: &'db dyn HirDatabase) -> SymbolIndex<'db> {
+            let _p = tracing::info_span!("extern_prelude_symbols").entered();
+
+            // We call this without attaching because this runs in parallel, so we need to attach here.
+            hir::attach_db(db, || {
+                let mut collector = SymbolCollector::new(db, false);
+
+                for krate in Crate::all(db) {
+                    if krate
+                        .display_name(db)
+                        .is_none_or(|name| name.canonical_name().as_str() == "build-script-build")
+                    {
+                        continue;
+                    }
+                    collector.push_crate_root(krate);
+                }
+
+                SymbolIndex::new(collector.finish())
+            })
+        }
+
+        extern_prelude_symbols(db)
+    }
 }
 
 impl fmt::Debug for SymbolIndex<'_> {
@@ -555,6 +556,7 @@ impl Query {
     /// Search symbols in the given indices.
     pub(crate) fn search<'db, T>(
         &self,
+        db: &'db RootDatabase,
         indices: &[&'db SymbolIndex<'db>],
         cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow,
     ) -> Option {
@@ -568,7 +570,7 @@ impl Query {
                 for index in indices.iter() {
                     op = op.add(index.map.search(&automaton));
                 }
-                self.search_maps(indices, op.union(), cb)
+                self.search_maps(db, indices, op.union(), cb)
             }
             SearchMode::Fuzzy => {
                 let automaton = fst::automaton::Subsequence::new(&self.lowercased);
@@ -576,7 +578,7 @@ impl Query {
                 for index in indices.iter() {
                     op = op.add(index.map.search(&automaton));
                 }
-                self.search_maps(indices, op.union(), cb)
+                self.search_maps(db, indices, op.union(), cb)
             }
             SearchMode::Prefix => {
                 let automaton = fst::automaton::Str::new(&self.lowercased).starts_with();
@@ -584,13 +586,14 @@ impl Query {
                 for index in indices.iter() {
                     op = op.add(index.map.search(&automaton));
                 }
-                self.search_maps(indices, op.union(), cb)
+                self.search_maps(db, indices, op.union(), cb)
             }
         }
     }
 
     fn search_maps<'db, T>(
         &self,
+        db: &'db RootDatabase,
         indices: &[&'db SymbolIndex<'db>],
         mut stream: fst::map::Union<'_>,
         mut cb: impl FnMut(&'db FileSymbol<'db>) -> ControlFlow,
@@ -598,18 +601,21 @@ impl Query {
         let ignore_underscore_prefixed = !self.query.starts_with("__");
         while let Some((_, indexed_values)) = stream.next() {
             for &IndexedValue { index, value } in indexed_values {
-                let symbol_index = &indices[index];
+                let symbol_index = indices[index];
                 let (start, end) = SymbolIndex::map_value_to_range(value);
 
                 for symbol in &symbol_index.symbols[start..end] {
                     let non_type_for_type_only_query = self.only_types
-                        && !matches!(
+                        && !(matches!(
                             symbol.def,
                             hir::ModuleDef::Adt(..)
                                 | hir::ModuleDef::TypeAlias(..)
                                 | hir::ModuleDef::BuiltinType(..)
                                 | hir::ModuleDef::Trait(..)
-                        );
+                        ) || matches!(
+                            symbol.def,
+                            hir::ModuleDef::Module(module) if module.is_crate_root(db)
+                        ));
                     if non_type_for_type_only_query || !self.matches_assoc_mode(symbol.is_assoc) {
                         continue;
                     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
index 71680699b739..0c28c312f83b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_doc_alias.txt
@@ -154,34 +154,6 @@
                 do_not_complete: Yes,
                 _marker: PhantomData<&()>,
             },
-            FileSymbol {
-                name: "ra_test_fixture",
-                def: Module(
-                    Module {
-                        id: ModuleIdLt {
-                            [salsa id]: Id(3800),
-                        },
-                    },
-                ),
-                loc: DeclarationLocation {
-                    hir_file_id: FileId(
-                        EditionedFileId(
-                            Id(3000),
-                        ),
-                    ),
-                    ptr: SyntaxNodePtr {
-                        kind: SOURCE_FILE,
-                        range: 0..128,
-                    },
-                    name_ptr: None,
-                },
-                container_name: None,
-                is_alias: false,
-                is_assoc: false,
-                is_import: false,
-                do_not_complete: Yes,
-                _marker: PhantomData<&()>,
-            },
             FileSymbol {
                 name: "s1",
                 def: Adt(
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
index 2d62a56fe22d..4b588572d328 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
+++ b/src/tools/rust-analyzer/crates/ide-db/src/test_data/test_symbol_index_collection.txt
@@ -919,34 +919,6 @@
                 do_not_complete: Yes,
                 _marker: PhantomData<&()>,
             },
-            FileSymbol {
-                name: "ra_test_fixture",
-                def: Module(
-                    Module {
-                        id: ModuleIdLt {
-                            [salsa id]: Id(3800),
-                        },
-                    },
-                ),
-                loc: DeclarationLocation {
-                    hir_file_id: FileId(
-                        EditionedFileId(
-                            Id(3000),
-                        ),
-                    ),
-                    ptr: SyntaxNodePtr {
-                        kind: SOURCE_FILE,
-                        range: 0..793,
-                    },
-                    name_ptr: None,
-                },
-                container_name: None,
-                is_alias: false,
-                is_assoc: false,
-                is_import: false,
-                do_not_complete: Yes,
-                _marker: PhantomData<&()>,
-            },
             FileSymbol {
                 name: "really_define_struct",
                 def: Macro(
diff --git a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
index 3795d3d4146d..dcc9a8c0d5f7 100644
--- a/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/syntax_highlighting/highlight.rs
@@ -494,10 +494,9 @@ pub(super) fn highlight_def(
             (Highlight::new(HlTag::Symbol(SymbolKind::Field)), Some(field.attrs(sema.db)))
         }
         Definition::TupleField(_) => (Highlight::new(HlTag::Symbol(SymbolKind::Field)), None),
-        Definition::Crate(krate) => (
-            Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into(),
-            Some(krate.attrs(sema.db)),
-        ),
+        Definition::Crate(krate) => {
+            (Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)), Some(krate.attrs(sema.db)))
+        }
         Definition::Module(module) => {
             let h = Highlight::new(HlTag::Symbol(if module.is_crate_root(db) {
                 SymbolKind::CrateRoot
@@ -661,7 +660,7 @@ pub(super) fn highlight_def(
             (h, None)
         }
         Definition::ExternCrateDecl(extern_crate) => {
-            let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot)).into();
+            let mut highlight = Highlight::new(HlTag::Symbol(SymbolKind::CrateRoot));
             if extern_crate.alias(db).is_none() {
                 highlight |= HlMod::Library;
             }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
index 9f3c6742d651..b4a7b44d165a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/tests/slow-tests/main.rs
@@ -1452,7 +1452,7 @@ foo = { path = "../foo" }
         json!([
         {
           "name": "bar",
-          "kind": 2,
+          "kind": 4,
           "location": {
             "uri": "file:///[..]bar/src/lib.rs",
             "range": {
@@ -1511,7 +1511,7 @@ version = "0.0.0"
         json!([
         {
           "name": "baz",
-          "kind": 2,
+          "kind": 4,
           "location": {
             "uri": "file:///[..]baz/src/lib.rs",
             "range": {

From 55f6901e967dda86ac15cf836777f1b32855feb7 Mon Sep 17 00:00:00 2001
From: Wilfred Hughes 
Date: Mon, 29 Dec 2025 03:23:48 -0800
Subject: [PATCH 054/319] Fix lowering with supertrait predicates

Previously both valid and invalid Rust code could crash r-a due to a
cyclic query during lowering.
---
 .../rust-analyzer/crates/hir-ty/src/lower.rs  | 38 +++++++++++----
 .../crates/hir-ty/src/tests/regression.rs     | 48 +++++++++++++++++++
 2 files changed, 78 insertions(+), 8 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
index 9befca11b3e5..5789bf02a42e 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/lower.rs
@@ -53,7 +53,7 @@ use tracing::debug;
 use triomphe::{Arc, ThinArc};
 
 use crate::{
-    FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind,
+    FnAbi, ImplTraitId, TyLoweringDiagnostic, TyLoweringDiagnosticKind, all_super_traits,
     consteval::intern_const_ref,
     db::{HirDatabase, InternedOpaqueTyId},
     generics::{Generics, generics, trait_self_param_idx},
@@ -1624,11 +1624,16 @@ pub(crate) fn field_types_with_diagnostics_query<'db>(
     (res, create_diagnostics(ctx.diagnostics))
 }
 
+/// Predicates for `param_id` of the form `P: SomeTrait`. If
+/// `assoc_name` is provided, only return predicates referencing traits
+/// that have an associated type of that name.
+///
 /// This query exists only to be used when resolving short-hand associated types
 /// like `T::Item`.
 ///
 /// See the analogous query in rustc and its comment:
 /// 
+///
 /// This is a query mostly to handle cycles somewhat gracefully; e.g. the
 /// following bounds are disallowed: `T: Foo, U: Foo`, but
 /// these are fine: `T: Foo, U: Foo<()>`.
@@ -1652,7 +1657,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
     );
 
     // we have to filter out all other predicates *first*, before attempting to lower them
-    let predicate = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred {
+    let has_relevant_bound = |pred: &_, ctx: &mut TyLoweringContext<'_, '_>| match pred {
         WherePredicate::ForLifetime { target, bound, .. }
         | WherePredicate::TypeBound { target, bound, .. } => {
             let invalid_target = { ctx.lower_ty_only_param(*target) != Some(param_id) };
@@ -1700,11 +1705,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
                         return false;
                     };
 
-                    rustc_type_ir::elaborate::supertrait_def_ids(interner, tr.into()).any(|tr| {
-                        tr.0.trait_items(db).items.iter().any(|(name, item)| {
-                            matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name
-                        })
-                    })
+                    trait_or_supertrait_has_assoc_type(db, tr, assoc_name)
                 }
                 TypeBound::Use(_) | TypeBound::Lifetime(_) | TypeBound::Error => false,
             }
@@ -1717,7 +1718,7 @@ pub(crate) fn generic_predicates_for_param<'db>(
     {
         ctx.store = maybe_parent_generics.store();
         for pred in maybe_parent_generics.where_predicates() {
-            if predicate(pred, &mut ctx) {
+            if has_relevant_bound(pred, &mut ctx) {
                 predicates.extend(
                     ctx.lower_where_predicate(
                         pred,
@@ -1757,6 +1758,27 @@ pub(crate) fn generic_predicates_for_param_cycle_result(
     StoredEarlyBinder::bind(Clauses::empty(DbInterner::new_no_crate(db)).store())
 }
 
+/// Check if this trait or any of its supertraits define an associated
+/// type with the given name.
+fn trait_or_supertrait_has_assoc_type(
+    db: &dyn HirDatabase,
+    tr: TraitId,
+    assoc_name: &Name,
+) -> bool {
+    for trait_id in all_super_traits(db, tr) {
+        if trait_id
+            .trait_items(db)
+            .items
+            .iter()
+            .any(|(name, item)| matches!(item, AssocItemId::TypeAliasId(_)) && name == assoc_name)
+        {
+            return true;
+        }
+    }
+
+    false
+}
+
 #[inline]
 pub(crate) fn type_alias_bounds<'db>(
     db: &'db dyn HirDatabase,
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 df49d7999fee..a04c46f8eabd 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
@@ -2598,3 +2598,51 @@ trait ColumnLike {
     "#,
     );
 }
+
+#[test]
+fn issue_21006_generic_predicates_for_param_supertrait_cycle() {
+    check_no_mismatches(
+        r#"
+trait VCipherSuite {}
+
+trait CipherSuite
+where
+    OprfHash: Hash,
+{
+}
+
+type Bar = ::Hash;
+
+type OprfHash = ::Hash;
+
+impl Foo {
+    fn seal() {}
+}
+        "#,
+    );
+}
+
+#[test]
+fn issue_21006_self_assoc_trait() {
+    check_types(
+        r#"
+trait Baz {
+    fn baz(&self);
+}
+
+trait Foo {
+    type Assoc;
+}
+
+trait Bar: Foo
+where
+    Self::Assoc: Baz,
+{
+    fn bar(v: Self::Assoc) {
+        let _ = v.baz();
+        //  ^ ()
+    }
+}
+        "#,
+    );
+}

From 01ebc285e1300de9f43a02b80a5bc408dab7bb3c Mon Sep 17 00:00:00 2001
From: Alex Butler 
Date: Mon, 12 Jan 2026 12:28:17 +0000
Subject: [PATCH 055/319] smol_str: update changelog 0.3.5

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

diff --git a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md
index b7da6d18a440..4aa25fa13446 100644
--- a/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md
+++ b/src/tools/rust-analyzer/lib/smol_str/CHANGELOG.md
@@ -1,6 +1,6 @@
 # Changelog
 
-## Unreleased
+## 0.3.5 - 2026-01-08
 - Optimise `SmolStr::clone` 4-5x speedup inline, 0.5x heap (slow down).
 
 ## 0.3.4 - 2025-10-23

From c20e6a12269678cf4012b5962ccfb73db18c940f Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 12 Jan 2026 20:26:24 +0800
Subject: [PATCH 056/319] Fix not complete `mut` and `raw` in `&x.foo()`

Example
---
```rust
fn main() {
    let _ = &$0x.foo();
}
```

**Before this PR**

```rust
...
kw loop
kw match
kw return
kw self::
...
```

**After this PR**

```rust
...
kw loop
kw match
kw mut
kw raw
kw return
kw self::
...
```
---
 .../ide-completion/src/context/analysis.rs    | 10 ++++----
 .../ide-completion/src/tests/expression.rs    | 25 ++++++++++++++++++-
 2 files changed, 29 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 65bae5b66e17..0db93b0837cd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -1305,14 +1305,14 @@ fn classify_name_ref<'db>(
 
     let make_path_kind_expr = |expr: ast::Expr| {
         let it = expr.syntax();
+        let prev_token = iter::successors(it.first_token(), |it| it.prev_token())
+            .skip(1)
+            .find(|it| !it.kind().is_trivia());
         let in_block_expr = is_in_block(it);
         let (in_loop_body, innermost_breakable) = is_in_breakable(it).unzip();
         let after_if_expr = is_after_if_expr(it.clone());
-        let ref_expr_parent =
-            path.as_single_name_ref().and_then(|_| it.parent()).and_then(ast::RefExpr::cast);
-        let after_amp = non_trivia_sibling(it.clone().into(), Direction::Prev)
-            .map(|it| it.kind() == SyntaxKind::AMP)
-            .unwrap_or(false);
+        let after_amp = prev_token.as_ref().is_some_and(|it| it.kind() == SyntaxKind::AMP);
+        let ref_expr_parent = prev_token.and_then(|it| it.parent()).and_then(ast::RefExpr::cast);
         let (innermost_ret_ty, self_param) = {
             let find_ret_ty = |it: SyntaxNode| {
                 if let Some(item) = ast::Item::cast(it.clone()) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index 78f003dd210b..ff005a29218b 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -706,7 +706,30 @@ fn completes_after_ref_expr() {
             kw while
             kw while let
         "#]],
-    )
+    );
+    check(
+        r#"fn main() { let _ = &$0x.foo() }"#,
+        expect![[r#"
+            fn main() fn()
+            bt u32     u32
+            kw const
+            kw crate::
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw mut
+            kw raw
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+        "#]],
+    );
 }
 
 #[test]

From 62e777ca76c62d34993dbf576957a6a9e224f034 Mon Sep 17 00:00:00 2001
From: Roberto Aloi 
Date: Mon, 12 Jan 2026 14:11:12 +0100
Subject: [PATCH 057/319] Fix overlapping cfg attributes for
 wasm32-unknown-emscripten target

---
 src/tools/rust-analyzer/crates/stdx/src/process.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/stdx/src/process.rs b/src/tools/rust-analyzer/crates/stdx/src/process.rs
index 2efeed45e44e..7c4ae978b04a 100644
--- a/src/tools/rust-analyzer/crates/stdx/src/process.rs
+++ b/src/tools/rust-analyzer/crates/stdx/src/process.rs
@@ -76,7 +76,7 @@ pub fn spawn_with_streaming_output(
     Ok(Output { status, stdout, stderr })
 }
 
-#[cfg(unix)]
+#[cfg(all(unix, not(target_arch = "wasm32")))]
 mod imp {
     use std::{
         io::{self, prelude::*},

From 2c32c0e8047eb5535f96c3b7e468cfe023568b31 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Tue, 13 Jan 2026 14:56:51 +0200
Subject: [PATCH 058/319] Make `naked_asm!()` always return `!`

As it should.
---
 .../rust-analyzer/crates/hir-ty/src/infer/expr.rs  |  8 ++++++--
 .../crates/hir-ty/src/tests/simple.rs              | 14 ++++++++++++++
 .../crates/test-utils/src/minicore.rs              |  4 ++++
 3 files changed, 24 insertions(+), 2 deletions(-)

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 62339779a562..c57d41cc5f73 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
@@ -8,7 +8,7 @@ use hir_def::{
     expr_store::path::{GenericArgs as HirGenericArgs, Path},
     hir::{
         Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
-        LabelId, Literal, Pat, PatId, Statement, UnaryOp,
+        InlineAsmKind, LabelId, Literal, Pat, PatId, Statement, UnaryOp,
     },
     resolver::ValueNs,
 };
@@ -1037,7 +1037,11 @@ impl<'db> InferenceContext<'_, 'db> {
                     // FIXME: `sym` should report for things that are not functions or statics.
                     AsmOperand::Sym(_) => (),
                 });
-                if diverge { self.types.types.never } else { self.types.types.unit }
+                if diverge || asm.kind == InlineAsmKind::NakedAsm {
+                    self.types.types.never
+                } else {
+                    self.types.types.unit
+                }
             }
         };
         // use a new type variable if we got unknown here
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 6367521841ab..d02e455fc3dc 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
@@ -3983,3 +3983,17 @@ fn foo() {
         "#]],
     );
 }
+
+#[test]
+fn naked_asm_returns_never() {
+    check_no_mismatches(
+        r#"
+//- minicore: asm
+
+#[unsafe(naked)]
+extern "C" fn foo() -> ! {
+    core::arch::naked_asm!("");
+}
+    "#,
+    );
+}
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 01274a9835f4..580a619cf108 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -1880,6 +1880,10 @@ mod arch {
     pub macro global_asm("assembly template", $(operands,)* $(options($(option),*))?) {
         /* compiler built-in */
     }
+    #[rustc_builtin_macro]
+    pub macro naked_asm("assembly template", $(operands,)* $(options($(option),*))?) {
+        /* compiler built-in */
+    }
 }
 // endregion:asm
 

From a2154802699c6de58702bcf4e3211ccb977cd3be Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Tue, 13 Jan 2026 19:00:03 +0800
Subject: [PATCH 059/319] Migrate `unwrap_block` assist to use SyntaxEditor

- Fix invalid match in let-stmt
- Fix multiple statements loses indent

Example
---
```rust
fn main() {
    let value = match rel_path {
        Ok(rel_path) => {$0
            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
            Some((*id, rel_path))
        }
        Err(_) => None,
    };
}
```

**Before this PR**

```rust
fn main() {
    let value = let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    let value = Some((*id, rel_path));
}
```

**After this PR**

```rust
fn main() {
    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
    let value = Some((*id, rel_path));
}
```

---

```rust
fn main() {
    let mut a = {$0
        1;
        2;
        3
    };
}
```

**Before this PR**

```rust
fn main() {
    1;
2;
    let mut a = 3;
}
```

**After this PR**

```rust
fn main() -> i32 {
    1;
    2;
    let mut a = 3;
}
```
---
 .../ide-assists/src/handlers/unwrap_block.rs  | 257 +++++++++---------
 1 file changed, 136 insertions(+), 121 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
index a83f6835ca61..e4f5e3523bd2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/unwrap_block.rs
@@ -1,10 +1,12 @@
 use syntax::{
-    AstNode, SyntaxKind, T, TextRange,
+    AstNode, SyntaxElement, SyntaxKind, SyntaxNode, T,
     ast::{
         self,
         edit::{AstNodeEdit, IndentLevel},
         make,
     },
+    match_ast,
+    syntax_editor::{Element, Position, SyntaxEditor},
 };
 
 use crate::{AssistContext, AssistId, Assists};
@@ -27,123 +29,108 @@ use crate::{AssistContext, AssistId, Assists};
 // }
 // ```
 pub(crate) fn unwrap_block(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
-    let assist_id = AssistId::refactor_rewrite("unwrap_block");
-    let assist_label = "Unwrap block";
     let l_curly_token = ctx.find_token_syntax_at_offset(T!['{'])?;
-    let mut block = ast::BlockExpr::cast(l_curly_token.parent_ancestors().nth(1)?)?;
+    let block = l_curly_token.parent_ancestors().nth(1).and_then(ast::BlockExpr::cast)?;
     let target = block.syntax().text_range();
-    let mut parent = block.syntax().parent()?;
-    if ast::MatchArm::can_cast(parent.kind()) {
-        parent = parent.ancestors().find(|it| ast::MatchExpr::can_cast(it.kind()))?
-    }
+    let mut container = block.syntax().clone();
+    let mut replacement = block.clone();
+    let mut prefer_container = None;
 
-    let kind = parent.kind();
-    if matches!(kind, SyntaxKind::STMT_LIST | SyntaxKind::EXPR_STMT) {
-        acc.add(assist_id, assist_label, target, |builder| {
-            builder.replace(block.syntax().text_range(), update_expr_string(block.to_string()));
-        })
-    } else if matches!(kind, SyntaxKind::LET_STMT) {
-        let parent = ast::LetStmt::cast(parent)?;
-        let pattern = ast::Pat::cast(parent.syntax().first_child()?)?;
-        let ty = parent.ty();
-        let list = block.stmt_list()?;
-        let replaced = match list.syntax().last_child() {
-            Some(last) => {
-                let stmts: Vec = list.statements().collect();
-                let initializer = ast::Expr::cast(last)?;
-                let let_stmt = make::let_stmt(pattern, ty, Some(initializer));
-                if !stmts.is_empty() {
-                    let block = make::block_expr(stmts, None);
-                    format!("{}\n    {}", update_expr_string(block.to_string()), let_stmt)
-                } else {
-                    let_stmt.to_string()
-                }
-            }
-            None => {
-                let empty_tuple = make::ext::expr_unit();
-                make::let_stmt(pattern, ty, Some(empty_tuple)).to_string()
-            }
-        };
-        acc.add(assist_id, assist_label, target, |builder| {
-            builder.replace(parent.syntax().text_range(), replaced);
-        })
-    } else {
-        let parent = ast::Expr::cast(parent)?;
-        match parent.clone() {
-            ast::Expr::ForExpr(_) | ast::Expr::WhileExpr(_) | ast::Expr::LoopExpr(_) => (),
-            ast::Expr::MatchExpr(_) => block = block.dedent(IndentLevel(1)),
-            ast::Expr::IfExpr(if_expr) => {
-                let then_branch = if_expr.then_branch()?;
-                if then_branch == block {
-                    if let Some(ancestor) = if_expr.syntax().parent().and_then(ast::IfExpr::cast) {
-                        // For `else if` blocks
-                        let ancestor_then_branch = ancestor.then_branch()?;
-
-                        return acc.add(assist_id, assist_label, target, |edit| {
-                            let range_to_del_else_if = TextRange::new(
-                                ancestor_then_branch.syntax().text_range().end(),
-                                l_curly_token.text_range().start(),
-                            );
-                            let range_to_del_rest = TextRange::new(
-                                then_branch.syntax().text_range().end(),
-                                if_expr.syntax().text_range().end(),
-                            );
-
-                            edit.delete(range_to_del_rest);
-                            edit.delete(range_to_del_else_if);
-                            edit.replace(
-                                target,
-                                update_expr_string_without_newline(then_branch.to_string()),
-                            );
-                        });
-                    }
-                } else {
-                    return acc.add(assist_id, assist_label, target, |edit| {
-                        let range_to_del = TextRange::new(
-                            then_branch.syntax().text_range().end(),
-                            l_curly_token.text_range().start(),
-                        );
-
-                        edit.delete(range_to_del);
-                        edit.replace(target, update_expr_string_without_newline(block.to_string()));
+    let from_indent = block.indent_level();
+    let into_indent = loop {
+        let parent = container.parent()?;
+        container = match_ast! {
+            match parent {
+                ast::ForExpr(it) => it.syntax().clone(),
+                ast::LoopExpr(it) => it.syntax().clone(),
+                ast::WhileExpr(it) => it.syntax().clone(),
+                ast::MatchArm(it) => it.parent_match().syntax().clone(),
+                ast::LetStmt(it) => {
+                    replacement = wrap_let(&it, replacement);
+                    prefer_container = Some(it.syntax().clone());
+                    it.syntax().clone()
+                },
+                ast::IfExpr(it) => {
+                    prefer_container.get_or_insert_with(|| {
+                        if let Some(else_branch) = it.else_branch()
+                            && *else_branch.syntax() == container
+                        {
+                            else_branch.syntax().clone()
+                        } else {
+                            it.syntax().clone()
+                        }
                     });
-                }
+                    it.syntax().clone()
+                },
+                ast::ExprStmt(it) => it.syntax().clone(),
+                ast::StmtList(it) => break it.indent_level(),
+                _ => return None,
             }
-            _ => return None,
         };
+    };
+    let replacement = replacement.stmt_list()?;
 
-        acc.add(assist_id, assist_label, target, |builder| {
-            builder.replace(parent.syntax().text_range(), update_expr_string(block.to_string()));
-        })
-    }
+    acc.add(AssistId::refactor_rewrite("unwrap_block"), "Unwrap block", target, |builder| {
+        let mut edit = builder.make_editor(block.syntax());
+        let replacement = replacement.dedent(from_indent).indent(into_indent);
+        let container = prefer_container.unwrap_or(container);
+
+        edit.replace_with_many(&container, extract_statements(replacement));
+        delete_else_before(container, &mut edit);
+
+        builder.add_file_edits(ctx.vfs_file_id(), edit);
+    })
 }
 
-fn update_expr_string(expr_string: String) -> String {
-    update_expr_string_with_pat(expr_string, &[' ', '\n'])
+fn delete_else_before(container: SyntaxNode, edit: &mut SyntaxEditor) {
+    let Some(else_token) = container
+        .siblings_with_tokens(syntax::Direction::Prev)
+        .skip(1)
+        .map_while(|it| it.into_token())
+        .find(|it| it.kind() == T![else])
+    else {
+        return;
+    };
+    itertools::chain(else_token.prev_token(), else_token.next_token())
+        .filter(|it| it.kind() == SyntaxKind::WHITESPACE)
+        .for_each(|it| edit.delete(it));
+    let indent = IndentLevel::from_node(&container);
+    let newline = make::tokens::whitespace(&format!("\n{indent}"));
+    edit.replace(else_token, newline);
 }
 
-fn update_expr_string_without_newline(expr_string: String) -> String {
-    update_expr_string_with_pat(expr_string, &[' '])
+fn wrap_let(assign: &ast::LetStmt, replacement: ast::BlockExpr) -> ast::BlockExpr {
+    let try_wrap_assign = || {
+        let initializer = assign.initializer()?.syntax().syntax_element();
+        let replacement = replacement.clone_subtree();
+        let assign = assign.clone_for_update();
+        let tail_expr = replacement.tail_expr()?;
+        let before =
+            assign.syntax().children_with_tokens().take_while(|it| *it != initializer).collect();
+        let after = assign
+            .syntax()
+            .children_with_tokens()
+            .skip_while(|it| *it != initializer)
+            .skip(1)
+            .collect();
+
+        let mut edit = SyntaxEditor::new(replacement.syntax().clone());
+        edit.insert_all(Position::before(tail_expr.syntax()), before);
+        edit.insert_all(Position::after(tail_expr.syntax()), after);
+        ast::BlockExpr::cast(edit.finish().new_root().clone())
+    };
+    try_wrap_assign().unwrap_or(replacement)
 }
 
-fn update_expr_string_with_pat(expr_str: String, whitespace_pat: &[char]) -> String {
-    // Remove leading whitespace, index to remove the leading '{',
-    // then continue to remove leading whitespace.
-    // We cannot assume the `{` is the first character because there are block modifiers
-    // (`unsafe`, `async` etc.).
-    let after_open_brace_index = expr_str.find('{').map_or(0, |it| it + 1);
-    let expr_str = expr_str[after_open_brace_index..].trim_start_matches(whitespace_pat);
-
-    // Remove trailing whitespace, index [..expr_str.len() - 1] to remove the trailing '}',
-    // then continue to remove trailing whitespace.
-    let expr_str = expr_str.trim_end_matches(whitespace_pat);
-    let expr_str = expr_str[..expr_str.len() - 1].trim_end_matches(whitespace_pat);
-
-    expr_str
-        .lines()
-        .map(|line| line.replacen("    ", "", 1)) // Delete indentation
-        .collect::>()
-        .join("\n")
+fn extract_statements(stmt_list: ast::StmtList) -> Vec {
+    let mut elements = stmt_list
+        .syntax()
+        .children_with_tokens()
+        .filter(|it| !matches!(it.kind(), T!['{'] | T!['}']))
+        .skip_while(|it| it.kind() == SyntaxKind::WHITESPACE)
+        .collect::>();
+    while elements.pop_if(|it| it.kind() == SyntaxKind::WHITESPACE).is_some() {}
+    elements
 }
 
 #[cfg(test)]
@@ -593,6 +580,30 @@ fn main() {
         );
     }
 
+    #[test]
+    fn unwrap_match_arm_in_let() {
+        check_assist(
+            unwrap_block,
+            r#"
+fn main() {
+    let value = match rel_path {
+        Ok(rel_path) => {$0
+            let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+            Some((*id, rel_path))
+        }
+        Err(_) => None,
+    };
+}
+"#,
+            r#"
+fn main() {
+    let rel_path = RelativePathBuf::from_path(rel_path).ok()?;
+    let value = Some((*id, rel_path));
+}
+"#,
+        );
+    }
+
     #[test]
     fn simple_if_in_while_bad_cursor_position() {
         check_assist_not_applicable(
@@ -750,19 +761,6 @@ fn main() -> i32 {
         check_assist(
             unwrap_block,
             r#"
-fn main() {
-    let x = {$0};
-}
-"#,
-            r#"
-fn main() {
-    let x = ();
-}
-"#,
-        );
-        check_assist(
-            unwrap_block,
-            r#"
 fn main() {
     let x = {$0
         bar
@@ -784,8 +782,7 @@ fn main() -> i32 {
 "#,
             r#"
 fn main() -> i32 {
-    1;
-    let _ = 2;
+    1; let _ = 2;
 }
 "#,
         );
@@ -795,11 +792,29 @@ fn main() -> i32 {
 fn main() -> i32 {
     let mut a = {$01; 2};
 }
+"#,
+            r#"
+fn main() -> i32 {
+    1; let mut a = 2;
+}
+"#,
+        );
+        check_assist(
+            unwrap_block,
+            r#"
+fn main() -> i32 {
+    let mut a = {$0
+        1;
+        2;
+        3
+    };
+}
 "#,
             r#"
 fn main() -> i32 {
     1;
-    let mut a = 2;
+    2;
+    let mut a = 3;
 }
 "#,
         );

From 8fb704c17510f22700d4a84fc7151f67d9d20a27 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 13 Jan 2026 19:57:37 +0100
Subject: [PATCH 060/319] fix: Hide renamed imports from macros in symbol index

---
 src/tools/rust-analyzer/crates/hir/src/symbols.rs | 15 +++++++++++++--
 1 file changed, 13 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/symbols.rs b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
index 4461659f5c4e..c088f3aa0cc0 100644
--- a/src/tools/rust-analyzer/crates/hir/src/symbols.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/symbols.rs
@@ -5,8 +5,8 @@ use std::marker::PhantomData;
 use base_db::FxIndexSet;
 use either::Either;
 use hir_def::{
-    AdtId, AssocItemId, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId, Lookup, MacroId,
-    ModuleDefId, ModuleId, TraitId,
+    AdtId, AssocItemId, AstIdLoc, Complete, DefWithBodyId, ExternCrateId, HasModule, ImplId,
+    Lookup, MacroId, ModuleDefId, ModuleId, TraitId,
     db::DefDatabase,
     item_scope::{ImportId, ImportOrExternCrate, ImportOrGlob},
     nameres::crate_def_map,
@@ -169,6 +169,7 @@ impl<'a> SymbolCollector<'a> {
 
     fn collect_from_module(&mut self, module_id: ModuleId) {
         let collect_pub_only = self.collect_pub_only;
+        let is_block_module = module_id.is_block_module(self.db);
         let push_decl = |this: &mut Self, def: ModuleDefId, name, vis| {
             if collect_pub_only && vis != Visibility::Public {
                 return;
@@ -240,6 +241,10 @@ impl<'a> SymbolCollector<'a> {
             let source = import_child_source_cache
                 .entry(i.use_)
                 .or_insert_with(|| i.use_.child_source(this.db));
+            if is_block_module && source.file_id.is_macro() {
+                // Macros tend to generate a lot of imports, the user really won't care about them
+                return;
+            }
             let Some(use_tree_src) = source.value.get(i.idx) else { return };
             let rename = use_tree_src.rename().and_then(|rename| rename.name());
             let name_syntax = match rename {
@@ -276,6 +281,12 @@ impl<'a> SymbolCollector<'a> {
                     return;
                 }
                 let loc = i.lookup(this.db);
+                if is_block_module && loc.ast_id().file_id.is_macro() {
+                    // Macros (especially derivves) tend to generate renamed extern crate items,
+                    // the user really won't care about them
+                    return;
+                }
+
                 let source = loc.source(this.db);
                 let rename = source.value.rename().and_then(|rename| rename.name());
 

From 8f953dea3aad273bc5e1450be7a15449ad07d7bb Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 13 Jan 2026 20:30:38 +0100
Subject: [PATCH 061/319] feat: Implement support for `feature(new_range)`

---
 .../crates/hir-def/src/lang_item.rs           |   5 +
 .../crates/hir-expand/src/mod_path.rs         |   4 +
 .../rust-analyzer/crates/hir-ty/src/infer.rs  |  28 ++++-
 .../crates/hir-ty/src/tests/patterns.rs       | 118 +++++++++---------
 .../crates/hir-ty/src/tests/simple.rs         |  41 ++++--
 .../crates/hir/src/source_analyzer.rs         |  80 ++++++++----
 .../crates/intern/src/symbol/symbols.rs       |   6 +
 .../crates/test-utils/src/minicore.rs         |  92 +++++++++-----
 8 files changed, 246 insertions(+), 128 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
index 092ff6e48671..51dd55301f44 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/lang_item.rs
@@ -499,6 +499,11 @@ language_item_table! { LangItems =>
     RangeToInclusive,        sym::RangeToInclusive,    StructId;
     RangeTo,                 sym::RangeTo,             StructId;
 
+    RangeFromCopy,           sym::RangeFromCopy,           StructId;
+    RangeInclusiveCopy,      sym::RangeInclusiveCopy,      StructId;
+    RangeCopy,               sym::RangeCopy,               StructId;
+    RangeToInclusiveCopy,    sym::RangeToInclusiveCopy,    StructId;
+
     String,                  sym::String,              StructId;
     CStr,                    sym::CStr,                StructId;
     Ordering,                sym::Ordering,            EnumId;
diff --git a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
index 1712c28aa8ab..78228cf82e67 100644
--- a/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
+++ b/src/tools/rust-analyzer/crates/hir-expand/src/mod_path.rs
@@ -423,6 +423,10 @@ macro_rules! __known_path {
     (core::ops::RangeTo) => {};
     (core::ops::RangeToInclusive) => {};
     (core::ops::RangeInclusive) => {};
+    (core::range::Range) => {};
+    (core::range::RangeFrom) => {};
+    (core::range::RangeInclusive) => {};
+    (core::range::RangeToInclusive) => {};
     (core::future::Future) => {};
     (core::future::IntoFuture) => {};
     (core::fmt::Debug) => {};
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 d527a4ae29c2..35d744e7d16b 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer.rs
@@ -1815,18 +1815,34 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
         Some(struct_.into())
     }
 
+    fn has_new_range_feature(&self) -> bool {
+        self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range)
+    }
+
     fn resolve_range(&self) -> Option {
-        let struct_ = self.lang_items.Range?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeCopy?
+        } else {
+            self.lang_items.Range?
+        };
         Some(struct_.into())
     }
 
     fn resolve_range_inclusive(&self) -> Option {
-        let struct_ = self.lang_items.RangeInclusiveStruct?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeInclusiveCopy?
+        } else {
+            self.lang_items.RangeInclusiveStruct?
+        };
         Some(struct_.into())
     }
 
     fn resolve_range_from(&self) -> Option {
-        let struct_ = self.lang_items.RangeFrom?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeFromCopy?
+        } else {
+            self.lang_items.RangeFrom?
+        };
         Some(struct_.into())
     }
 
@@ -1836,7 +1852,11 @@ impl<'body, 'db> InferenceContext<'body, 'db> {
     }
 
     fn resolve_range_to_inclusive(&self) -> Option {
-        let struct_ = self.lang_items.RangeToInclusive?;
+        let struct_ = if self.has_new_range_feature() {
+            self.lang_items.RangeToInclusiveCopy?
+        } else {
+            self.lang_items.RangeToInclusive?
+        };
         Some(struct_.into())
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
index 0b776938c5b3..8c7d29f99371 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/patterns.rs
@@ -13,11 +13,11 @@ fn infer_pattern() {
             let a = z;
             let (c, d) = (1, "hello");
 
-            for (e, f) in some_iter {
+            for (e, f) in [(0, 1)] {
                 let g = e;
             }
 
-            if let [val] = opt {
+            if let [val] = [y] {
                 let h = val;
             }
 
@@ -33,7 +33,7 @@ fn infer_pattern() {
         "#,
         expect![[r#"
             8..9 'x': &'? i32
-            17..400 '{     ...o_x; }': ()
+            17..399 '{     ...o_x; }': ()
             27..28 'y': &'? i32
             31..32 'x': &'? i32
             42..44 '&z': &'? i32
@@ -47,58 +47,62 @@ fn infer_pattern() {
             82..94 '(1, "hello")': (i32, &'? str)
             83..84 '1': i32
             86..93 '"hello"': &'static str
-            101..151 'for (e...     }': fn into_iter<{unknown}>({unknown}) -> <{unknown} as IntoIterator>::IntoIter
-            101..151 'for (e...     }': <{unknown} as IntoIterator>::IntoIter
-            101..151 'for (e...     }': !
-            101..151 'for (e...     }': {unknown}
-            101..151 'for (e...     }': &'? mut {unknown}
-            101..151 'for (e...     }': fn next<{unknown}>(&'? mut {unknown}) -> Option<<{unknown} as Iterator>::Item>
-            101..151 'for (e...     }': Option<<{unknown} as Iterator>::Item>
-            101..151 'for (e...     }': ()
-            101..151 'for (e...     }': ()
-            101..151 'for (e...     }': ()
-            101..151 'for (e...     }': ()
-            105..111 '(e, f)': ({unknown}, {unknown})
-            106..107 'e': {unknown}
-            109..110 'f': {unknown}
-            115..124 'some_iter': {unknown}
-            125..151 '{     ...     }': ()
-            139..140 'g': {unknown}
-            143..144 'e': {unknown}
-            157..204 'if let...     }': ()
-            160..175 'let [val] = opt': bool
-            164..169 '[val]': [{unknown}]
-            165..168 'val': {unknown}
-            172..175 'opt': [{unknown}]
-            176..204 '{     ...     }': ()
-            190..191 'h': {unknown}
-            194..197 'val': {unknown}
-            210..236 'if let...rue {}': ()
-            213..233 'let x ... &true': bool
-            217..225 'x @ true': &'? bool
-            221..225 'true': bool
-            221..225 'true': bool
-            228..233 '&true': &'? bool
-            229..233 'true': bool
-            234..236 '{}': ()
-            246..252 'lambda': impl Fn(u64, u64, i32) -> i32
-            255..287 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
-            256..257 'a': u64
-            264..265 'b': u64
-            267..268 'c': i32
-            275..287 '{ a + b; c }': i32
-            277..278 'a': u64
-            277..282 'a + b': u64
-            281..282 'b': u64
-            284..285 'c': i32
-            298..310 'ref ref_to_x': &'? &'? i32
-            313..314 'x': &'? i32
-            324..333 'mut mut_x': &'? i32
-            336..337 'x': &'? i32
-            347..367 'ref mu...f_to_x': &'? mut &'? i32
-            370..371 'x': &'? i32
-            381..382 'k': &'? mut &'? i32
-            385..397 'mut_ref_to_x': &'? mut &'? i32
+            101..150 'for (e...     }': fn into_iter<[(i32, i32); 1]>([(i32, i32); 1]) -> <[(i32, i32); 1] as IntoIterator>::IntoIter
+            101..150 'for (e...     }': IntoIter<(i32, i32), 1>
+            101..150 'for (e...     }': !
+            101..150 'for (e...     }': IntoIter<(i32, i32), 1>
+            101..150 'for (e...     }': &'? mut IntoIter<(i32, i32), 1>
+            101..150 'for (e...     }': fn next>(&'? mut IntoIter<(i32, i32), 1>) -> Option< as Iterator>::Item>
+            101..150 'for (e...     }': Option<(i32, i32)>
+            101..150 'for (e...     }': ()
+            101..150 'for (e...     }': ()
+            101..150 'for (e...     }': ()
+            101..150 'for (e...     }': ()
+            105..111 '(e, f)': (i32, i32)
+            106..107 'e': i32
+            109..110 'f': i32
+            115..123 '[(0, 1)]': [(i32, i32); 1]
+            116..122 '(0, 1)': (i32, i32)
+            117..118 '0': i32
+            120..121 '1': i32
+            124..150 '{     ...     }': ()
+            138..139 'g': i32
+            142..143 'e': i32
+            156..203 'if let...     }': ()
+            159..174 'let [val] = [y]': bool
+            163..168 '[val]': [&'? i32; 1]
+            164..167 'val': &'? i32
+            171..174 '[y]': [&'? i32; 1]
+            172..173 'y': &'? i32
+            175..203 '{     ...     }': ()
+            189..190 'h': &'? i32
+            193..196 'val': &'? i32
+            209..235 'if let...rue {}': ()
+            212..232 'let x ... &true': bool
+            216..224 'x @ true': &'? bool
+            220..224 'true': bool
+            220..224 'true': bool
+            227..232 '&true': &'? bool
+            228..232 'true': bool
+            233..235 '{}': ()
+            245..251 'lambda': impl Fn(u64, u64, i32) -> i32
+            254..286 '|a: u6...b; c }': impl Fn(u64, u64, i32) -> i32
+            255..256 'a': u64
+            263..264 'b': u64
+            266..267 'c': i32
+            274..286 '{ a + b; c }': i32
+            276..277 'a': u64
+            276..281 'a + b': u64
+            280..281 'b': u64
+            283..284 'c': i32
+            297..309 'ref ref_to_x': &'? &'? i32
+            312..313 'x': &'? i32
+            323..332 'mut mut_x': &'? i32
+            335..336 'x': &'? i32
+            346..366 'ref mu...f_to_x': &'? mut &'? i32
+            369..370 'x': &'? i32
+            380..381 'k': &'? mut &'? i32
+            384..396 'mut_ref_to_x': &'? mut &'? i32
         "#]],
     );
 }
@@ -380,7 +384,7 @@ fn infer_pattern_match_string_literal() {
 fn infer_pattern_match_byte_string_literal() {
     check_infer_with_mismatches(
         r#"
-        //- minicore: index
+        //- minicore: index, range
         struct S;
         impl core::ops::Index for [T; N] {
             type Output = [u8];
@@ -395,7 +399,7 @@ fn infer_pattern_match_byte_string_literal() {
         "#,
         expect![[r#"
             105..109 'self': &'? [T; N]
-            111..116 'index': {unknown}
+            111..116 'index': RangeFull
             157..180 '{     ...     }': &'? [u8]
             167..174 'loop {}': !
             172..174 '{}': ()
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 a9a5e96f75cc..988d1dc7f242 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
@@ -64,20 +64,37 @@ fn type_alias_in_struct_lit() {
 
 #[test]
 fn infer_ranges() {
-    check_types(
+    check_no_mismatches(
         r#"
-//- minicore: range
-fn test() {
-    let a = ..;
-    let b = 1..;
-    let c = ..2u32;
-    let d = 1..2usize;
-    let e = ..=10;
-    let f = 'a'..='z';
+//- minicore: range, new_range
 
-    let t = (a, b, c, d, e, f);
-    t;
-} //^ (RangeFull, RangeFrom, RangeTo, Range, RangeToInclusive, RangeInclusive)
+fn test() {
+    let _: core::ops::RangeFull = ..;
+    let _: core::ops::RangeFrom = 1..;
+    let _: core::ops::RangeTo = ..2u32;
+    let _: core::ops::Range = 1..2usize;
+    let _: core::ops::RangeToInclusive = ..=10;
+    let _: core::ops::RangeInclusive = 'a'..='z';
+}
+"#,
+    );
+}
+
+#[test]
+fn infer_ranges_new_range() {
+    check_no_mismatches(
+        r#"
+//- minicore: range, new_range
+#![feature(new_range)]
+
+fn test() {
+    let _: core::ops::RangeFull = ..;
+    let _: core::range::RangeFrom = 1..;
+    let _: core::ops::RangeTo = ..2u32;
+    let _: core::range::Range = 1..2usize;
+    let _: core::range::RangeToInclusive = ..=10;
+    let _: core::range::RangeInclusive = 'a'..='z';
+}
 "#,
     );
 }
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 6ba7a42c1946..38e9c5b3f714 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -531,18 +531,12 @@ impl<'db> SourceAnalyzer<'db> {
         db: &'db dyn HirDatabase,
         range_pat: &ast::RangePat,
     ) -> Option {
-        let path: ModPath = match (range_pat.op_kind()?, range_pat.start(), range_pat.end()) {
-            (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
-            (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
-            (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
-            (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
-            (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
-
-            (RangeOp::Exclusive, None, None) => return None,
-            (RangeOp::Inclusive, None, None) => return None,
-            (RangeOp::Inclusive, Some(_), None) => return None,
-        };
-        self.resolver.resolve_known_struct(db, &path)
+        self.resolve_range_struct(
+            db,
+            range_pat.op_kind()?,
+            range_pat.start().is_some(),
+            range_pat.end().is_some(),
+        )
     }
 
     pub(crate) fn resolve_range_expr(
@@ -550,19 +544,59 @@ impl<'db> SourceAnalyzer<'db> {
         db: &'db dyn HirDatabase,
         range_expr: &ast::RangeExpr,
     ) -> Option {
-        let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) {
-            (RangeOp::Exclusive, None, None) => path![core::ops::RangeFull],
-            (RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
-            (RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
-            (RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
-            (RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
-            (RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
+        self.resolve_range_struct(
+            db,
+            range_expr.op_kind()?,
+            range_expr.start().is_some(),
+            range_expr.end().is_some(),
+        )
+    }
 
+    fn resolve_range_struct(
+        &self,
+        db: &'db dyn HirDatabase,
+        op_kind: RangeOp,
+        has_start: bool,
+        has_end: bool,
+    ) -> Option {
+        let has_new_range =
+            self.resolver.top_level_def_map().is_unstable_feature_enabled(&sym::new_range);
+        let lang_items = self.lang_items(db);
+        match (op_kind, has_start, has_end) {
+            (RangeOp::Exclusive, false, false) => lang_items.RangeFull,
+            (RangeOp::Exclusive, false, true) => lang_items.RangeTo,
+            (RangeOp::Exclusive, true, false) => {
+                if has_new_range {
+                    lang_items.RangeFromCopy
+                } else {
+                    lang_items.RangeFrom
+                }
+            }
+            (RangeOp::Exclusive, true, true) => {
+                if has_new_range {
+                    lang_items.RangeCopy
+                } else {
+                    lang_items.Range
+                }
+            }
+            (RangeOp::Inclusive, false, true) => {
+                if has_new_range {
+                    lang_items.RangeToInclusiveCopy
+                } else {
+                    lang_items.RangeToInclusive
+                }
+            }
+            (RangeOp::Inclusive, true, true) => {
+                if has_new_range {
+                    lang_items.RangeInclusiveCopy
+                } else {
+                    lang_items.RangeInclusiveStruct
+                }
+            }
             // [E0586] inclusive ranges must be bounded at the end
-            (RangeOp::Inclusive, None, None) => return None,
-            (RangeOp::Inclusive, Some(_), None) => return None,
-        };
-        self.resolver.resolve_known_struct(db, &path)
+            (RangeOp::Inclusive, false, false) => None,
+            (RangeOp::Inclusive, true, false) => None,
+        }
     }
 
     pub(crate) fn resolve_await_to_poll(
diff --git a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
index 781e58a1204e..513f51faba7f 100644
--- a/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
+++ b/src/tools/rust-analyzer/crates/intern/src/symbol/symbols.rs
@@ -525,6 +525,12 @@ define_symbols! {
     arbitrary_self_types,
     arbitrary_self_types_pointers,
     supertrait_item_shadowing,
+    new_range,
+    range,
+    RangeCopy,
+    RangeFromCopy,
+    RangeInclusiveCopy,
+    RangeToInclusiveCopy,
     hash,
     partial_cmp,
     cmp,
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 c3429356d9e5..26e125f8f2d6 100644
--- a/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
+++ b/src/tools/rust-analyzer/crates/test-utils/src/minicore.rs
@@ -58,6 +58,7 @@
 //!     pin:
 //!     pointee: copy, send, sync, ord, hash, unpin, phantom_data
 //!     range:
+//!     new_range:
 //!     receiver: deref
 //!     result:
 //!     send: sized
@@ -175,7 +176,9 @@ pub mod marker {
 
     // region:clone
     impl Clone for PhantomData {
-        fn clone(&self) -> Self { Self }
+        fn clone(&self) -> Self {
+            Self
+        }
     }
     // endregion:clone
 
@@ -1128,6 +1131,32 @@ pub mod ops {
     // endregion:dispatch_from_dyn
 }
 
+// region:new_range
+pub mod range {
+    #[lang = "RangeCopy"]
+    pub struct Range {
+        pub start: Idx,
+        pub end: Idx,
+    }
+
+    #[lang = "RangeFromCopy"]
+    pub struct RangeFrom {
+        pub start: Idx,
+    }
+
+    #[lang = "RangeInclusiveCopy"]
+    pub struct RangeInclusive {
+        pub start: Idx,
+        pub end: Idx,
+    }
+
+    #[lang = "RangeToInclusiveCopy"]
+    pub struct RangeToInclusive {
+        pub end: Idx,
+    }
+}
+// endregion:new_range
+
 // region:eq
 pub mod cmp {
     use crate::marker::PointeeSized;
@@ -1144,7 +1173,9 @@ pub mod cmp {
 
     // region:builtin_impls
     impl PartialEq for () {
-        fn eq(&self, other: &()) -> bool { true }
+        fn eq(&self, other: &()) -> bool {
+            true
+        }
     }
     // endregion:builtin_impls
 
@@ -1567,10 +1598,7 @@ pub mod pin {
     }
     // endregion:dispatch_from_dyn
     // region:coerce_unsized
-    impl crate::ops::CoerceUnsized> for Pin where
-        Ptr: crate::ops::CoerceUnsized
-    {
-    }
+    impl crate::ops::CoerceUnsized> for Pin where Ptr: crate::ops::CoerceUnsized {}
     // endregion:coerce_unsized
 }
 // endregion:pin
@@ -1792,9 +1820,9 @@ pub mod iter {
                 fn from_iter>(iter: T) -> Self;
             }
         }
-        pub use self::collect::{IntoIterator, FromIterator};
+        pub use self::collect::{FromIterator, IntoIterator};
     }
-    pub use self::traits::{IntoIterator, FromIterator, Iterator};
+    pub use self::traits::{FromIterator, IntoIterator, Iterator};
 }
 // endregion:iterator
 
@@ -2087,30 +2115,30 @@ macro_rules! column {
 pub mod prelude {
     pub mod v1 {
         pub use crate::{
-            clone::Clone,                            // :clone
-            cmp::{Eq, PartialEq},                    // :eq
-            cmp::{Ord, PartialOrd},                  // :ord
-            convert::AsMut,                          // :as_mut
-            convert::AsRef,                          // :as_ref
-            convert::{From, Into, TryFrom, TryInto}, // :from
-            default::Default,                        // :default
-            iter::{IntoIterator, Iterator, FromIterator}, // :iterator
-            macros::builtin::{derive, derive_const}, // :derive
-            marker::Copy,                            // :copy
-            marker::Send,                            // :send
-            marker::Sized,                           // :sized
-            marker::Sync,                            // :sync
-            mem::drop,                               // :drop
-            mem::size_of,                            // :size_of
-            ops::Drop,                               // :drop
-            ops::{AsyncFn, AsyncFnMut, AsyncFnOnce}, // :async_fn
-            ops::{Fn, FnMut, FnOnce},                // :fn
-            option::Option::{self, None, Some},      // :option
-            panic,                                   // :panic
-            result::Result::{self, Err, Ok},         // :result
-            str::FromStr,                            // :str
-            fmt::derive::Debug,                      // :fmt, derive
-            hash::derive::Hash,                      // :hash, derive
+            clone::Clone,                                 // :clone
+            cmp::{Eq, PartialEq},                         // :eq
+            cmp::{Ord, PartialOrd},                       // :ord
+            convert::AsMut,                               // :as_mut
+            convert::AsRef,                               // :as_ref
+            convert::{From, Into, TryFrom, TryInto},      // :from
+            default::Default,                             // :default
+            fmt::derive::Debug,                           // :fmt, derive
+            hash::derive::Hash,                           // :hash, derive
+            iter::{FromIterator, IntoIterator, Iterator}, // :iterator
+            macros::builtin::{derive, derive_const},      // :derive
+            marker::Copy,                                 // :copy
+            marker::Send,                                 // :send
+            marker::Sized,                                // :sized
+            marker::Sync,                                 // :sync
+            mem::drop,                                    // :drop
+            mem::size_of,                                 // :size_of
+            ops::Drop,                                    // :drop
+            ops::{AsyncFn, AsyncFnMut, AsyncFnOnce},      // :async_fn
+            ops::{Fn, FnMut, FnOnce},                     // :fn
+            option::Option::{self, None, Some},           // :option
+            panic,                                        // :panic
+            result::Result::{self, Err, Ok},              // :result
+            str::FromStr,                                 // :str
         };
     }
 

From 6948a66ad81e149d8a0af5dcc0888b9a44f3d2b4 Mon Sep 17 00:00:00 2001
From: Shoyu Vanilla 
Date: Wed, 14 Jan 2026 05:40:27 +0900
Subject: [PATCH 062/319] fix: Sync cast checks to rustc again

---
 .../crates/hir-ty/src/infer/cast.rs           | 270 +++++++++++++-----
 .../crates/hir-ty/src/infer/unify.rs          |  10 -
 .../src/handlers/invalid_cast.rs              |  10 +-
 3 files changed, 201 insertions(+), 89 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
index d073b06ccc8a..d69b00adb7f7 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/cast.rs
@@ -2,8 +2,10 @@
 
 use hir_def::{AdtId, hir::ExprId, signatures::TraitFlags};
 use rustc_ast_ir::Mutability;
+use rustc_hash::FxHashSet;
 use rustc_type_ir::{
-    Flags, InferTy, TypeFlags, UintTy,
+    InferTy, TypeVisitableExt, UintTy, elaborate,
+    error::TypeError,
     inherent::{AdtDef, BoundExistentialPredicates as _, IntoKind, Ty as _},
 };
 use stdx::never;
@@ -12,7 +14,10 @@ use crate::{
     InferenceDiagnostic,
     db::HirDatabase,
     infer::{AllowTwoPhase, InferenceContext, expr::ExprIsRead},
-    next_solver::{BoundExistentialPredicates, DbInterner, ParamTy, Ty, TyKind},
+    next_solver::{
+        BoundExistentialPredicates, ExistentialPredicate, ParamTy, Region, Ty, TyKind,
+        infer::traits::ObligationCause,
+    },
 };
 
 #[derive(Debug)]
@@ -66,12 +71,13 @@ pub enum CastError {
     DifferingKinds,
     SizedUnsizedCast,
     IllegalCast,
-    IntToFatCast,
+    IntToWideCast,
     NeedDeref,
     NeedViaPtr,
     NeedViaThinPtr,
     NeedViaInt,
     NonScalar,
+    PtrPtrAddingAutoTraits,
     // We don't want to report errors with unknown types currently.
     // UnknownCastPtrKind,
     // UnknownExprPtrKind,
@@ -137,22 +143,13 @@ impl<'db> CastCheck<'db> {
             return Ok(());
         }
 
-        if !self.cast_ty.flags().contains(TypeFlags::HAS_TY_INFER)
-            && !ctx.table.is_sized(self.cast_ty)
-        {
+        if !self.cast_ty.has_infer_types() && !ctx.table.is_sized(self.cast_ty) {
             return Err(InferenceDiagnostic::CastToUnsized {
                 expr: self.expr,
                 cast_ty: self.cast_ty.store(),
             });
         }
 
-        // Chalk doesn't support trait upcasting and fails to solve some obvious goals
-        // when the trait environment contains some recursive traits (See issue #18047)
-        // We skip cast checks for such cases for now, until the next-gen solver.
-        if contains_dyn_trait(self.cast_ty) {
-            return Ok(());
-        }
-
         self.do_check(ctx).map_err(|e| e.into_diagnostic(self.expr, self.expr_ty, self.cast_ty))
     }
 
@@ -162,22 +159,23 @@ impl<'db> CastCheck<'db> {
                 (Some(t_from), Some(t_cast)) => (t_from, t_cast),
                 (None, Some(t_cast)) => match self.expr_ty.kind() {
                     TyKind::FnDef(..) => {
-                        let sig =
-                            self.expr_ty.callable_sig(ctx.interner()).expect("FnDef had no sig");
-                        let sig = ctx.table.normalize_associated_types_in(sig);
+                        // rustc calls `FnCtxt::normalize` on this but it's a no-op in next-solver
+                        let sig = self.expr_ty.fn_sig(ctx.interner());
                         let fn_ptr = Ty::new_fn_ptr(ctx.interner(), sig);
-                        if ctx
-                            .coerce(
-                                self.source_expr.into(),
-                                self.expr_ty,
-                                fn_ptr,
-                                AllowTwoPhase::No,
-                                ExprIsRead::Yes,
-                            )
-                            .is_ok()
-                        {
-                        } else {
-                            return Err(CastError::IllegalCast);
+                        match ctx.coerce(
+                            self.source_expr.into(),
+                            self.expr_ty,
+                            fn_ptr,
+                            AllowTwoPhase::No,
+                            ExprIsRead::Yes,
+                        ) {
+                            Ok(_) => {}
+                            Err(TypeError::IntrinsicCast) => {
+                                return Err(CastError::IllegalCast);
+                            }
+                            Err(_) => {
+                                return Err(CastError::NonScalar);
+                            }
                         }
 
                         (CastTy::FnPtr, t_cast)
@@ -213,23 +211,41 @@ impl<'db> CastCheck<'db> {
         // rustc checks whether the `expr_ty` is foreign adt with `non_exhaustive` sym
 
         match (t_from, t_cast) {
+            // These types have invariants! can't cast into them.
             (_, CastTy::Int(Int::CEnum) | CastTy::FnPtr) => Err(CastError::NonScalar),
+
+            // * -> Bool
             (_, CastTy::Int(Int::Bool)) => Err(CastError::CastToBool),
-            (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()),
+
+            // * -> Char
+            (CastTy::Int(Int::U(UintTy::U8)), CastTy::Int(Int::Char)) => Ok(()), // u8-char-cast
             (_, CastTy::Int(Int::Char)) => Err(CastError::CastToChar),
+
+            // prim -> float,ptr
             (CastTy::Int(Int::Bool | Int::CEnum | Int::Char), CastTy::Float) => {
                 Err(CastError::NeedViaInt)
             }
+
             (CastTy::Int(Int::Bool | Int::CEnum | Int::Char) | CastTy::Float, CastTy::Ptr(..))
             | (CastTy::Ptr(..) | CastTy::FnPtr, CastTy::Float) => Err(CastError::IllegalCast),
-            (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst),
+
+            // ptr -> ptr
+            (CastTy::Ptr(src, _), CastTy::Ptr(dst, _)) => self.check_ptr_ptr_cast(ctx, src, dst), // ptr-ptr-cast
+
+            // // ptr-addr-cast
             (CastTy::Ptr(src, _), CastTy::Int(_)) => self.check_ptr_addr_cast(ctx, src),
+            (CastTy::FnPtr, CastTy::Int(_)) => Ok(()),
+
+            // addr-ptr-cast
             (CastTy::Int(_), CastTy::Ptr(dst, _)) => self.check_addr_ptr_cast(ctx, dst),
+
+            // fn-ptr-cast
             (CastTy::FnPtr, CastTy::Ptr(dst, _)) => self.check_fptr_ptr_cast(ctx, dst),
+
+            // prim -> prim
             (CastTy::Int(Int::CEnum), CastTy::Int(_)) => Ok(()),
             (CastTy::Int(Int::Char | Int::Bool), CastTy::Int(_)) => Ok(()),
             (CastTy::Int(_) | CastTy::Float, CastTy::Int(_) | CastTy::Float) => Ok(()),
-            (CastTy::FnPtr, CastTy::Int(_)) => Ok(()),
         }
     }
 
@@ -241,10 +257,16 @@ impl<'db> CastCheck<'db> {
         t_cast: Ty<'db>,
         m_cast: Mutability,
     ) -> Result<(), CastError> {
-        // Mutability order is opposite to rustc. `Mut < Not`
-        if m_expr <= m_cast
+        let t_expr = ctx.table.try_structurally_resolve_type(t_expr);
+        let t_cast = ctx.table.try_structurally_resolve_type(t_cast);
+
+        if m_expr >= m_cast
             && let TyKind::Array(ety, _) = t_expr.kind()
+            && ctx.infcx().can_eq(ctx.table.param_env, ety, t_cast)
         {
+            // Due to historical reasons we allow directly casting references of
+            // arrays into raw pointers of their element type.
+
             // Coerce to a raw pointer so that we generate RawPtr in MIR.
             let array_ptr_type = Ty::new_ptr(ctx.interner(), t_expr, m_expr);
             if ctx
@@ -265,14 +287,9 @@ impl<'db> CastCheck<'db> {
                 );
             }
 
-            // This is a less strict condition than rustc's `demand_eqtype`,
-            // but false negative is better than false positive
-            if ctx
-                .coerce(self.source_expr.into(), ety, t_cast, AllowTwoPhase::No, ExprIsRead::Yes)
-                .is_ok()
-            {
-                return Ok(());
-            }
+            // this will report a type mismatch if needed
+            let _ = ctx.demand_eqtype(self.expr.into(), ety, t_cast);
+            return Ok(());
         }
 
         Err(CastError::IllegalCast)
@@ -289,30 +306,147 @@ impl<'db> CastCheck<'db> {
 
         match (src_kind, dst_kind) {
             (Some(PointerKind::Error), _) | (_, Some(PointerKind::Error)) => Ok(()),
+
             // (_, None) => Err(CastError::UnknownCastPtrKind),
             // (None, _) => Err(CastError::UnknownExprPtrKind),
             (_, None) | (None, _) => Ok(()),
+
+            // Cast to thin pointer is OK
             (_, Some(PointerKind::Thin)) => Ok(()),
+
+            // thin -> fat? report invalid cast (don't complain about vtable kinds)
             (Some(PointerKind::Thin), _) => Err(CastError::SizedUnsizedCast),
+
+            // trait object -> trait object? need to do additional checks
             (Some(PointerKind::VTable(src_tty)), Some(PointerKind::VTable(dst_tty))) => {
                 match (src_tty.principal_def_id(), dst_tty.principal_def_id()) {
+                    // A + SrcAuto> -> B + DstAuto>. need to make sure
+                    // - `Src` and `Dst` traits are the same
+                    // - traits have the same generic arguments
+                    // - projections are the same
+                    // - `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`
+                    //
+                    // Note that trait upcasting goes through a different mechanism (`coerce_unsized`)
+                    // and is unaffected by this check.
                     (Some(src_principal), Some(dst_principal)) => {
                         if src_principal == dst_principal {
                             return Ok(());
                         }
-                        let src_principal = ctx.db.trait_signature(src_principal.0);
-                        let dst_principal = ctx.db.trait_signature(dst_principal.0);
-                        if src_principal.flags.contains(TraitFlags::AUTO)
-                            && dst_principal.flags.contains(TraitFlags::AUTO)
+
+                        // We need to reconstruct trait object types.
+                        // `m_src` and `m_dst` won't work for us here because they will potentially
+                        // contain wrappers, which we do not care about.
+                        //
+                        // e.g. we want to allow `dyn T -> (dyn T,)`, etc.
+                        //
+                        // We also need to skip auto traits to emit an FCW and not an error.
+                        let src_obj = Ty::new_dynamic(
+                            ctx.interner(),
+                            BoundExistentialPredicates::new_from_iter(
+                                ctx.interner(),
+                                src_tty.iter().filter(|pred| {
+                                    !matches!(
+                                        pred.skip_binder(),
+                                        ExistentialPredicate::AutoTrait(_)
+                                    )
+                                }),
+                            ),
+                            Region::new_erased(ctx.interner()),
+                        );
+                        let dst_obj = Ty::new_dynamic(
+                            ctx.interner(),
+                            BoundExistentialPredicates::new_from_iter(
+                                ctx.interner(),
+                                dst_tty.iter().filter(|pred| {
+                                    !matches!(
+                                        pred.skip_binder(),
+                                        ExistentialPredicate::AutoTrait(_)
+                                    )
+                                }),
+                            ),
+                            Region::new_erased(ctx.interner()),
+                        );
+
+                        // `dyn Src = dyn Dst`, this checks for matching traits/generics/projections
+                        // This is `fcx.demand_eqtype`, but inlined to give a better error.
+                        if ctx
+                            .table
+                            .at(&ObligationCause::dummy())
+                            .eq(src_obj, dst_obj)
+                            .map(|infer_ok| ctx.table.register_infer_ok(infer_ok))
+                            .is_err()
                         {
-                            Ok(())
-                        } else {
-                            Err(CastError::DifferingKinds)
+                            return Err(CastError::DifferingKinds);
                         }
+
+                        // Check that `SrcAuto` (+auto traits implied by `Src`) is a superset of `DstAuto`.
+                        // Emit an FCW otherwise.
+                        let src_auto: FxHashSet<_> = src_tty
+                            .auto_traits()
+                            .into_iter()
+                            .chain(
+                                elaborate::supertrait_def_ids(ctx.interner(), src_principal)
+                                    .filter(|trait_| {
+                                        ctx.db
+                                            .trait_signature(trait_.0)
+                                            .flags
+                                            .contains(TraitFlags::AUTO)
+                                    }),
+                            )
+                            .collect();
+
+                        let added = dst_tty
+                            .auto_traits()
+                            .into_iter()
+                            .any(|trait_| !src_auto.contains(&trait_));
+
+                        if added {
+                            return Err(CastError::PtrPtrAddingAutoTraits);
+                        }
+
+                        Ok(())
                     }
-                    _ => Err(CastError::Unknown),
+
+                    // dyn Auto -> dyn Auto'? ok.
+                    (None, None) => Ok(()),
+
+                    // dyn Trait -> dyn Auto? not ok (for now).
+                    //
+                    // Although dropping the principal is already allowed for unsizing coercions
+                    // (e.g. `*const (dyn Trait + Auto)` to `*const dyn Auto`), dropping it is
+                    // currently **NOT** allowed for (non-coercion) ptr-to-ptr casts (e.g
+                    // `*const Foo` to `*const Bar` where `Foo` has a `dyn Trait + Auto` tail
+                    // and `Bar` has a `dyn Auto` tail), because the underlying MIR operations
+                    // currently work very differently:
+                    //
+                    // * A MIR unsizing coercion on raw pointers to trait objects (`*const dyn Src`
+                    //   to `*const dyn Dst`) is currently equivalent to downcasting the source to
+                    //   the concrete sized type that it was originally unsized from first (via a
+                    //   ptr-to-ptr cast from `*const Src` to `*const T` with `T: Sized`) and then
+                    //   unsizing this thin pointer to the target type (unsizing `*const T` to
+                    //   `*const Dst`). In particular, this means that the pointer's metadata
+                    //   (vtable) will semantically change, e.g. for const eval and miri, even
+                    //   though the vtables will always be merged for codegen.
+                    //
+                    // * A MIR ptr-to-ptr cast is currently equivalent to a transmute and does not
+                    //   change the pointer metadata (vtable) at all.
+                    //
+                    // In addition to this potentially surprising difference between coercion and
+                    // non-coercion casts, casting away the principal with a MIR ptr-to-ptr cast
+                    // is currently considered undefined behavior:
+                    //
+                    // As a validity invariant of pointers to trait objects, we currently require
+                    // that the principal of the vtable in the pointer metadata exactly matches
+                    // the principal of the pointee type, where "no principal" is also considered
+                    // a kind of principal.
+                    (Some(_), None) => Err(CastError::DifferingKinds),
+
+                    // dyn Auto -> dyn Trait? not ok.
+                    (None, Some(_)) => Err(CastError::DifferingKinds),
                 }
             }
+
+            // fat -> fat? metadata kinds must match
             (Some(src_kind), Some(dst_kind)) if src_kind == dst_kind => Ok(()),
             (_, _) => Err(CastError::DifferingKinds),
         }
@@ -342,9 +476,9 @@ impl<'db> CastCheck<'db> {
             None => Ok(()),
             Some(PointerKind::Error) => Ok(()),
             Some(PointerKind::Thin) => Ok(()),
-            Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast),
-            Some(PointerKind::Length) => Err(CastError::IntToFatCast),
-            Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToFatCast),
+            Some(PointerKind::VTable(_)) => Err(CastError::IntToWideCast),
+            Some(PointerKind::Length) => Err(CastError::IntToWideCast),
+            Some(PointerKind::OfAlias | PointerKind::OfParam(_)) => Err(CastError::IntToWideCast),
         }
     }
 
@@ -363,15 +497,20 @@ impl<'db> CastCheck<'db> {
     }
 }
 
+/// The kind of pointer and associated metadata (thin, length or vtable) - we
+/// only allow casts between wide pointers if their metadata have the same
+/// kind.
 #[derive(Debug, PartialEq, Eq)]
 enum PointerKind<'db> {
-    // thin pointer
+    /// No metadata attached, ie pointer to sized type or foreign type
     Thin,
-    // trait object
+    /// A trait object
     VTable(BoundExistentialPredicates<'db>),
-    // slice
+    /// Slice
     Length,
+    /// The unsize info of this projection or opaque type
     OfAlias,
+    /// The unsize info of this parameter
     OfParam(ParamTy),
     Error,
 }
@@ -439,24 +578,3 @@ fn pointer_kind<'db>(
         }
     }
 }
-
-fn contains_dyn_trait<'db>(ty: Ty<'db>) -> bool {
-    use std::ops::ControlFlow;
-
-    use rustc_type_ir::{TypeSuperVisitable, TypeVisitable, TypeVisitor};
-
-    struct DynTraitVisitor;
-
-    impl<'db> TypeVisitor> for DynTraitVisitor {
-        type Result = ControlFlow<()>;
-
-        fn visit_ty(&mut self, ty: Ty<'db>) -> ControlFlow<()> {
-            match ty.kind() {
-                TyKind::Dynamic(..) => ControlFlow::Break(()),
-                _ => ty.super_visit_with(self),
-            }
-        }
-    }
-
-    ty.visit_with(&mut DynTraitVisitor).is_break()
-}
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
index d55fc0ab0da6..2057159c46d2 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/unify.rs
@@ -261,16 +261,6 @@ impl<'db> InferenceTable<'db> {
         self.infer_ctxt.canonicalize_response(t)
     }
 
-    // FIXME: We should get rid of this method. We cannot deeply normalize during inference, only when finishing.
-    // Inference should use shallow normalization (`try_structurally_resolve_type()`) only, when needed.
-    pub(crate) fn normalize_associated_types_in(&mut self, ty: T) -> T
-    where
-        T: TypeFoldable> + Clone,
-    {
-        let ty = self.resolve_vars_with_obligations(ty);
-        self.at(&ObligationCause::new()).deeply_normalize(ty.clone()).unwrap_or(ty)
-    }
-
     pub(crate) fn normalize_alias_ty(&mut self, alias: Ty<'db>) -> Ty<'db> {
         self.infer_ctxt
             .at(&ObligationCause::new(), self.param_env)
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
index a59077b757b1..7479f8147d2e 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/invalid_cast.rs
@@ -51,7 +51,7 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_
             DiagnosticCode::RustcHardError("E0606"),
             format_ty!(ctx, "casting `{}` as `{}` is invalid", d.expr_ty, d.cast_ty),
         ),
-        CastError::IntToFatCast => (
+        CastError::IntToWideCast => (
             DiagnosticCode::RustcHardError("E0606"),
             format_ty!(ctx, "cannot cast `{}` to a fat pointer `{}`", d.expr_ty, d.cast_ty),
         ),
@@ -95,6 +95,10 @@ pub(crate) fn invalid_cast(ctx: &DiagnosticsContext<'_>, d: &hir::InvalidCast<'_
             DiagnosticCode::RustcHardError("E0605"),
             format_ty!(ctx, "non-primitive cast: `{}` as `{}`", d.expr_ty, d.cast_ty),
         ),
+        CastError::PtrPtrAddingAutoTraits => (
+            DiagnosticCode::RustcHardError("E0804"),
+            "cannot add auto trait to dyn bound via pointer cast".to_owned(),
+        ),
         // CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => (
         //     DiagnosticCode::RustcHardError("E0641"),
         //     "cannot cast to a pointer of an unknown kind".to_owned(),
@@ -444,8 +448,8 @@ fn main() {
     q as *const [i32];
   //^^^^^^^^^^^^^^^^^ error: cannot cast thin pointer `*const i32` to fat pointer `*const [i32]`
 
-    // FIXME: This should emit diagnostics but disabled to prevent many false positives
     let t: *mut (dyn Trait + 'static) = 0 as *mut _;
+                                      //^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*mut (dyn Trait + 'static)`
 
     let mut fail: *const str = 0 as *const str;
                              //^^^^^^^^^^^^^^^ error: cannot cast `usize` to a fat pointer `*const str`
@@ -543,7 +547,7 @@ fn main() {
     fn ptr_to_trait_obj_ok() {
         check_diagnostics(
             r#"
-//- minicore: pointee
+//- minicore: pointee, send, sync
 trait Trait<'a> {}
 
 fn remove_auto<'a>(x: *mut (dyn Trait<'a> + Send)) -> *mut dyn Trait<'a> {

From cd6c41283838023ee3b22709106d666869ec9a6f Mon Sep 17 00:00:00 2001
From: Zachary S 
Date: Tue, 13 Jan 2026 22:40:12 -0600
Subject: [PATCH 063/319] Add test for feature-gating `mut ref` patterns in
 struct field shorthand.

---
 tests/ui/feature-gates/feature-gate-mut-ref.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.rs b/tests/ui/feature-gates/feature-gate-mut-ref.rs
index 752ae35d8a9a..eac2a59002e2 100644
--- a/tests/ui/feature-gates/feature-gate-mut-ref.rs
+++ b/tests/ui/feature-gates/feature-gate-mut-ref.rs
@@ -10,4 +10,7 @@ fn main() {
     let mut ref x = 10; //~  ERROR [E0658]
     #[cfg(false)]
     let mut ref mut y = 10; //~  ERROR [E0658]
+
+    struct Foo { x: i32 }
+    let Foo { mut ref x } = Foo { x: 10 };
 }

From f809e332d895bc80d28d8d2cb10297a3fe9ca4f2 Mon Sep 17 00:00:00 2001
From: Zachary S 
Date: Tue, 13 Jan 2026 12:57:04 -0600
Subject: [PATCH 064/319] Feature-gate `mut ref` patterns in struct field
 shorthand.

---
 compiler/rustc_parse/src/parser/pat.rs             |  6 ++++++
 tests/ui/feature-gates/feature-gate-mut-ref.rs     |  2 +-
 tests/ui/feature-gates/feature-gate-mut-ref.stderr | 12 +++++++++++-
 3 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs
index d7f3a36122e5..f3e12d48a6a5 100644
--- a/compiler/rustc_parse/src/parser/pat.rs
+++ b/compiler/rustc_parse/src/parser/pat.rs
@@ -1750,6 +1750,12 @@ impl<'a> Parser<'a> {
             hi = self.prev_token.span;
             let ann = BindingMode(by_ref, mutability);
             let fieldpat = self.mk_pat_ident(boxed_span.to(hi), ann, fieldname);
+            if matches!(
+                fieldpat.kind,
+                PatKind::Ident(BindingMode(ByRef::Yes(..), Mutability::Mut), ..)
+            ) {
+                self.psess.gated_spans.gate(sym::mut_ref, fieldpat.span);
+            }
             let subpat = if is_box {
                 self.mk_pat(lo.to(hi), PatKind::Box(Box::new(fieldpat)))
             } else {
diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.rs b/tests/ui/feature-gates/feature-gate-mut-ref.rs
index eac2a59002e2..74b1fba5dfea 100644
--- a/tests/ui/feature-gates/feature-gate-mut-ref.rs
+++ b/tests/ui/feature-gates/feature-gate-mut-ref.rs
@@ -12,5 +12,5 @@ fn main() {
     let mut ref mut y = 10; //~  ERROR [E0658]
 
     struct Foo { x: i32 }
-    let Foo { mut ref x } = Foo { x: 10 };
+    let Foo { mut ref x } = Foo { x: 10 }; //~  ERROR [E0658]
 }
diff --git a/tests/ui/feature-gates/feature-gate-mut-ref.stderr b/tests/ui/feature-gates/feature-gate-mut-ref.stderr
index d3eb674e92de..921ff878bf4d 100644
--- a/tests/ui/feature-gates/feature-gate-mut-ref.stderr
+++ b/tests/ui/feature-gates/feature-gate-mut-ref.stderr
@@ -38,6 +38,16 @@ LL |     let mut ref mut y = 10;
    = help: add `#![feature(mut_ref)]` 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 4 previous errors
+error[E0658]: mutable by-reference bindings are experimental
+  --> $DIR/feature-gate-mut-ref.rs:15:15
+   |
+LL |     let Foo { mut ref x } = Foo { x: 10 };
+   |               ^^^^^^^^^
+   |
+   = note: see issue #123076  for more information
+   = help: add `#![feature(mut_ref)]` 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 5 previous errors
 
 For more information about this error, try `rustc --explain E0658`.

From 05939a8d38959b1ff418a0f54663eaba0f5e3108 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Wed, 14 Jan 2026 17:54:23 +0800
Subject: [PATCH 065/319] Fix false positive precedence in `(2 as i32) < 3`

Example
---
```rust
fn f() { _ = $0(1 as u32) << 10; }
```

**Before this PR**

This is syntax error

```rust
fn f() { _ = 1 as u32 << 10; }
```

**After this PR**

Assist not applicable
---
 .../src/handlers/remove_parentheses.rs        |  6 +++++
 .../crates/syntax/src/ast/prec.rs             | 24 +++++++++++++++----
 2 files changed, 26 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
index aa4d2bcadb01..f07da489e23a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/remove_parentheses.rs
@@ -321,6 +321,12 @@ mod tests {
         );
     }
 
+    #[test]
+    fn remove_parens_conflict_cast_before_l_angle() {
+        check_assist_not_applicable(remove_parentheses, r#"fn f() { _ = $0(1 as u32) << 10; }"#);
+        check_assist_not_applicable(remove_parentheses, r#"fn f() { _ = $0(1 as u32) < 10; }"#);
+    }
+
     #[test]
     fn remove_parens_double_paren_stmt() {
         check_assist(
diff --git a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
index 8c88224a761a..d99cf492616e 100644
--- a/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
+++ b/src/tools/rust-analyzer/crates/syntax/src/ast/prec.rs
@@ -154,6 +154,11 @@ fn check_ancestry(ancestor: &SyntaxNode, descendent: &SyntaxNode) -> bool {
     bail()
 }
 
+fn next_token_of(node: &SyntaxNode) -> Option {
+    let last = node.last_token()?;
+    skip_trivia_token(last.next_token()?, Direction::Next)
+}
+
 impl Expr {
     pub fn precedence(&self) -> ExprPrecedence {
         precedence(self)
@@ -197,6 +202,8 @@ impl Expr {
         if is_parent_call_expr && is_field_expr {
             return true;
         }
+        let place_of_parent =
+            || place_of.ancestors().find(|it| it.parent().is_none_or(|p| &p == parent.syntax()));
 
         // Special-case block weirdness
         if parent.child_is_followed_by_a_block() {
@@ -226,15 +233,24 @@ impl Expr {
         // For `&&`, we avoid introducing ` && ` into a binary chain.
 
         if self.precedence() == ExprPrecedence::Jump
-            && let Some(node) =
-                place_of.ancestors().find(|it| it.parent().is_none_or(|p| &p == parent.syntax()))
-            && let Some(next) =
-                node.last_token().and_then(|t| skip_trivia_token(t.next_token()?, Direction::Next))
+            && let Some(node) = place_of_parent()
+            && let Some(next) = next_token_of(&node)
             && matches!(next.kind(), T![||] | T![&&])
         {
             return true;
         }
 
+        // Special-case `2 as x < 3`
+        if let ast::Expr::CastExpr(it) = self
+            && let Some(ty) = it.ty()
+            && ty.syntax().last_token().and_then(|it| ast::NameLike::cast(it.parent()?)).is_some()
+            && let Some(node) = place_of_parent()
+            && let Some(next) = next_token_of(&node)
+            && matches!(next.kind(), T![<] | T![<<])
+        {
+            return true;
+        }
+
         if self.is_paren_like()
             || parent.is_paren_like()
             || self.is_prefix()

From 4b5b42c82c1cf2095fcb183c72406eb82c908b21 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Wed, 14 Jan 2026 16:34:31 +0530
Subject: [PATCH 066/319] remove postcard from legacy

---
 .../src/bidirectional_protocol.rs             |  9 +---
 .../proc-macro-api/src/legacy_protocol.rs     |  8 +---
 .../crates/proc-macro-api/src/lib.rs          |  3 --
 .../crates/proc-macro-api/src/process.rs      | 45 ++++++-------------
 .../crates/proc-macro-srv-cli/src/main.rs     |  5 ---
 .../proc-macro-srv-cli/src/main_loop.rs       |  1 -
 6 files changed, 16 insertions(+), 55 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index e44723a6a389..5996f882981c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -212,14 +212,7 @@ fn run_request(
     if let Some(err) = srv.exited() {
         return Err(err.clone());
     }
-
-    match srv.use_postcard() {
-        true => srv.run_bidirectional::(msg, callback),
-        false => Err(ServerError {
-            message: "bidirectional messaging does not support JSON".to_owned(),
-            io: None,
-        }),
-    }
+    srv.run_bidirectional::(msg, callback)
 }
 
 pub fn reject_subrequests(req: SubRequest) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 4524d1b66bfe..aabe5a011851 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -19,7 +19,7 @@ use crate::{
     },
     process::ProcMacroServerProcess,
     transport::codec::Codec,
-    transport::codec::{json::JsonProtocol, postcard::PostcardProtocol},
+    transport::codec::json::JsonProtocol,
     version,
 };
 
@@ -148,11 +148,7 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result(send_request::, req)
-    } else {
-        srv.send_task::<_, _, JsonProtocol>(send_request::, req)
-    }
+    srv.send_task::<_, _, JsonProtocol>(send_request::, req)
 }
 
 /// Sends a request to the server and reads the response.
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 822809943a36..01195c10feef 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
@@ -49,8 +49,6 @@ pub mod version {
 pub enum ProtocolFormat {
     /// JSON-based legacy protocol (newline-delimited JSON).
     JsonLegacy,
-    /// Postcard-based legacy protocol (COBS-encoded postcard).
-    PostcardLegacy,
     /// Bidirectional postcard protocol with sub-request support.
     BidirectionalPostcardPrototype,
 }
@@ -59,7 +57,6 @@ impl fmt::Display for ProtocolFormat {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         match self {
             ProtocolFormat::JsonLegacy => write!(f, "json-legacy"),
-            ProtocolFormat::PostcardLegacy => write!(f, "postcard-legacy"),
             ProtocolFormat::BidirectionalPostcardPrototype => {
                 write!(f, "bidirectional-postcard-prototype")
             }
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 4f8762158790..cd387dad0d0b 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
@@ -43,7 +43,6 @@ impl std::fmt::Debug for ProcMacroServerProcess {
 #[derive(Debug, Clone)]
 pub(crate) enum Protocol {
     LegacyJson { mode: SpanMode },
-    LegacyPostcard { mode: SpanMode },
     BidirectionalPostcardPrototype { mode: SpanMode },
 }
 
@@ -136,7 +135,6 @@ impl ProcMacroServerProcess {
         {
             &[
                 Some(ProtocolFormat::BidirectionalPostcardPrototype),
-                Some(ProtocolFormat::PostcardLegacy),
                 Some(ProtocolFormat::JsonLegacy),
             ]
         } else {
@@ -155,9 +153,6 @@ impl ProcMacroServerProcess {
                         Some(ProtocolFormat::BidirectionalPostcardPrototype) => {
                             Protocol::BidirectionalPostcardPrototype { mode: SpanMode::Id }
                         }
-                        Some(ProtocolFormat::PostcardLegacy) => {
-                            Protocol::LegacyPostcard { mode: SpanMode::Id }
-                        }
                         Some(ProtocolFormat::JsonLegacy) | None => {
                             Protocol::LegacyJson { mode: SpanMode::Id }
                         }
@@ -185,7 +180,6 @@ impl ProcMacroServerProcess {
                     {
                         match &mut srv.protocol {
                             Protocol::LegacyJson { mode }
-                            | Protocol::LegacyPostcard { mode }
                             | Protocol::BidirectionalPostcardPrototype { mode } => *mode = new_mode,
                         }
                     }
@@ -208,10 +202,6 @@ impl ProcMacroServerProcess {
         self.exited.get().map(|it| &it.0)
     }
 
-    pub(crate) fn use_postcard(&self) -> bool {
-        matches!(self.protocol, Protocol::LegacyPostcard { .. })
-    }
-
     /// Retrieves the API version of the proc-macro server.
     pub(crate) fn version(&self) -> u32 {
         self.version
@@ -221,7 +211,6 @@ impl ProcMacroServerProcess {
     pub(crate) fn rust_analyzer_spans(&self) -> bool {
         match self.protocol {
             Protocol::LegacyJson { mode } => mode == SpanMode::RustAnalyzer,
-            Protocol::LegacyPostcard { mode } => mode == SpanMode::RustAnalyzer,
             Protocol::BidirectionalPostcardPrototype { mode } => mode == SpanMode::RustAnalyzer,
         }
     }
@@ -229,9 +218,7 @@ impl ProcMacroServerProcess {
     /// Checks the API version of the running proc-macro server.
     fn version_check(&self, callback: Option>) -> Result {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::version_check(self)
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::version_check(self),
             Protocol::BidirectionalPostcardPrototype { .. } => {
                 let cb = callback.expect("callback required for bidirectional protocol");
                 bidirectional_protocol::version_check(self, cb)
@@ -245,9 +232,7 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::enable_rust_analyzer_spans(self)
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::enable_rust_analyzer_spans(self),
             Protocol::BidirectionalPostcardPrototype { .. } => {
                 let cb = callback.expect("callback required for bidirectional protocol");
                 bidirectional_protocol::enable_rust_analyzer_spans(self, cb)
@@ -262,9 +247,7 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result, String>, ServerError> {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::find_proc_macros(self, dylib_path)
-            }
+            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)
@@ -285,18 +268,16 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result, ServerError> {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::expand(
-                    proc_macro,
-                    subtree,
-                    attr,
-                    env,
-                    def_site,
-                    call_site,
-                    mixed_site,
-                    current_dir,
-                )
-            }
+            Protocol::LegacyJson { .. } => legacy_protocol::expand(
+                proc_macro,
+                subtree,
+                attr,
+                env,
+                def_site,
+                call_site,
+                mixed_site,
+                current_dir,
+            ),
             Protocol::BidirectionalPostcardPrototype { .. } => bidirectional_protocol::expand(
                 proc_macro,
                 subtree,
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
index a246d4d3f28f..928753659f1c 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main.rs
@@ -67,7 +67,6 @@ impl ValueEnum for ProtocolFormatArg {
     fn value_variants<'a>() -> &'a [Self] {
         &[
             ProtocolFormatArg(ProtocolFormat::JsonLegacy),
-            ProtocolFormatArg(ProtocolFormat::PostcardLegacy),
             ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype),
         ]
     }
@@ -75,9 +74,6 @@ impl ValueEnum for ProtocolFormatArg {
     fn to_possible_value(&self) -> Option {
         match self.0 {
             ProtocolFormat::JsonLegacy => Some(clap::builder::PossibleValue::new("json-legacy")),
-            ProtocolFormat::PostcardLegacy => {
-                Some(clap::builder::PossibleValue::new("postcard-legacy"))
-            }
             ProtocolFormat::BidirectionalPostcardPrototype => {
                 Some(clap::builder::PossibleValue::new("bidirectional-postcard-prototype"))
             }
@@ -87,7 +83,6 @@ impl ValueEnum for ProtocolFormatArg {
     fn from_str(input: &str, _ignore_case: bool) -> Result {
         match input {
             "json-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::JsonLegacy)),
-            "postcard-legacy" => Ok(ProtocolFormatArg(ProtocolFormat::PostcardLegacy)),
             "bidirectional-postcard-prototype" => {
                 Ok(ProtocolFormatArg(ProtocolFormat::BidirectionalPostcardPrototype))
             }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
index 5180ede9fb90..70e1e091c197 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs
@@ -41,7 +41,6 @@ pub fn run(
 ) -> io::Result<()> {
     match format {
         ProtocolFormat::JsonLegacy => run_old::(stdin, stdout),
-        ProtocolFormat::PostcardLegacy => run_old::(stdin, stdout),
         ProtocolFormat::BidirectionalPostcardPrototype => {
             run_new::(stdin, stdout)
         }

From 31817f6deaf57c58f713bb61666d32bbd8306a3b Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Wed, 14 Jan 2026 16:34:57 +0530
Subject: [PATCH 067/319] remove flatten from ExpandMacro message in
 bidirectional messages

---
 .../crates/proc-macro-api/src/bidirectional_protocol/msg.rs      | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index 57e7b1ee8f68..c56ed5191694 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -70,7 +70,6 @@ pub struct ExpandMacro {
     pub lib: Utf8PathBuf,
     pub env: Vec<(String, String)>,
     pub current_dir: Option,
-    #[serde(flatten)]
     pub data: ExpandMacroData,
 }
 

From 98e1f7103b5d6bfcfcad849fa30a19614303160d Mon Sep 17 00:00:00 2001
From: Roberto Aloi 
Date: Wed, 14 Jan 2026 13:15:09 +0100
Subject: [PATCH 068/319] Bump camino to 1.2.2

---
 src/tools/rust-analyzer/Cargo.lock | 4 ++--
 src/tools/rust-analyzer/Cargo.toml | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock
index d6c6250e13dc..a2a18cf8eeea 100644
--- a/src/tools/rust-analyzer/Cargo.lock
+++ b/src/tools/rust-analyzer/Cargo.lock
@@ -178,9 +178,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "camino"
-version = "1.2.0"
+version = "1.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e1de8bc0aa9e9385ceb3bf0c152e3a9b9544f6c4a912c8ae504e80c1f0368603"
+checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48"
 dependencies = [
  "serde_core",
 ]
diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 8003cb2fba8e..04b513b38b58 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -107,7 +107,7 @@ anyhow = "1.0.98"
 arrayvec = "0.7.6"
 bitflags = "2.9.1"
 cargo_metadata = "0.23.0"
-camino = "1.1.10"
+camino = "1.2.2"
 crossbeam-channel = "0.5.15"
 dissimilar = "1.0.10"
 dot = "0.1.4"

From 0d8aa8991c33f7dec592856760b3a1c32251bfba Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Wed, 14 Jan 2026 09:16:17 +0100
Subject: [PATCH 069/319] fix: Fix path symbol search not respecting re-exports

---
 src/tools/rust-analyzer/crates/hir/src/lib.rs | 17 ++++
 .../crates/ide-db/src/symbol_index.rs         | 97 ++++++++++++++-----
 2 files changed, 91 insertions(+), 23 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/lib.rs b/src/tools/rust-analyzer/crates/hir/src/lib.rs
index 78be5a7e8fa9..252d71fb80a4 100644
--- a/src/tools/rust-analyzer/crates/hir/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/lib.rs
@@ -610,6 +610,23 @@ impl Module {
         res
     }
 
+    pub fn modules_in_scope(&self, db: &dyn HirDatabase, pub_only: bool) -> Vec<(Name, Module)> {
+        let def_map = self.id.def_map(db);
+        let scope = &def_map[self.id].scope;
+
+        let mut res = Vec::new();
+
+        for (name, item) in scope.types() {
+            if let ModuleDefId::ModuleId(m) = item.def
+                && (!pub_only || item.vis == Visibility::Public)
+            {
+                res.push((name.clone(), Module { id: m }));
+            }
+        }
+
+        res
+    }
+
     /// Returns a `ModuleScope`: a set of items, visible in this module.
     pub fn scope(
         self,
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index c95b541748ec..d7f4c66f465b 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -35,6 +35,7 @@ use hir::{
     import_map::{AssocSearchMode, SearchMode},
     symbols::{FileSymbol, SymbolCollector},
 };
+use itertools::Itertools;
 use rayon::prelude::*;
 use salsa::Update;
 
@@ -224,12 +225,10 @@ pub fn world_symbols(db: &RootDatabase, mut query: Query) -> Vec>
     // Search for crates by name (handles "::" and "::foo" queries)
     let indices: Vec<_> = if query.is_crate_search() {
         query.only_types = false;
-        query.libs = true;
         vec![SymbolIndex::extern_prelude_symbols(db)]
         // If we have a path filter, resolve it to target modules
     } else if !query.path_filter.is_empty() {
         query.only_types = false;
-        query.libs = true;
         let target_modules = resolve_path_to_modules(
             db,
             &query.path_filter,
@@ -313,11 +312,11 @@ fn resolve_path_to_modules(
 
     // If anchor_to_crate is true, first segment MUST be a crate name
     // If anchor_to_crate is false, first segment could be a crate OR a module in local crates
-    let mut candidate_modules: Vec = vec![];
+    let mut candidate_modules: Vec<(Module, bool)> = vec![];
 
     // Add crate root modules for matching crates
     for krate in matching_crates {
-        candidate_modules.push(krate.root_module(db));
+        candidate_modules.push((krate.root_module(db), krate.origin(db).is_local()));
     }
 
     // If not anchored to crate, also search for modules matching first segment in local crates
@@ -329,7 +328,7 @@ fn resolve_path_to_modules(
                     if let Some(name) = child.name(db)
                         && names_match(name.as_str(), first_segment)
                     {
-                        candidate_modules.push(child);
+                        candidate_modules.push((child, true));
                     }
                 }
             }
@@ -340,11 +339,14 @@ fn resolve_path_to_modules(
     for segment in rest_segments {
         candidate_modules = candidate_modules
             .into_iter()
-            .flat_map(|module| {
-                module.children(db).filter(|child| {
-                    child.name(db).is_some_and(|name| names_match(name.as_str(), segment))
-                })
+            .flat_map(|(module, local)| {
+                module
+                    .modules_in_scope(db, !local)
+                    .into_iter()
+                    .filter(|(name, _)| names_match(name.as_str(), segment))
+                    .map(move |(_, module)| (module, local))
             })
+            .unique()
             .collect();
 
         if candidate_modules.is_empty() {
@@ -352,7 +354,7 @@ fn resolve_path_to_modules(
         }
     }
 
-    candidate_modules
+    candidate_modules.into_iter().map(|(module, _)| module).collect()
 }
 
 #[derive(Default)]
@@ -839,7 +841,7 @@ pub struct Foo;
         assert_eq!(item, "foo");
         assert!(anchor);
 
-        // Trailing :: (module browsing)
+        // Trailing ::
         let (path, item, anchor) = Query::parse_path_query("foo::");
         assert_eq!(path, vec!["foo"]);
         assert_eq!(item, "");
@@ -909,7 +911,7 @@ pub mod nested {
     }
 
     #[test]
-    fn test_module_browsing() {
+    fn test_path_search_module() {
         let (mut db, _) = RootDatabase::with_many_files(
             r#"
 //- /lib.rs crate:main
@@ -1066,20 +1068,11 @@ pub fn root_fn() {}
         let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
         assert!(names.contains(&"RootItem"), "Expected RootItem at crate root in {:?}", names);
 
-        // Browse crate root
         let query = Query::new("mylib::".to_owned());
         let symbols = world_symbols(&db, query);
         let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
-        assert!(
-            names.contains(&"RootItem"),
-            "Expected RootItem when browsing crate root in {:?}",
-            names
-        );
-        assert!(
-            names.contains(&"root_fn"),
-            "Expected root_fn when browsing crate root in {:?}",
-            names
-        );
+        assert!(names.contains(&"RootItem"), "Expected RootItem {:?}", names);
+        assert!(names.contains(&"root_fn"), "Expected root_fn {:?}", names);
     }
 
     #[test]
@@ -1163,4 +1156,62 @@ pub struct FooStruct;
         let symbols = world_symbols(&db, query);
         assert!(symbols.is_empty(), "Expected empty results for non-matching crate pattern");
     }
+
+    #[test]
+    fn test_path_search_with_use_reexport() {
+        // Test that module resolution works for `use` items (re-exports), not just `mod` items
+        let (mut db, _) = RootDatabase::with_many_files(
+            r#"
+//- /lib.rs crate:main
+mod inner;
+pub use inner::nested;
+
+//- /inner.rs
+pub mod nested {
+    pub struct NestedStruct;
+    pub fn nested_fn() {}
+}
+"#,
+        );
+
+        let mut local_roots = FxHashSet::default();
+        local_roots.insert(WORKSPACE);
+        LocalRoots::get(&db).set_roots(&mut db).to(local_roots);
+
+        // Search via the re-exported path (main::nested::NestedStruct)
+        // This should work because `nested` is in scope via `pub use inner::nested`
+        let query = Query::new("main::nested::NestedStruct".to_owned());
+        let symbols = world_symbols(&db, query);
+        let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
+        assert!(
+            names.contains(&"NestedStruct"),
+            "Expected NestedStruct via re-exported path in {:?}",
+            names
+        );
+
+        // Also verify the original path still works
+        let query = Query::new("main::inner::nested::NestedStruct".to_owned());
+        let symbols = world_symbols(&db, query);
+        let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
+        assert!(
+            names.contains(&"NestedStruct"),
+            "Expected NestedStruct via original path in {:?}",
+            names
+        );
+
+        // Browse the re-exported module
+        let query = Query::new("main::nested::".to_owned());
+        let symbols = world_symbols(&db, query);
+        let names: Vec<_> = symbols.iter().map(|s| s.name.as_str()).collect();
+        assert!(
+            names.contains(&"NestedStruct"),
+            "Expected NestedStruct when browsing re-exported module in {:?}",
+            names
+        );
+        assert!(
+            names.contains(&"nested_fn"),
+            "Expected nested_fn when browsing re-exported module in {:?}",
+            names
+        );
+    }
 }

From 6ecee2a415db5f3abf32414372e8325d3ff47f7b Mon Sep 17 00:00:00 2001
From: Wilfred Hughes 
Date: Wed, 14 Jan 2026 18:26:02 +0000
Subject: [PATCH 070/319] internal: Improve docs for discoverConfig

Add concrete examples of CLI invocations and JSONL outputs, use BUCK
for consistency with the first example, and polish the wording.
---
 .../crates/rust-analyzer/src/config.rs        | 116 +++++++++++-------
 .../docs/book/src/configuration_generated.md  | 116 +++++++++++-------
 .../docs/book/src/non_cargo_based_projects.md |   2 +-
 .../rust-analyzer/editors/code/package.json   |   2 +-
 4 files changed, 142 insertions(+), 94 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 28ac94e4deb6..8d6b19a84caa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -478,14 +478,26 @@ config_data! {
         typing_triggerChars: Option = Some("=.".to_owned()),
 
 
-        /// Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
+        /// Configure a command that rust-analyzer can invoke to
+        /// obtain configuration.
         ///
-        /// [`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.
-        /// `progressLabel` is used for the title in progress indicators, whereas `filesToWatch`
-        /// is used to determine which build system-specific files should be watched in order to
-        /// reload rust-analyzer.
+        /// This is an alternative to manually generating
+        /// `rust-project.json`: it enables rust-analyzer to generate
+        /// rust-project.json on the fly, and regenerate it when
+        /// switching or modifying projects.
+        ///
+        /// This is an object with three fields:
+        ///
+        /// * `command`: the shell command to invoke
+        ///
+        /// * `filesToWatch`: which build system-specific files should
+        /// be watched to trigger regenerating the configuration
+        ///
+        /// * `progressLabel`: the name of the command, used in
+        /// progress indicators in the IDE
+        ///
+        /// Here's an example of a valid configuration:
         ///
-        /// Below is an example of a valid configuration:
         /// ```json
         /// "rust-analyzer.workspace.discoverConfig": {
         ///     "command": [
@@ -500,12 +512,49 @@ config_data! {
         /// }
         /// ```
         ///
-        /// ## Workspace Discovery Protocol
+        /// ## Argument Substitutions
+        ///
+        /// If `command` includes the argument `{arg}`, that argument will be substituted
+        /// with the JSON-serialized form of the following enum:
+        ///
+        /// ```norun
+        /// #[derive(PartialEq, Clone, Debug, Serialize)]
+        /// #[serde(rename_all = "camelCase")]
+        /// pub enum DiscoverArgument {
+        ///    Path(AbsPathBuf),
+        ///    Buildfile(AbsPathBuf),
+        /// }
+        /// ```
+        ///
+        /// rust-analyzer will use the path invocation to find and
+        /// generate a `rust-project.json` and therefore a
+        /// workspace. Example:
+        ///
+        ///
+        /// ```norun
+        /// rust-project develop-json '{ "path": "myproject/src/main.rs" }'
+        /// ```
+        ///
+        /// rust-analyzer will use build file invocations to update an
+        /// existing workspace. Example:
+        ///
+        /// Or with a build file and the configuration above:
+        ///
+        /// ```norun
+        /// rust-project develop-json '{ "buildfile": "myproject/BUCK" }'
+        /// ```
+        ///
+        /// As a reference for implementors, buck2's `rust-project`
+        /// will likely be useful:
+        /// .
+        ///
+        /// ## Discover Command Output
         ///
         /// **Warning**: This format is provisional and subject to change.
         ///
-        /// [`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to
-        /// `DiscoverProjectData::Finished`:
+        /// The discover command should output JSON objects, one per
+        /// line (JSONL format). These objects should correspond to
+        /// this Rust data type:
         ///
         /// ```norun
         /// #[derive(Debug, Clone, Deserialize, Serialize)]
@@ -518,7 +567,14 @@ config_data! {
         /// }
         /// ```
         ///
-        /// As JSON, `DiscoverProjectData::Finished` is:
+        /// For example, a progress event:
+        ///
+        /// ```json
+        /// {"kind":"progress","message":"generating rust-project.json"}
+        /// ```
+        ///
+        /// A finished event can look like this (expanded and
+        /// commented for readability):
         ///
         /// ```json
         /// {
@@ -526,7 +582,7 @@ config_data! {
         ///     "kind": "finished",
         ///     // the file used by a non-Cargo build system to define
         ///     // a package or target.
-        ///     "buildfile": "rust-analyzer/BUILD",
+        ///     "buildfile": "rust-analyzer/BUCK",
         ///     // the contents of a rust-project.json, elided for brevity
         ///     "project": {
         ///         "sysroot": "foo",
@@ -535,41 +591,9 @@ config_data! {
         /// }
         /// ```
         ///
-        /// It is encouraged, but not required, to use the other variants on `DiscoverProjectData`
-        /// to provide a more polished end-user experience.
-        ///
-        /// `DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be
-        /// substituted with the JSON-serialized form of the following enum:
-        ///
-        /// ```norun
-        /// #[derive(PartialEq, Clone, Debug, Serialize)]
-        /// #[serde(rename_all = "camelCase")]
-        /// pub enum DiscoverArgument {
-        ///    Path(AbsPathBuf),
-        ///    Buildfile(AbsPathBuf),
-        /// }
-        /// ```
-        ///
-        /// The JSON representation of `DiscoverArgument::Path` is:
-        ///
-        /// ```json
-        /// {
-        ///     "path": "src/main.rs"
-        /// }
-        /// ```
-        ///
-        /// Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
-        ///
-        /// ```json
-        /// {
-        ///     "buildfile": "BUILD"
-        /// }
-        /// ```
-        ///
-        /// `DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and
-        /// therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an
-        /// existing workspace. As a reference for implementors, buck2's `rust-project` will likely
-        /// be useful: .
+        /// Only the finished event is required, but the other
+        /// variants are encouraged to give users more feedback about
+        /// progress or errors.
         workspace_discoverConfig: Option = None,
     }
 }
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index c4124aaae075..9bc412631039 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -1619,14 +1619,26 @@ though Cargo might be the eventual consumer.
 
 Default: `null`
 
-Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].
+Configure a command that rust-analyzer can invoke to
+obtain configuration.
 
-[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.
-`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`
-is used to determine which build system-specific files should be watched in order to
-reload rust-analyzer.
+This is an alternative to manually generating
+`rust-project.json`: it enables rust-analyzer to generate
+rust-project.json on the fly, and regenerate it when
+switching or modifying projects.
+
+This is an object with three fields:
+
+* `command`: the shell command to invoke
+
+* `filesToWatch`: which build system-specific files should
+be watched to trigger regenerating the configuration
+
+* `progressLabel`: the name of the command, used in
+progress indicators in the IDE
+
+Here's an example of a valid configuration:
 
-Below is an example of a valid configuration:
 ```json
 "rust-analyzer.workspace.discoverConfig": {
     "command": [
@@ -1641,12 +1653,49 @@ Below is an example of a valid configuration:
 }
 ```
 
-## Workspace Discovery Protocol
+## Argument Substitutions
+
+If `command` includes the argument `{arg}`, that argument will be substituted
+with the JSON-serialized form of the following enum:
+
+```norun
+#[derive(PartialEq, Clone, Debug, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub enum DiscoverArgument {
+   Path(AbsPathBuf),
+   Buildfile(AbsPathBuf),
+}
+```
+
+rust-analyzer will use the path invocation to find and
+generate a `rust-project.json` and therefore a
+workspace. Example:
+
+
+```norun
+rust-project develop-json '{ "path": "myproject/src/main.rs" }'
+```
+
+rust-analyzer will use build file invocations to update an
+existing workspace. Example:
+
+Or with a build file and the configuration above:
+
+```norun
+rust-project develop-json '{ "buildfile": "myproject/BUCK" }'
+```
+
+As a reference for implementors, buck2's `rust-project`
+will likely be useful:
+.
+
+## Discover Command Output
 
 **Warning**: This format is provisional and subject to change.
 
-[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to
-`DiscoverProjectData::Finished`:
+The discover command should output JSON objects, one per
+line (JSONL format). These objects should correspond to
+this Rust data type:
 
 ```norun
 #[derive(Debug, Clone, Deserialize, Serialize)]
@@ -1659,7 +1708,14 @@ enum DiscoverProjectData {
 }
 ```
 
-As JSON, `DiscoverProjectData::Finished` is:
+For example, a progress event:
+
+```json
+{"kind":"progress","message":"generating rust-project.json"}
+```
+
+A finished event can look like this (expanded and
+commented for readability):
 
 ```json
 {
@@ -1667,7 +1723,7 @@ As JSON, `DiscoverProjectData::Finished` is:
     "kind": "finished",
     // the file used by a non-Cargo build system to define
     // a package or target.
-    "buildfile": "rust-analyzer/BUILD",
+    "buildfile": "rust-analyzer/BUCK",
     // the contents of a rust-project.json, elided for brevity
     "project": {
         "sysroot": "foo",
@@ -1676,41 +1732,9 @@ As JSON, `DiscoverProjectData::Finished` is:
 }
 ```
 
-It is encouraged, but not required, to use the other variants on `DiscoverProjectData`
-to provide a more polished end-user experience.
-
-`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be
-substituted with the JSON-serialized form of the following enum:
-
-```norun
-#[derive(PartialEq, Clone, Debug, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub enum DiscoverArgument {
-   Path(AbsPathBuf),
-   Buildfile(AbsPathBuf),
-}
-```
-
-The JSON representation of `DiscoverArgument::Path` is:
-
-```json
-{
-    "path": "src/main.rs"
-}
-```
-
-Similarly, the JSON representation of `DiscoverArgument::Buildfile` is:
-
-```json
-{
-    "buildfile": "BUILD"
-}
-```
-
-`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and
-therefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an
-existing workspace. As a reference for implementors, buck2's `rust-project` will likely
-be useful: .
+Only the finished event is required, but the other
+variants are encouraged to give users more feedback about
+progress or errors.
 
 
 ## rust-analyzer.workspace.symbol.search.excludeImports {#workspace.symbol.search.excludeImports}
diff --git a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
index a48b025c7b3a..f1f10ae33653 100644
--- a/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
+++ b/src/tools/rust-analyzer/docs/book/src/non_cargo_based_projects.md
@@ -237,7 +237,7 @@ There are four ways to feed `rust-project.json` to rust-analyzer:
     [`"rust-analyzer.workspace.discoverConfig": … }`](./configuration.md#workspace.discoverConfig)
     to specify a workspace discovery command to generate project descriptions
     on-the-fly. Please note that the command output is message-oriented and must
-    follow [the discovery protocol](./configuration.md#workspace-discovery-protocol).
+    output JSONL [as described in the configuration docs](./configuration.md#workspace.discoverConfig).
 
 -   Place `rust-project.json` file at the root of the project, and
     rust-analyzer will discover it.
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 0d91378706a4..a197b7abd84c 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -3135,7 +3135,7 @@
                 "title": "Workspace",
                 "properties": {
                     "rust-analyzer.workspace.discoverConfig": {
-                        "markdownDescription": "Enables automatic discovery of projects using [`DiscoverWorkspaceConfig::command`].\n\n[`DiscoverWorkspaceConfig`] also requires setting `progressLabel` and `filesToWatch`.\n`progressLabel` is used for the title in progress indicators, whereas `filesToWatch`\nis used to determine which build system-specific files should be watched in order to\nreload rust-analyzer.\n\nBelow is an example of a valid configuration:\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n        \"command\": [\n                \"rust-project\",\n                \"develop-json\",\n                \"{arg}\"\n        ],\n        \"progressLabel\": \"buck2/rust-project\",\n        \"filesToWatch\": [\n                \"BUCK\"\n        ]\n}\n```\n\n## Workspace Discovery Protocol\n\n**Warning**: This format is provisional and subject to change.\n\n[`DiscoverWorkspaceConfig::command`] *must* return a JSON object corresponding to\n`DiscoverProjectData::Finished`:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n        Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n        Error { error: String, source: Option },\n        Progress { message: String },\n}\n```\n\nAs JSON, `DiscoverProjectData::Finished` is:\n\n```json\n{\n        // the internally-tagged representation of the enum.\n        \"kind\": \"finished\",\n        // the file used by a non-Cargo build system to define\n        // a package or target.\n        \"buildfile\": \"rust-analyzer/BUILD\",\n        // the contents of a rust-project.json, elided for brevity\n        \"project\": {\n                \"sysroot\": \"foo\",\n                \"crates\": []\n        }\n}\n```\n\nIt is encouraged, but not required, to use the other variants on `DiscoverProjectData`\nto provide a more polished end-user experience.\n\n`DiscoverWorkspaceConfig::command` may *optionally* include an `{arg}`, which will be\nsubstituted with the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n     Path(AbsPathBuf),\n     Buildfile(AbsPathBuf),\n}\n```\n\nThe JSON representation of `DiscoverArgument::Path` is:\n\n```json\n{\n        \"path\": \"src/main.rs\"\n}\n```\n\nSimilarly, the JSON representation of `DiscoverArgument::Buildfile` is:\n\n```json\n{\n        \"buildfile\": \"BUILD\"\n}\n```\n\n`DiscoverArgument::Path` is used to find and generate a `rust-project.json`, and\ntherefore, a workspace, whereas `DiscoverArgument::buildfile` is used to to update an\nexisting workspace. As a reference for implementors, buck2's `rust-project` will likely\nbe useful: .",
+                        "markdownDescription": "Configure a command that rust-analyzer can invoke to\nobtain configuration.\n\nThis is an alternative to manually generating\n`rust-project.json`: it enables rust-analyzer to generate\nrust-project.json on the fly, and regenerate it when\nswitching or modifying projects.\n\nThis is an object with three fields:\n\n* `command`: the shell command to invoke\n\n* `filesToWatch`: which build system-specific files should\nbe watched to trigger regenerating the configuration\n\n* `progressLabel`: the name of the command, used in\nprogress indicators in the IDE\n\nHere's an example of a valid configuration:\n\n```json\n\"rust-analyzer.workspace.discoverConfig\": {\n        \"command\": [\n                \"rust-project\",\n                \"develop-json\",\n                \"{arg}\"\n        ],\n        \"progressLabel\": \"buck2/rust-project\",\n        \"filesToWatch\": [\n                \"BUCK\"\n        ]\n}\n```\n\n## Argument Substitutions\n\nIf `command` includes the argument `{arg}`, that argument will be substituted\nwith the JSON-serialized form of the following enum:\n\n```norun\n#[derive(PartialEq, Clone, Debug, Serialize)]\n#[serde(rename_all = \"camelCase\")]\npub enum DiscoverArgument {\n     Path(AbsPathBuf),\n     Buildfile(AbsPathBuf),\n}\n```\n\nrust-analyzer will use the path invocation to find and\ngenerate a `rust-project.json` and therefore a\nworkspace. Example:\n\n\n```norun\nrust-project develop-json '{ \"path\": \"myproject/src/main.rs\" }'\n```\n\nrust-analyzer will use build file invocations to update an\nexisting workspace. Example:\n\nOr with a build file and the configuration above:\n\n```norun\nrust-project develop-json '{ \"buildfile\": \"myproject/BUCK\" }'\n```\n\nAs a reference for implementors, buck2's `rust-project`\nwill likely be useful:\n.\n\n## Discover Command Output\n\n**Warning**: This format is provisional and subject to change.\n\nThe discover command should output JSON objects, one per\nline (JSONL format). These objects should correspond to\nthis Rust data type:\n\n```norun\n#[derive(Debug, Clone, Deserialize, Serialize)]\n#[serde(tag = \"kind\")]\n#[serde(rename_all = \"snake_case\")]\nenum DiscoverProjectData {\n        Finished { buildfile: Utf8PathBuf, project: ProjectJsonData },\n        Error { error: String, source: Option },\n        Progress { message: String },\n}\n```\n\nFor example, a progress event:\n\n```json\n{\"kind\":\"progress\",\"message\":\"generating rust-project.json\"}\n```\n\nA finished event can look like this (expanded and\ncommented for readability):\n\n```json\n{\n        // the internally-tagged representation of the enum.\n        \"kind\": \"finished\",\n        // the file used by a non-Cargo build system to define\n        // a package or target.\n        \"buildfile\": \"rust-analyzer/BUCK\",\n        // the contents of a rust-project.json, elided for brevity\n        \"project\": {\n                \"sysroot\": \"foo\",\n                \"crates\": []\n        }\n}\n```\n\nOnly the finished event is required, but the other\nvariants are encouraged to give users more feedback about\nprogress or errors.",
                         "default": null,
                         "anyOf": [
                             {

From c31add03870e80556703be3c9544109328559379 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Thu, 15 Jan 2026 04:21:53 +0000
Subject: [PATCH 071/319] Prepare for merging from rust-lang/rust

This updates the rust-version file to b6fdaf2a15736cbccf248b532f48e33179614d40.
---
 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 b53a66c66751..a6ccd9bab393 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-44a5b55557c26353f388400d7da95527256fe260
+b6fdaf2a15736cbccf248b532f48e33179614d40

From d85965634996e60e80440b4fef7415b6379d2487 Mon Sep 17 00:00:00 2001
From: lummax 
Date: Thu, 15 Jan 2026 12:54:01 +0100
Subject: [PATCH 072/319] fix: lookup flycheck by ID instead of vector index

After a recent introduction of per-package flycheck for JSON projects, the code
assumed that `world.flycheck` indices matched `world.workspaces` indices.
However, not all workspaces have flycheck enabled (e.g., JSON projects
without a flycheck template configured), so the flycheck vector can be
shorter than the workspaces vector.

This caused an index-out-of-bounds panic when saving a file in a JSON
project without flycheck configured:

  thread 'Worker' panicked at notification.rs:
  index out of bounds: the len is 0 but the index is 0

Fix by looking up the flycheck handle by its ID (which is the workspace
index set during spawn) rather than using the workspace index directly
as a vector index.
---
 .../src/handlers/notification.rs              | 23 ++++++++++++-------
 1 file changed, 15 insertions(+), 8 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index d95601043330..6cc40677fb51 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -387,14 +387,21 @@ fn run_flycheck(state: &mut GlobalState, vfs_path: VfsPath) -> bool {
                                         } => false,
                                     });
                                 if let Some(idx) = package_workspace_idx {
-                                    let workspace_deps =
-                                        world.all_workspace_dependencies_for_package(&package);
-                                    world.flycheck[idx].restart_for_package(
-                                        package,
-                                        target,
-                                        workspace_deps,
-                                        saved_file.clone(),
-                                    );
+                                    // flycheck handles are indexed by their ID (which is the workspace index),
+                                    // but not all workspaces have flycheck enabled (e.g., JSON projects without
+                                    // a flycheck template). Find the flycheck handle by its ID.
+                                    if let Some(flycheck) =
+                                        world.flycheck.iter().find(|fc| fc.id() == idx)
+                                    {
+                                        let workspace_deps =
+                                            world.all_workspace_dependencies_for_package(&package);
+                                        flycheck.restart_for_package(
+                                            package,
+                                            target,
+                                            workspace_deps,
+                                            saved_file.clone(),
+                                        );
+                                    }
                                 }
                             }
                         }

From 3387a58bc777d31493f37160cdd1f57223f0bc0c Mon Sep 17 00:00:00 2001
From: James Hendry 
Date: Wed, 14 Jan 2026 10:48:24 +0000
Subject: [PATCH 073/319] fN::BITS constants for feature float_bits_const

Also enables the feature for compiler_builtins as otherwise
this causes a warning and conflicts with the Float extension trait

Explicitly use Float trait BITS in miri tests to
prevent warnings against upcoming BITS field for floats
---
 library/compiler-builtins/compiler-builtins/src/lib.rs | 1 +
 library/core/src/num/f128.rs                           | 5 +++++
 library/core/src/num/f16.rs                            | 5 +++++
 library/core/src/num/f32.rs                            | 4 ++++
 library/core/src/num/f64.rs                            | 4 ++++
 src/tools/miri/tests/pass/float.rs                     | 6 +++---
 src/tools/test-float-parse/src/traits.rs               | 2 +-
 7 files changed, 23 insertions(+), 4 deletions(-)

diff --git a/library/compiler-builtins/compiler-builtins/src/lib.rs b/library/compiler-builtins/compiler-builtins/src/lib.rs
index c993209699be..342427aca9c7 100644
--- a/library/compiler-builtins/compiler-builtins/src/lib.rs
+++ b/library/compiler-builtins/compiler-builtins/src/lib.rs
@@ -11,6 +11,7 @@
 #![feature(repr_simd)]
 #![feature(macro_metavar_expr_concat)]
 #![feature(rustc_attrs)]
+#![feature(float_bits_const)]
 #![cfg_attr(f16_enabled, feature(f16))]
 #![cfg_attr(f128_enabled, feature(f128))]
 #![no_builtins]
diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs
index bf99fee4fc78..8bb919de729e 100644
--- a/library/core/src/num/f128.rs
+++ b/library/core/src/num/f128.rs
@@ -145,6 +145,11 @@ impl f128 {
     #[unstable(feature = "f128", issue = "116909")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    // #[unstable(feature = "f128", issue = "116909")]
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 128;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs
index f39ee22871d5..699b2e3c25c4 100644
--- a/library/core/src/num/f16.rs
+++ b/library/core/src/num/f16.rs
@@ -142,6 +142,11 @@ impl f16 {
     #[unstable(feature = "f16", issue = "116909")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    // #[unstable(feature = "f16", issue = "116909")]
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 16;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs
index 6fe4285374b2..118ef4ff3d43 100644
--- a/library/core/src/num/f32.rs
+++ b/library/core/src/num/f32.rs
@@ -390,6 +390,10 @@ impl f32 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 32;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs
index d0aca152415e..eacbd3ff20bf 100644
--- a/library/core/src/num/f64.rs
+++ b/library/core/src/num/f64.rs
@@ -390,6 +390,10 @@ impl f64 {
     #[stable(feature = "assoc_int_consts", since = "1.43.0")]
     pub const RADIX: u32 = 2;
 
+    /// The size of this float type in bits.
+    #[unstable(feature = "float_bits_const", issue = "151073")]
+    pub const BITS: u32 = 64;
+
     /// Number of significant digits in base 2.
     ///
     /// Note that the size of the mantissa in the bitwise representation is one
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index a74a66d5455a..052c83d1aa31 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -353,7 +353,7 @@ macro_rules! test_ftoi_itof {
             assert_itof(i, f, msg);
         }
 
-        let fbits = <$fty>::BITS;
+        let fbits = <$fty as Float>::BITS;
         let fsig_bits = <$fty>::SIGNIFICAND_BITS;
         let ibits = <$ity>::BITS;
         let imax: $ity = <$ity>::MAX;
@@ -528,9 +528,9 @@ macro_rules! test_ftof {
         assert!((<$f1>::NAN as $f2).is_nan(), "{} -> {} nan", stringify!($f1), stringify!($f2));
 
         let min_sub_casted = <$f1>::from_bits(0x1) as $f2;
-        let min_neg_sub_casted = <$f1>::from_bits(0x1 | 1 << (<$f1>::BITS - 1)) as $f2;
+        let min_neg_sub_casted = <$f1>::from_bits(0x1 | 1 << (<$f1 as Float>::BITS - 1)) as $f2;
 
-        if <$f1>::BITS > <$f2>::BITS {
+        if <$f1 as Float>::BITS > <$f2 as Float>::BITS {
             assert_feq(<$f1>::MAX as $f2, <$f2>::INFINITY, "max -> inf");
             assert_feq(<$f1>::MIN as $f2, <$f2>::NEG_INFINITY, "max -> inf");
             assert_biteq(min_sub_casted, f2zero, "min subnormal -> 0.0");
diff --git a/src/tools/test-float-parse/src/traits.rs b/src/tools/test-float-parse/src/traits.rs
index 65a8721bfa5c..0cb44d297bfb 100644
--- a/src/tools/test-float-parse/src/traits.rs
+++ b/src/tools/test-float-parse/src/traits.rs
@@ -155,7 +155,7 @@ macro_rules! impl_float {
                 const BITS: u32 = <$ity>::BITS;
                 const MAN_BITS: u32 = Self::MANTISSA_DIGITS - 1;
                 const MAN_MASK: Self::Int = (Self::Int::ONE << Self::MAN_BITS) - Self::Int::ONE;
-                const SIGN_MASK: Self::Int = Self::Int::ONE << (Self::BITS-1);
+                const SIGN_MASK: Self::Int = Self::Int::ONE << (::BITS-1);
                 fn from_bits(i: Self::Int) -> Self { Self::from_bits(i) }
                 fn to_bits(self) -> Self::Int { self.to_bits() }
                 fn constants() -> &'static Constants {

From b4781c8fe9f97b582b44d615d2edf2bb114f1f2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Esteban=20K=C3=BCber?= 
Date: Thu, 15 Jan 2026 20:39:14 +0000
Subject: [PATCH 074/319] Use default field values in a few more cases

Makes the use expressions significantly shorter.
---
 .../src/diagnostics/conflict_errors.rs        | 48 ++++++-------------
 compiler/rustc_borrowck/src/lib.rs            |  1 +
 .../rustc_hir_analysis/src/hir_wf_check.rs    | 18 ++-----
 compiler/rustc_hir_analysis/src/lib.rs        |  1 +
 4 files changed, 22 insertions(+), 46 deletions(-)

diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
index 5cfe9db009bf..1b1c72394e75 100644
--- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
+++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs
@@ -2312,12 +2312,12 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
             tcx: TyCtxt<'hir>,
             issue_span: Span,
             expr_span: Span,
-            body_expr: Option<&'hir hir::Expr<'hir>>,
-            loop_bind: Option<&'hir Ident>,
-            loop_span: Option,
-            head_span: Option,
-            pat_span: Option,
-            head: Option<&'hir hir::Expr<'hir>>,
+            body_expr: Option<&'hir hir::Expr<'hir>> = None,
+            loop_bind: Option<&'hir Ident> = None,
+            loop_span: Option = None,
+            head_span: Option = None,
+            pat_span: Option = None,
+            head: Option<&'hir hir::Expr<'hir>> = None,
         }
         impl<'hir> Visitor<'hir> for ExprFinder<'hir> {
             fn visit_expr(&mut self, ex: &'hir hir::Expr<'hir>) {
@@ -2383,17 +2383,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
                 hir::intravisit::walk_expr(self, ex);
             }
         }
-        let mut finder = ExprFinder {
-            tcx,
-            expr_span: span,
-            issue_span,
-            loop_bind: None,
-            body_expr: None,
-            head_span: None,
-            loop_span: None,
-            pat_span: None,
-            head: None,
-        };
+        let mut finder = ExprFinder { tcx, expr_span: span, issue_span, .. };
         finder.visit_expr(tcx.hir_body(body_id).value);
 
         if let Some(body_expr) = finder.body_expr
@@ -2628,13 +2618,13 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
 
         struct ExpressionFinder<'tcx> {
             capture_span: Span,
-            closure_change_spans: Vec,
-            closure_arg_span: Option,
-            in_closure: bool,
-            suggest_arg: String,
+            closure_change_spans: Vec = vec![],
+            closure_arg_span: Option = None,
+            in_closure: bool = false,
+            suggest_arg: String = String::new(),
             tcx: TyCtxt<'tcx>,
-            closure_local_id: Option,
-            closure_call_changes: Vec<(Span, String)>,
+            closure_local_id: Option = None,
+            closure_call_changes: Vec<(Span, String)> = vec![],
         }
         impl<'hir> Visitor<'hir> for ExpressionFinder<'hir> {
             fn visit_expr(&mut self, e: &'hir hir::Expr<'hir>) {
@@ -2715,16 +2705,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
         }) = self.infcx.tcx.hir_node(self.mir_hir_id())
             && let hir::Node::Expr(expr) = self.infcx.tcx.hir_node(body_id.hir_id)
         {
-            let mut finder = ExpressionFinder {
-                capture_span: *capture_kind_span,
-                closure_change_spans: vec![],
-                closure_arg_span: None,
-                in_closure: false,
-                suggest_arg: String::new(),
-                closure_local_id: None,
-                closure_call_changes: vec![],
-                tcx: self.infcx.tcx,
-            };
+            let mut finder =
+                ExpressionFinder { capture_span: *capture_kind_span, tcx: self.infcx.tcx, .. };
             finder.visit_expr(expr);
 
             if finder.closure_change_spans.is_empty() || finder.closure_call_changes.is_empty() {
diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs
index 91defbad0a0e..f2ea8cef192e 100644
--- a/compiler/rustc_borrowck/src/lib.rs
+++ b/compiler/rustc_borrowck/src/lib.rs
@@ -4,6 +4,7 @@
 #![allow(internal_features)]
 #![feature(assert_matches)]
 #![feature(box_patterns)]
+#![feature(default_field_values)]
 #![feature(file_buffered)]
 #![feature(if_let_guard)]
 #![feature(negative_impls)]
diff --git a/compiler/rustc_hir_analysis/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index f879153c5765..d414f4dbcc24 100644
--- a/compiler/rustc_hir_analysis/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -51,12 +51,12 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>(
     struct HirWfCheck<'tcx> {
         tcx: TyCtxt<'tcx>,
         predicate: ty::Predicate<'tcx>,
-        cause: Option>,
-        cause_depth: usize,
+        cause: Option> = None,
+        cause_depth: usize = 0,
         icx: ItemCtxt<'tcx>,
         def_id: LocalDefId,
         param_env: ty::ParamEnv<'tcx>,
-        depth: usize,
+        depth: usize = 0,
     }
 
     impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
@@ -124,16 +124,8 @@ pub(super) fn diagnostic_hir_wf_check<'tcx>(
         }
     }
 
-    let mut visitor = HirWfCheck {
-        tcx,
-        predicate,
-        cause: None,
-        cause_depth: 0,
-        icx,
-        def_id,
-        param_env: tcx.param_env(def_id.to_def_id()),
-        depth: 0,
-    };
+    let param_env = tcx.param_env(def_id.to_def_id());
+    let mut visitor = HirWfCheck { tcx, predicate, icx, def_id, param_env, .. };
 
     // Get the starting `hir::Ty` using our `WellFormedLoc`.
     // We will walk 'into' this type to try to find
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
index a51355adf72f..95f0e641c611 100644
--- a/compiler/rustc_hir_analysis/src/lib.rs
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -59,6 +59,7 @@ This API is completely unstable and subject to change.
 #![allow(rustc::diagnostic_outside_of_impl)]
 #![allow(rustc::untranslatable_diagnostic)]
 #![feature(assert_matches)]
+#![feature(default_field_values)]
 #![feature(gen_blocks)]
 #![feature(if_let_guard)]
 #![feature(iter_intersperse)]

From 4870a5fb692e0fb796a4db6a8259a22b047e14e9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Thu, 1 Jan 2026 09:41:09 +0530
Subject: [PATCH 075/319] add worker abstraction

---
 .../src/bidirectional_protocol.rs             | 12 +--
 .../proc-macro-api/src/legacy_protocol.rs     |  4 +-
 .../crates/proc-macro-api/src/lib.rs          | 17 ++--
 .../crates/proc-macro-api/src/process.rs      | 94 +++++++++++++++++--
 4 files changed, 103 insertions(+), 24 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 5996f882981c..643ba98f5176 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -21,7 +21,7 @@ use crate::{
             serialize_span_data_index_map,
         },
     },
-    process::ProcMacroServerProcess,
+    process::{ProcMacroWorker, SynIO},
     transport::codec::postcard::PostcardProtocol,
     version,
 };
@@ -84,7 +84,7 @@ fn wrap_decode(err: io::Error) -> ServerError {
 }
 
 pub(crate) fn version_check(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::ApiVersionCheck {});
@@ -101,7 +101,7 @@ pub(crate) fn version_check(
 
 /// Enable support for rust-analyzer span mode if the server supports it.
 pub(crate) fn enable_rust_analyzer_spans(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::SetConfig(ServerConfig {
@@ -120,7 +120,7 @@ pub(crate) fn enable_rust_analyzer_spans(
 
 /// Finds proc-macros in a given dynamic library.
 pub(crate) fn find_proc_macros(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     dylib_path: &AbsPath,
     callback: SubCallback<'_>,
 ) -> Result, String>, ServerError> {
@@ -175,7 +175,7 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     })));
 
-    let response_payload = run_request(&proc_macro.process, task, callback)?;
+    let response_payload = run_request(proc_macro.process.as_ref(), task, callback)?;
 
     match response_payload {
         BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it
@@ -205,7 +205,7 @@ pub(crate) fn expand(
 }
 
 fn run_request(
-    srv: &ProcMacroServerProcess,
+    srv: &dyn ProcMacroWorker,
     msg: BidirectionalMessage,
     callback: SubCallback<'_>,
 ) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index aabe5a011851..56bf863a88e9 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -113,7 +113,7 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     };
 
-    let response = send_task(&proc_macro.process, Request::ExpandMacro(Box::new(task)))?;
+    let response = send_task(proc_macro.process.as_ref(), Request::ExpandMacro(Box::new(task)))?;
 
     match response {
         Response::ExpandMacro(it) => Ok(it
@@ -143,7 +143,7 @@ pub(crate) fn expand(
 }
 
 /// Sends a request to the proc-macro server and waits for a response.
-fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result {
+fn send_task(srv: &dyn ProcMacroWorker, req: Request) -> Result {
     if let Some(server_error) = srv.exited() {
         return Err(server_error.clone());
     }
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 01195c10feef..dd0c89103a2b 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
@@ -27,7 +27,10 @@ use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span};
 use std::{fmt, io, sync::Arc, time::SystemTime};
 
 pub use crate::transport::codec::Codec;
-use crate::{bidirectional_protocol::SubCallback, process::ProcMacroServerProcess};
+use crate::{
+    bidirectional_protocol::SubCallback,
+    process::{ProcMacroServerProcess, ProcMacroWorker},
+};
 
 /// The versions of the server protocol
 pub mod version {
@@ -85,7 +88,7 @@ pub struct ProcMacroClient {
     ///
     /// That means that concurrent salsa requests may block each other when expanding proc macros,
     /// which is unfortunate, but simple and good enough for the time being.
-    process: Arc,
+    worker: Arc,
     path: AbsPathBuf,
 }
 
@@ -107,7 +110,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: Arc,
+    process: Arc,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -171,7 +174,7 @@ impl ProcMacroClient {
         version: Option<&Version>,
     ) -> io::Result {
         let process = ProcMacroServerProcess::run(spawn, version, || "".to_owned())?;
-        Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
+        Ok(ProcMacroClient { worker: Arc::new(process), path: process_path.to_owned() })
     }
 
     /// Returns the absolute path to the proc-macro server.
@@ -186,7 +189,7 @@ impl ProcMacroClient {
         callback: Option>,
     ) -> Result, ServerError> {
         let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
-        let macros = self.process.find_proc_macros(&dylib.path, callback)?;
+        let macros = self.worker.find_proc_macros(&dylib.path, callback)?;
 
         let dylib_path = Arc::new(dylib.path);
         let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
@@ -196,7 +199,7 @@ impl ProcMacroClient {
             Ok(macros) => Ok(macros
                 .into_iter()
                 .map(|(name, kind)| ProcMacro {
-                    process: self.process.clone(),
+                    process: self.worker.clone(),
                     name: name.into(),
                     kind,
                     dylib_path: dylib_path.clone(),
@@ -209,7 +212,7 @@ impl ProcMacroClient {
 
     /// Checks if the proc-macro server has exited.
     pub fn exited(&self) -> Option<&ServerError> {
-        self.process.exited()
+        self.worker.exited()
     }
 }
 
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 cd387dad0d0b..a206e9fc5d90 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
@@ -1,8 +1,9 @@
 //! Handle process life-time and message passing for proc-macro client
 
 use std::{
+    fmt::Debug,
     io::{self, BufRead, BufReader, Read, Write},
-    panic::AssertUnwindSafe,
+    panic::{AssertUnwindSafe, RefUnwindSafe},
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
     sync::{Arc, Mutex, OnceLock},
 };
@@ -74,12 +75,79 @@ impl ProcessExit for Process {
 }
 
 /// Maintains the state of the proc-macro server process.
-struct ProcessSrvState {
+pub(crate) struct ProcessSrvState {
     process: Box,
     stdin: Box,
     stdout: Box,
 }
 
+impl ProcMacroWorker for ProcMacroServerProcess {
+    fn find_proc_macros(
+        &self,
+        dylib_path: &AbsPath,
+        callback: Option>,
+    ) -> Result, String>, ServerError> {
+        ProcMacroServerProcess::find_proc_macros(self, dylib_path, callback)
+    }
+
+    fn expand(
+        &self,
+        proc_macro: &ProcMacro,
+        subtree: tt::SubtreeView<'_>,
+        attr: Option>,
+        env: Vec<(String, String)>,
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
+        current_dir: String,
+        callback: Option>,
+    ) -> Result, ServerError> {
+        ProcMacroServerProcess::expand(
+            self,
+            proc_macro,
+            subtree,
+            attr,
+            env,
+            def_site,
+            call_site,
+            mixed_site,
+            current_dir,
+            callback,
+        )
+    }
+
+    fn exited(&self) -> Option<&ServerError> {
+        ProcMacroServerProcess::exited(self)
+    }
+
+    fn version(&self) -> u32 {
+        ProcMacroServerProcess::version(self)
+    }
+
+    fn rust_analyzer_spans(&self) -> bool {
+        ProcMacroServerProcess::rust_analyzer_spans(self)
+    }
+
+    fn enable_rust_analyzer_spans(
+        &self,
+        callback: Option>,
+    ) -> Result {
+        ProcMacroServerProcess::enable_rust_analyzer_spans(self, callback)
+    }
+
+    fn use_postcard(&self) -> bool {
+        ProcMacroServerProcess::use_postcard(self)
+    }
+
+    fn state(&self) -> &Mutex {
+        &self.state
+    }
+
+    fn get_exited(&self) -> &OnceLock> {
+        &self.exited
+    }
+}
+
 impl ProcMacroServerProcess {
     /// Starts the proc-macro server and performs a version check
     pub(crate) fn spawn<'a>(
@@ -291,9 +359,13 @@ impl ProcMacroServerProcess {
             ),
         }
     }
+}
 
+pub(crate) struct SynIO;
+
+impl SynIO {
     pub(crate) fn send_task(
-        &self,
+        proc_macro_worker: &dyn ProcMacroWorker,
         send: impl FnOnce(
             &mut dyn Write,
             &mut dyn BufRead,
@@ -302,7 +374,7 @@ impl ProcMacroServerProcess {
         ) -> Result, ServerError>,
         req: Request,
     ) -> Result {
-        self.with_locked_io::(|writer, reader, buf| {
+        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
             send(writer, reader, req, buf).and_then(|res| {
                 res.ok_or_else(|| {
                     let message = "proc-macro server did not respond with data".to_owned();
@@ -319,10 +391,10 @@ impl ProcMacroServerProcess {
     }
 
     pub(crate) fn with_locked_io(
-        &self,
+        proc_macro_worker: &dyn ProcMacroWorker,
         f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut C::Buf) -> Result,
     ) -> Result {
-        let state = &mut *self.state.lock().unwrap();
+        let state = &mut *proc_macro_worker.state().lock().unwrap();
         let mut buf = C::Buf::default();
 
         f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| {
@@ -330,7 +402,11 @@ impl ProcMacroServerProcess {
                 match state.process.exit_err() {
                     None => e,
                     Some(server_error) => {
-                        self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone()
+                        proc_macro_worker
+                            .get_exited()
+                            .get_or_init(|| AssertUnwindSafe(server_error))
+                            .0
+                            .clone()
                     }
                 }
             } else {
@@ -340,11 +416,11 @@ impl ProcMacroServerProcess {
     }
 
     pub(crate) fn run_bidirectional(
-        &self,
+        proc_macro_worker: &dyn ProcMacroWorker,
         initial: BidirectionalMessage,
         callback: SubCallback<'_>,
     ) -> Result {
-        self.with_locked_io::(|writer, reader, buf| {
+        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
             bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback)
         })
     }

From 98d4496a1e9f03a5193e85304f069757d8032768 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Thu, 1 Jan 2026 13:39:04 +0530
Subject: [PATCH 076/319] add termination flag to procmacroserverprocess

---
 .../crates/proc-macro-api/src/process.rs      | 20 ++++++++++++++++++-
 1 file changed, 19 insertions(+), 1 deletion(-)

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 a206e9fc5d90..efb8e0e84a1f 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
@@ -29,6 +29,7 @@ pub(crate) struct ProcMacroServerProcess {
     protocol: Protocol,
     /// Populated when the server exits.
     exited: OnceLock>,
+    single_use: bool,
 }
 
 impl std::fmt::Debug for ProcMacroServerProcess {
@@ -146,6 +147,10 @@ impl ProcMacroWorker for ProcMacroServerProcess {
     fn get_exited(&self) -> &OnceLock> {
         &self.exited
     }
+
+    fn is_reusable(&self) -> bool {
+        !self.single_use
+    }
 }
 
 impl ProcMacroServerProcess {
@@ -226,6 +231,7 @@ impl ProcMacroServerProcess {
                         }
                     },
                     exited: OnceLock::new(),
+                    single_use,
                 })
             };
             let mut srv = create_srv()?;
@@ -335,7 +341,7 @@ impl ProcMacroServerProcess {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        match self.protocol {
+        let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
                 subtree,
@@ -357,6 +363,18 @@ impl ProcMacroServerProcess {
                 current_dir,
                 callback.expect("callback required for bidirectional protocol"),
             ),
+        };
+
+        if self.is_reusable() {
+            self.terminate();
+        }
+
+        result
+    }
+
+    fn terminate(&self) {
+        if let Ok(mut state) = self.state.lock() {
+            let _ = state.process.child.kill();
         }
     }
 }

From 9fb5d34626d2ce137167e556d53c01f04e410329 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Thu, 1 Jan 2026 16:05:33 +0530
Subject: [PATCH 077/319] add pool of processes

---
 .../src/bidirectional_protocol.rs             |  10 +-
 .../proc-macro-api/src/legacy_protocol.rs     |   2 +-
 .../crates/proc-macro-api/src/lib.rs          |  31 +---
 .../crates/proc-macro-api/src/pool.rs         |  61 ++++++++
 .../crates/proc-macro-api/src/process.rs      | 147 ++++++------------
 5 files changed, 122 insertions(+), 129 deletions(-)
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 643ba98f5176..137f2dafc0de 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -21,7 +21,7 @@ use crate::{
             serialize_span_data_index_map,
         },
     },
-    process::{ProcMacroWorker, SynIO},
+    process::ProcMacroServerProcess,
     transport::codec::postcard::PostcardProtocol,
     version,
 };
@@ -84,7 +84,7 @@ fn wrap_decode(err: io::Error) -> ServerError {
 }
 
 pub(crate) fn version_check(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::ApiVersionCheck {});
@@ -101,7 +101,7 @@ pub(crate) fn version_check(
 
 /// Enable support for rust-analyzer span mode if the server supports it.
 pub(crate) fn enable_rust_analyzer_spans(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     callback: SubCallback<'_>,
 ) -> Result {
     let request = BidirectionalMessage::Request(Request::SetConfig(ServerConfig {
@@ -120,7 +120,7 @@ pub(crate) fn enable_rust_analyzer_spans(
 
 /// Finds proc-macros in a given dynamic library.
 pub(crate) fn find_proc_macros(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     dylib_path: &AbsPath,
     callback: SubCallback<'_>,
 ) -> Result, String>, ServerError> {
@@ -205,7 +205,7 @@ pub(crate) fn expand(
 }
 
 fn run_request(
-    srv: &dyn ProcMacroWorker,
+    srv: &ProcMacroServerProcess,
     msg: BidirectionalMessage,
     callback: SubCallback<'_>,
 ) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 56bf863a88e9..7b546cf7aef6 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -143,7 +143,7 @@ pub(crate) fn expand(
 }
 
 /// Sends a request to the proc-macro server and waits for a response.
-fn send_task(srv: &dyn ProcMacroWorker, req: Request) -> Result {
+fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result {
     if let Some(server_error) = srv.exited() {
         return Err(server_error.clone());
     }
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 dd0c89103a2b..ffae28f92c05 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
@@ -18,6 +18,7 @@ extern crate rustc_driver as _;
 
 pub mod bidirectional_protocol;
 pub mod legacy_protocol;
+pub mod pool;
 pub mod process;
 pub mod transport;
 
@@ -29,7 +30,8 @@ use std::{fmt, io, sync::Arc, time::SystemTime};
 pub use crate::transport::codec::Codec;
 use crate::{
     bidirectional_protocol::SubCallback,
-    process::{ProcMacroServerProcess, ProcMacroWorker},
+    pool::{ProcMacroServerPool, default_pool_size},
+    process::ProcMacroServerProcess,
 };
 
 /// The versions of the server protocol
@@ -88,7 +90,7 @@ pub struct ProcMacroClient {
     ///
     /// That means that concurrent salsa requests may block each other when expanding proc macros,
     /// which is unfortunate, but simple and good enough for the time being.
-    worker: Arc,
+    pool: Arc,
     path: AbsPathBuf,
 }
 
@@ -110,7 +112,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: Arc,
+    process: Arc,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -188,31 +190,12 @@ impl ProcMacroClient {
         dylib: MacroDylib,
         callback: Option>,
     ) -> Result, ServerError> {
-        let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
-        let macros = self.worker.find_proc_macros(&dylib.path, callback)?;
-
-        let dylib_path = Arc::new(dylib.path);
-        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
-            .ok()
-            .and_then(|metadata| metadata.modified().ok());
-        match macros {
-            Ok(macros) => Ok(macros
-                .into_iter()
-                .map(|(name, kind)| ProcMacro {
-                    process: self.worker.clone(),
-                    name: name.into(),
-                    kind,
-                    dylib_path: dylib_path.clone(),
-                    dylib_last_modified,
-                })
-                .collect()),
-            Err(message) => Err(ServerError { message, io: None }),
-        }
+        self.pool.load_dylib(&dylib, callback)
     }
 
     /// Checks if the proc-macro server has exited.
     pub fn exited(&self) -> Option<&ServerError> {
-        self.worker.exited()
+        self.pool.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
new file mode 100644
index 000000000000..685bc05be62a
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs
@@ -0,0 +1,61 @@
+use std::sync::Arc;
+
+use crate::{
+    MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback,
+    process::ProcMacroServerProcess,
+};
+
+#[derive(Debug)]
+pub(crate) struct ProcMacroServerPool {
+    workers: Vec>,
+}
+
+impl ProcMacroServerPool {
+    pub(crate) fn new(workers: Vec>) -> Self {
+        Self { workers }
+    }
+}
+
+impl ProcMacroServerPool {
+    pub(crate) fn exited(&self) -> Option<&ServerError> {
+        for worker in &self.workers {
+            if let Some(e) = worker.exited() {
+                return Some(e);
+            }
+        }
+        None
+    }
+
+    pub(crate) fn load_dylib(
+        &self,
+        dylib: &MacroDylib,
+        _callback: Option>,
+    ) -> Result, ServerError> {
+        let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
+        let mut all_macros = Vec::new();
+
+        for worker in &self.workers {
+            let dylib_path = Arc::new(dylib.path.clone());
+            let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
+                .ok()
+                .and_then(|metadata| metadata.modified().ok());
+            let macros = worker.load_dylib(&dylib.path, None)?;
+
+            for (name, kind) in macros {
+                all_macros.push(ProcMacro {
+                    process: worker.clone(),
+                    name: name.into(),
+                    kind,
+                    dylib_path: Arc::new(dylib.path.clone()),
+                    dylib_last_modified,
+                });
+            }
+        }
+
+        Ok(all_macros)
+    }
+}
+
+pub(crate) fn default_pool_size() -> usize {
+    std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(4)
+}
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 efb8e0e84a1f..30877c5cf491 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
@@ -3,7 +3,7 @@
 use std::{
     fmt::Debug,
     io::{self, BufRead, BufReader, Read, Write},
-    panic::{AssertUnwindSafe, RefUnwindSafe},
+    panic::AssertUnwindSafe,
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
     sync::{Arc, Mutex, OnceLock},
 };
@@ -82,77 +82,6 @@ pub(crate) struct ProcessSrvState {
     stdout: Box,
 }
 
-impl ProcMacroWorker for ProcMacroServerProcess {
-    fn find_proc_macros(
-        &self,
-        dylib_path: &AbsPath,
-        callback: Option>,
-    ) -> Result, String>, ServerError> {
-        ProcMacroServerProcess::find_proc_macros(self, dylib_path, callback)
-    }
-
-    fn expand(
-        &self,
-        proc_macro: &ProcMacro,
-        subtree: tt::SubtreeView<'_>,
-        attr: Option>,
-        env: Vec<(String, String)>,
-        def_site: Span,
-        call_site: Span,
-        mixed_site: Span,
-        current_dir: String,
-        callback: Option>,
-    ) -> Result, ServerError> {
-        ProcMacroServerProcess::expand(
-            self,
-            proc_macro,
-            subtree,
-            attr,
-            env,
-            def_site,
-            call_site,
-            mixed_site,
-            current_dir,
-            callback,
-        )
-    }
-
-    fn exited(&self) -> Option<&ServerError> {
-        ProcMacroServerProcess::exited(self)
-    }
-
-    fn version(&self) -> u32 {
-        ProcMacroServerProcess::version(self)
-    }
-
-    fn rust_analyzer_spans(&self) -> bool {
-        ProcMacroServerProcess::rust_analyzer_spans(self)
-    }
-
-    fn enable_rust_analyzer_spans(
-        &self,
-        callback: Option>,
-    ) -> Result {
-        ProcMacroServerProcess::enable_rust_analyzer_spans(self, callback)
-    }
-
-    fn use_postcard(&self) -> bool {
-        ProcMacroServerProcess::use_postcard(self)
-    }
-
-    fn state(&self) -> &Mutex {
-        &self.state
-    }
-
-    fn get_exited(&self) -> &OnceLock> {
-        &self.exited
-    }
-
-    fn is_reusable(&self) -> bool {
-        !self.single_use
-    }
-}
-
 impl ProcMacroServerProcess {
     /// Starts the proc-macro server and performs a version check
     pub(crate) fn spawn<'a>(
@@ -220,7 +149,11 @@ impl ProcMacroServerProcess {
                 let (process, stdin, stdout) = spawn(format)?;
 
                 io::Result::Ok(ProcMacroServerProcess {
-                    state: Mutex::new(ProcessSrvState { process, stdin, stdout }),
+                    state: Mutex::new(ProcessSrvState {
+                        process,
+                        stdin,
+                        stdout,
+                    }),
                     version: 0,
                     protocol: match format {
                         Some(ProtocolFormat::BidirectionalPostcardPrototype) => {
@@ -271,6 +204,37 @@ impl ProcMacroServerProcess {
         Err(err.unwrap())
     }
 
+    pub(crate) fn load_dylib(
+        &self,
+        dylib_path: &AbsPath,
+        callback: Option>,
+    ) -> Result, ServerError> {
+        let _state = self.state.lock().unwrap();
+
+        // if state.loaded_dylibs.contains(dylib_path) {
+        //     // Already loaded in this worker
+        //     return Ok(Vec::new());
+        // }
+
+        let result = match self.protocol {
+            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
+                legacy_protocol::find_proc_macros(self, dylib_path)?
+            }
+            Protocol::BidirectionalPostcardPrototype { .. } => {
+                let cb = callback.expect("callback required");
+                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)?
+            }
+        };
+
+        match result {
+            Ok(macros) => {
+                // state.loaded_dylibs.insert(dylib_path.to_owned());
+                Ok(macros)
+            }
+            Err(message) => Err(ServerError { message, io: None }),
+        }
+    }
+
     /// Returns the server error if the process has exited.
     pub(crate) fn exited(&self) -> Option<&ServerError> {
         self.exited.get().map(|it| &it.0)
@@ -314,21 +278,6 @@ impl ProcMacroServerProcess {
         }
     }
 
-    /// Finds proc-macros in a given dynamic library.
-    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)
-            }
-        }
-    }
-
     pub(crate) fn expand(
         &self,
         proc_macro: &ProcMacro,
@@ -365,25 +314,25 @@ impl ProcMacroServerProcess {
             ),
         };
 
-        if self.is_reusable() {
+        if !self.is_reusable() {
             self.terminate();
         }
 
         result
     }
 
+    fn is_reusable(&self) -> bool {
+        self.single_use
+    }
+
     fn terminate(&self) {
         if let Ok(mut state) = self.state.lock() {
             let _ = state.process.child.kill();
         }
     }
-}
 
-pub(crate) struct SynIO;
-
-impl SynIO {
     pub(crate) fn send_task(
-        proc_macro_worker: &dyn ProcMacroWorker,
+        &self,
         send: impl FnOnce(
             &mut dyn Write,
             &mut dyn BufRead,
@@ -392,7 +341,7 @@ impl SynIO {
         ) -> Result, ServerError>,
         req: Request,
     ) -> Result {
-        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
+        self.with_locked_io::(|writer, reader, buf| {
             send(writer, reader, req, buf).and_then(|res| {
                 res.ok_or_else(|| {
                     let message = "proc-macro server did not respond with data".to_owned();
@@ -409,10 +358,10 @@ impl SynIO {
     }
 
     pub(crate) fn with_locked_io(
-        proc_macro_worker: &dyn ProcMacroWorker,
+        &self,
         f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut C::Buf) -> Result,
     ) -> Result {
-        let state = &mut *proc_macro_worker.state().lock().unwrap();
+        let state = &mut *self.state.lock().unwrap();
         let mut buf = C::Buf::default();
 
         f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| {
@@ -434,11 +383,11 @@ impl SynIO {
     }
 
     pub(crate) fn run_bidirectional(
-        proc_macro_worker: &dyn ProcMacroWorker,
+        &self,
         initial: BidirectionalMessage,
         callback: SubCallback<'_>,
     ) -> Result {
-        SynIO::with_locked_io::(proc_macro_worker, |writer, reader, buf| {
+        self.with_locked_io::(|writer, reader, buf| {
             bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback)
         })
     }

From c685aa912349b96f21485c434d03ee288cfceb9e Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 11:03:20 +0530
Subject: [PATCH 078/319] direct client calls via pool

---
 .../src/bidirectional_protocol.rs             | 11 +--
 .../proc-macro-api/src/legacy_protocol.rs     | 11 +--
 .../crates/proc-macro-api/src/lib.rs          | 21 +-----
 .../crates/proc-macro-api/src/pool.rs         | 72 ++++++++++++++++---
 .../crates/proc-macro-api/src/process.rs      | 65 ++++++-----------
 5 files changed, 98 insertions(+), 82 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 137f2dafc0de..cd1f6f6f1f33 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -138,6 +138,7 @@ pub(crate) fn find_proc_macros(
 
 pub(crate) fn expand(
     proc_macro: &ProcMacro,
+    process: &ProcMacroServerProcess,
     subtree: tt::SubtreeView<'_>,
     attr: Option>,
     env: Vec<(String, String)>,
@@ -147,7 +148,7 @@ pub(crate) fn expand(
     current_dir: String,
     callback: SubCallback<'_>,
 ) -> Result, crate::ServerError> {
-    let version = proc_macro.process.version();
+    let version = process.version();
     let mut span_data_table = SpanDataIndexMap::default();
     let def_site = span_data_table.insert_full(def_site).0;
     let call_site = span_data_table.insert_full(call_site).0;
@@ -164,7 +165,7 @@ pub(crate) fn expand(
                 call_site,
                 mixed_site,
             },
-            span_data_table: if proc_macro.process.rust_analyzer_spans() {
+            span_data_table: if process.rust_analyzer_spans() {
                 serialize_span_data_index_map(&span_data_table)
             } else {
                 Vec::new()
@@ -175,13 +176,13 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     })));
 
-    let response_payload = run_request(proc_macro.process.as_ref(), task, callback)?;
+    let response_payload = run_request(process, task, callback)?;
 
     match response_payload {
         BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -194,7 +195,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 7b546cf7aef6..412d20730324 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -77,6 +77,7 @@ pub(crate) fn find_proc_macros(
 
 pub(crate) fn expand(
     proc_macro: &ProcMacro,
+    process: &ProcMacroServerProcess,
     subtree: tt::SubtreeView<'_>,
     attr: Option>,
     env: Vec<(String, String)>,
@@ -85,7 +86,7 @@ pub(crate) fn expand(
     mixed_site: Span,
     current_dir: String,
 ) -> Result, crate::ServerError> {
-    let version = proc_macro.process.version();
+    let version = process.version();
     let mut span_data_table = SpanDataIndexMap::default();
     let def_site = span_data_table.insert_full(def_site).0;
     let call_site = span_data_table.insert_full(call_site).0;
@@ -102,7 +103,7 @@ pub(crate) fn expand(
                 call_site,
                 mixed_site,
             },
-            span_data_table: if proc_macro.process.rust_analyzer_spans() {
+            span_data_table: if process.rust_analyzer_spans() {
                 serialize_span_data_index_map(&span_data_table)
             } else {
                 Vec::new()
@@ -113,13 +114,13 @@ pub(crate) fn expand(
         current_dir: Some(current_dir),
     };
 
-    let response = send_task(proc_macro.process.as_ref(), Request::ExpandMacro(Box::new(task)))?;
+    let response = send_task(process, Request::ExpandMacro(Box::new(task)))?;
 
     match response {
         Response::ExpandMacro(it) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -132,7 +133,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change() {
+                if proc_macro.needs_fixup_change(process) {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
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 ffae28f92c05..fe17e14024cc 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
@@ -112,7 +112,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: Arc,
+    process: ProcMacroServerPool,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -126,7 +126,6 @@ impl PartialEq for ProcMacro {
             && self.kind == other.kind
             && self.dylib_path == other.dylib_path
             && self.dylib_last_modified == other.dylib_last_modified
-            && Arc::ptr_eq(&self.process, &other.process)
     }
 }
 
@@ -210,8 +209,8 @@ impl ProcMacro {
         self.kind
     }
 
-    fn needs_fixup_change(&self) -> bool {
-        let version = self.process.version();
+    fn needs_fixup_change(&self, process: &ProcMacroServerProcess) -> bool {
+        let version = process.version();
         (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version)
     }
 
@@ -241,20 +240,6 @@ impl ProcMacro {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        let (mut subtree, mut attr) = (subtree, attr);
-        let (mut subtree_changed, mut attr_changed);
-        if self.needs_fixup_change() {
-            subtree_changed = tt::TopSubtree::from_subtree(subtree);
-            self.change_fixup_to_match_old_server(&mut subtree_changed);
-            subtree = subtree_changed.view();
-
-            if let Some(attr) = &mut attr {
-                attr_changed = tt::TopSubtree::from_subtree(*attr);
-                self.change_fixup_to_match_old_server(&mut attr_changed);
-                *attr = attr_changed.view();
-            }
-        }
-
         self.process.expand(
             self,
             subtree,
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 685bc05be62a..4639374f3e24 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,29 +1,38 @@
 use std::sync::Arc;
 
+use tt::Span;
+
 use crate::{
     MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback,
     process::ProcMacroServerProcess,
 };
 
-#[derive(Debug)]
+#[derive(Debug, Clone)]
 pub(crate) struct ProcMacroServerPool {
-    workers: Vec>,
+    workers: Arc<[ProcMacroServerProcess]>,
 }
 
 impl ProcMacroServerPool {
-    pub(crate) fn new(workers: Vec>) -> Self {
-        Self { workers }
+    pub(crate) fn new(workers: Vec) -> Self {
+        Self { workers: workers.into() }
     }
 }
 
 impl ProcMacroServerPool {
     pub(crate) fn exited(&self) -> Option<&ServerError> {
-        for worker in &self.workers {
-            if let Some(e) = worker.exited() {
-                return Some(e);
+        for worker in &*self.workers {
+            worker.exited()?;
+        }
+        self.workers[0].exited()
+    }
+
+    fn pick_process(&self) -> &ProcMacroServerProcess {
+        for workers in &*self.workers {
+            if workers.can_use() {
+                return workers;
             }
         }
-        None
+        &self.workers[0]
     }
 
     pub(crate) fn load_dylib(
@@ -34,16 +43,16 @@ impl ProcMacroServerPool {
         let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
         let mut all_macros = Vec::new();
 
-        for worker in &self.workers {
+        for worker in &*self.workers {
             let dylib_path = Arc::new(dylib.path.clone());
             let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
                 .ok()
                 .and_then(|metadata| metadata.modified().ok());
-            let macros = worker.load_dylib(&dylib.path, None)?;
+            let macros = worker.find_proc_macros(&dylib.path, None)?.unwrap();
 
             for (name, kind) in macros {
                 all_macros.push(ProcMacro {
-                    process: worker.clone(),
+                    process: self.clone(),
                     name: name.into(),
                     kind,
                     dylib_path: Arc::new(dylib.path.clone()),
@@ -54,6 +63,47 @@ impl ProcMacroServerPool {
 
         Ok(all_macros)
     }
+
+    pub(crate) fn expand(
+        &self,
+        proc_macro: &ProcMacro,
+        subtree: tt::SubtreeView<'_>,
+        attr: Option>,
+        env: Vec<(String, String)>,
+        def_site: Span,
+        call_site: Span,
+        mixed_site: Span,
+        current_dir: String,
+        callback: Option>,
+    ) -> Result, ServerError> {
+        let process = self.pick_process();
+
+        let (mut subtree, mut attr) = (subtree, attr);
+        let (mut subtree_changed, mut attr_changed);
+        if proc_macro.needs_fixup_change(process) {
+            subtree_changed = tt::TopSubtree::from_subtree(subtree);
+            proc_macro.change_fixup_to_match_old_server(&mut subtree_changed);
+            subtree = subtree_changed.view();
+
+            if let Some(attr) = &mut attr {
+                attr_changed = tt::TopSubtree::from_subtree(*attr);
+                proc_macro.change_fixup_to_match_old_server(&mut attr_changed);
+                *attr = attr_changed.view();
+            }
+        }
+
+        process.expand(
+            proc_macro,
+            subtree,
+            attr,
+            env,
+            def_site,
+            call_site,
+            mixed_site,
+            current_dir,
+            callback,
+        )
+    }
 }
 
 pub(crate) fn default_pool_size() -> usize {
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 30877c5cf491..a41bb58e74be 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
@@ -5,7 +5,10 @@ use std::{
     io::{self, BufRead, BufReader, Read, Write},
     panic::AssertUnwindSafe,
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
-    sync::{Arc, Mutex, OnceLock},
+    sync::{
+        Arc, Mutex, OnceLock,
+        atomic::{AtomicBool, Ordering},
+    },
 };
 
 use paths::AbsPath;
@@ -29,7 +32,7 @@ pub(crate) struct ProcMacroServerProcess {
     protocol: Protocol,
     /// Populated when the server exits.
     exited: OnceLock>,
-    single_use: bool,
+    can_use: AtomicBool,
 }
 
 impl std::fmt::Debug for ProcMacroServerProcess {
@@ -149,11 +152,7 @@ impl ProcMacroServerProcess {
                 let (process, stdin, stdout) = spawn(format)?;
 
                 io::Result::Ok(ProcMacroServerProcess {
-                    state: Mutex::new(ProcessSrvState {
-                        process,
-                        stdin,
-                        stdout,
-                    }),
+                    state: Mutex::new(ProcessSrvState { process, stdin, stdout }),
                     version: 0,
                     protocol: match format {
                         Some(ProtocolFormat::BidirectionalPostcardPrototype) => {
@@ -164,7 +163,7 @@ impl ProcMacroServerProcess {
                         }
                     },
                     exited: OnceLock::new(),
-                    single_use,
+                    can_use: AtomicBool::new(true),
                 })
             };
             let mut srv = create_srv()?;
@@ -204,34 +203,20 @@ impl ProcMacroServerProcess {
         Err(err.unwrap())
     }
 
-    pub(crate) fn load_dylib(
+    /// Finds proc-macros in a given dynamic library.
+    pub(crate) fn find_proc_macros(
         &self,
         dylib_path: &AbsPath,
         callback: Option>,
-    ) -> Result, ServerError> {
-        let _state = self.state.lock().unwrap();
-
-        // if state.loaded_dylibs.contains(dylib_path) {
-        //     // Already loaded in this worker
-        //     return Ok(Vec::new());
-        // }
-
-        let result = match self.protocol {
+    ) -> Result, String>, ServerError> {
+        match self.protocol {
             Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::find_proc_macros(self, dylib_path)?
+                legacy_protocol::find_proc_macros(self, dylib_path)
             }
             Protocol::BidirectionalPostcardPrototype { .. } => {
-                let cb = callback.expect("callback required");
-                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)?
+                let cb = callback.expect("callback required for bidirectional protocol");
+                bidirectional_protocol::find_proc_macros(self, dylib_path, cb)
             }
-        };
-
-        match result {
-            Ok(macros) => {
-                // state.loaded_dylibs.insert(dylib_path.to_owned());
-                Ok(macros)
-            }
-            Err(message) => Err(ServerError { message, io: None }),
         }
     }
 
@@ -290,9 +275,11 @@ impl ProcMacroServerProcess {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
+        self.can_use.store(false, Ordering::Release);
         let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
+                    self,
                 subtree,
                 attr,
                 env,
@@ -303,6 +290,7 @@ impl ProcMacroServerProcess {
             ),
             Protocol::BidirectionalPostcardPrototype { .. } => bidirectional_protocol::expand(
                 proc_macro,
+                self,
                 subtree,
                 attr,
                 env,
@@ -314,23 +302,10 @@ impl ProcMacroServerProcess {
             ),
         };
 
-        if !self.is_reusable() {
-            self.terminate();
-        }
-
+        self.can_use.store(true, Ordering::Release);
         result
     }
 
-    fn is_reusable(&self) -> bool {
-        self.single_use
-    }
-
-    fn terminate(&self) {
-        if let Ok(mut state) = self.state.lock() {
-            let _ = state.process.child.kill();
-        }
-    }
-
     pub(crate) fn send_task(
         &self,
         send: impl FnOnce(
@@ -391,6 +366,10 @@ impl ProcMacroServerProcess {
             bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback)
         })
     }
+
+    pub(crate) fn can_use(&self) -> bool {
+        self.can_use.load(Ordering::Acquire)
+    }
 }
 
 /// Manages the execution of the proc-macro server process.

From 82e758acc1a76b2a8d8083121b031fc2b3e3a653 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 11:46:00 +0530
Subject: [PATCH 079/319] add better process picker and improve loading dylib

---
 .../crates/proc-macro-api/src/pool.rs         | 48 ++++++++++---------
 .../crates/proc-macro-api/src/process.rs      | 14 +++---
 2 files changed, 32 insertions(+), 30 deletions(-)

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 4639374f3e24..4215b0f2c04f 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
@@ -27,12 +27,10 @@ impl ProcMacroServerPool {
     }
 
     fn pick_process(&self) -> &ProcMacroServerProcess {
-        for workers in &*self.workers {
-            if workers.can_use() {
-                return workers;
-            }
-        }
-        &self.workers[0]
+        self.workers
+            .iter()
+            .min_by_key(|w| w.number_of_active_req())
+            .expect("worker pool must not be empty")
     }
 
     pub(crate) fn load_dylib(
@@ -41,27 +39,31 @@ impl ProcMacroServerPool {
         _callback: Option>,
     ) -> Result, ServerError> {
         let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
-        let mut all_macros = Vec::new();
 
-        for worker in &*self.workers {
-            let dylib_path = Arc::new(dylib.path.clone());
-            let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
-                .ok()
-                .and_then(|metadata| metadata.modified().ok());
-            let macros = worker.find_proc_macros(&dylib.path, None)?.unwrap();
+        let dylib_path = Arc::new(dylib.path.clone());
+        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
+            .ok()
+            .and_then(|metadata| metadata.modified().ok());
 
-            for (name, kind) in macros {
-                all_macros.push(ProcMacro {
-                    process: self.clone(),
-                    name: name.into(),
-                    kind,
-                    dylib_path: Arc::new(dylib.path.clone()),
-                    dylib_last_modified,
-                });
-            }
+        let first = &self.workers[0];
+        let macros = first.find_proc_macros(&dylib.path, None)?.unwrap();
+
+        for worker in &self.workers[1..] {
+            let _ = worker.find_proc_macros(&dylib.path, None)?;
         }
 
-        Ok(all_macros)
+        let result = macros
+            .into_iter()
+            .map(|(name, kind)| ProcMacro {
+                process: self.clone(),
+                name: name.into(),
+                kind,
+                dylib_path: dylib_path.clone(),
+                dylib_last_modified,
+            })
+            .collect();
+
+        Ok(result)
     }
 
     pub(crate) fn expand(
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 a41bb58e74be..775d59174f1c 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
@@ -7,7 +7,7 @@ use std::{
     process::{Child, ChildStdin, ChildStdout, Command, Stdio},
     sync::{
         Arc, Mutex, OnceLock,
-        atomic::{AtomicBool, Ordering},
+        atomic::{AtomicU32, Ordering},
     },
 };
 
@@ -32,7 +32,7 @@ pub(crate) struct ProcMacroServerProcess {
     protocol: Protocol,
     /// Populated when the server exits.
     exited: OnceLock>,
-    can_use: AtomicBool,
+    active: AtomicU32,
 }
 
 impl std::fmt::Debug for ProcMacroServerProcess {
@@ -163,7 +163,7 @@ impl ProcMacroServerProcess {
                         }
                     },
                     exited: OnceLock::new(),
-                    can_use: AtomicBool::new(true),
+                    active: AtomicU32::new(0),
                 })
             };
             let mut srv = create_srv()?;
@@ -275,7 +275,7 @@ impl ProcMacroServerProcess {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        self.can_use.store(false, Ordering::Release);
+        self.active.fetch_add(1, Ordering::AcqRel);
         let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
@@ -302,7 +302,7 @@ impl ProcMacroServerProcess {
             ),
         };
 
-        self.can_use.store(true, Ordering::Release);
+        self.active.fetch_sub(1, Ordering::AcqRel);
         result
     }
 
@@ -367,8 +367,8 @@ impl ProcMacroServerProcess {
         })
     }
 
-    pub(crate) fn can_use(&self) -> bool {
-        self.can_use.load(Ordering::Acquire)
+    pub(crate) fn number_of_active_req(&self) -> u32 {
+        self.active.load(Ordering::Acquire)
     }
 }
 

From 922bc7e4d49da5656c901e0a2d4a9805f413dcf8 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 11:56:19 +0530
Subject: [PATCH 080/319] rename process to pool in ProcMacro struct

---
 src/tools/rust-analyzer/crates/proc-macro-api/src/lib.rs  | 4 ++--
 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

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 fe17e14024cc..09999ea5081a 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
@@ -112,7 +112,7 @@ impl MacroDylib {
 /// we share a single expander process for all macros within a workspace.
 #[derive(Debug, Clone)]
 pub struct ProcMacro {
-    process: ProcMacroServerPool,
+    pool: ProcMacroServerPool,
     dylib_path: Arc,
     name: Box,
     kind: ProcMacroKind,
@@ -240,7 +240,7 @@ impl ProcMacro {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        self.process.expand(
+        self.pool.expand(
             self,
             subtree,
             attr,
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 4215b0f2c04f..eef8d0194dc5 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
@@ -55,7 +55,7 @@ impl ProcMacroServerPool {
         let result = macros
             .into_iter()
             .map(|(name, kind)| ProcMacro {
-                process: self.clone(),
+                pool: self.clone(),
                 name: name.into(),
                 kind,
                 dylib_path: dylib_path.clone(),

From c4c336ad7c291686542826bfd5b0bc8f2e298777 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 2 Jan 2026 12:11:11 +0530
Subject: [PATCH 081/319] keep it clean and tidy

---
 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs | 1 +
 1 file changed, 1 insertion(+)

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 eef8d0194dc5..fe4649441b89 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,3 +1,4 @@
+//! This module represents Process Pool
 use std::sync::Arc;
 
 use tt::Span;

From c8a3551bd162c6374d4c49bfc06070488edb44ed Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sat, 3 Jan 2026 11:32:08 +0530
Subject: [PATCH 082/319] change callback from FnMut to Fn as we only transform
 messages and not really change change state

---
 src/tools/rust-analyzer/crates/load-cargo/src/lib.rs        | 6 +++---
 .../crates/proc-macro-api/src/bidirectional_protocol.rs     | 2 +-
 .../rust-analyzer/crates/proc-macro-api/src/process.rs      | 4 ++--
 3 files changed, 6 insertions(+), 6 deletions(-)

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 8342492a33a4..ccc9aa4291ec 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -435,7 +435,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(&mut reject_subrequests)).map_err(|e| {
+        let vec = server.load_dylib(dylib, Some(&reject_subrequests)).map_err(|e| {
             ProcMacroLoadingError::ProcMacroSrvError(format!("{e}").into_boxed_str())
         })?;
         if vec.is_empty() {
@@ -541,7 +541,7 @@ impl ProcMacroExpander for Expander {
         mixed_site: Span,
         current_dir: String,
     ) -> Result {
-        let mut cb = |req| match req {
+        let cb = |req| match req {
             SubRequest::LocalFilePath { file_id } => {
                 let file_id = FileId::from_raw(file_id);
                 let source_root_id = db.file_source_root(file_id).source_root_id(db);
@@ -613,7 +613,7 @@ impl ProcMacroExpander for Expander {
             call_site,
             mixed_site,
             current_dir,
-            Some(&mut cb),
+            Some(&cb),
         ) {
             Ok(Ok(subtree)) => Ok(subtree),
             Ok(Err(err)) => Err(ProcMacroExpansionError::Panic(err)),
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index cd1f6f6f1f33..25266c46fe89 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -28,7 +28,7 @@ use crate::{
 
 pub mod msg;
 
-pub type SubCallback<'a> = &'a mut dyn FnMut(SubRequest) -> Result;
+pub type SubCallback<'a> = &'a dyn Fn(SubRequest) -> Result;
 
 pub fn run_conversation(
     writer: &mut dyn Write,
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 775d59174f1c..c1b95fa7f10e 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
@@ -168,7 +168,7 @@ impl ProcMacroServerProcess {
             };
             let mut srv = create_srv()?;
             tracing::info!("sending proc-macro server version check");
-            match srv.version_check(Some(&mut reject_subrequests)) {
+            match srv.version_check(Some(&reject_subrequests)) {
                 Ok(v) if v > version::CURRENT_API_VERSION => {
                     let process_version = binary_server_version();
                     err = Some(io::Error::other(format!(
@@ -182,7 +182,7 @@ impl ProcMacroServerProcess {
                     srv.version = v;
                     if srv.version >= version::RUST_ANALYZER_SPAN_SUPPORT
                         && let Ok(new_mode) =
-                            srv.enable_rust_analyzer_spans(Some(&mut reject_subrequests))
+                            srv.enable_rust_analyzer_spans(Some(&reject_subrequests))
                     {
                         match &mut srv.protocol {
                             Protocol::LegacyJson { mode }

From 66bca6a25214332ff559acb7678d3ce423279e77 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sat, 3 Jan 2026 11:36:09 +0530
Subject: [PATCH 083/319] propagate error from load dylibs

---
 .../crates/proc-macro-api/src/pool.rs         | 28 ++++++++++---------
 1 file changed, 15 insertions(+), 13 deletions(-)

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 fe4649441b89..20389f666883 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
@@ -37,23 +37,27 @@ impl ProcMacroServerPool {
     pub(crate) fn load_dylib(
         &self,
         dylib: &MacroDylib,
-        _callback: Option>,
+        callback: Option>,
     ) -> Result, ServerError> {
-        let _p = tracing::info_span!("ProcMacroServer::load_dylib").entered();
+        let _span = tracing::info_span!("ProcMacroServer::load_dylib").entered();
 
         let dylib_path = Arc::new(dylib.path.clone());
-        let dylib_last_modified = std::fs::metadata(dylib_path.as_path())
-            .ok()
-            .and_then(|metadata| metadata.modified().ok());
+        let dylib_last_modified =
+            std::fs::metadata(dylib_path.as_path()).ok().and_then(|m| m.modified().ok());
 
-        let first = &self.workers[0];
-        let macros = first.find_proc_macros(&dylib.path, None)?.unwrap();
+        let (first, rest) = self.workers.split_first().expect("worker pool must not be empty");
 
-        for worker in &self.workers[1..] {
-            let _ = worker.find_proc_macros(&dylib.path, None)?;
+        let macros = first
+            .find_proc_macros(&dylib.path, callback)?
+            .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 })?;
         }
 
-        let result = macros
+        Ok(macros
             .into_iter()
             .map(|(name, kind)| ProcMacro {
                 pool: self.clone(),
@@ -62,9 +66,7 @@ impl ProcMacroServerPool {
                 dylib_path: dylib_path.clone(),
                 dylib_last_modified,
             })
-            .collect();
-
-        Ok(result)
+            .collect())
     }
 
     pub(crate) fn expand(

From 09c91b79a84faebb2e06094f8c8d1592a371a3e4 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sat, 3 Jan 2026 13:43:23 +0530
Subject: [PATCH 084/319] pick workers which have not exited

---
 .../rust-analyzer/crates/proc-macro-api/src/pool.rs    | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

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 20389f666883..c75e9742a5d1 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
@@ -27,11 +27,15 @@ impl ProcMacroServerPool {
         self.workers[0].exited()
     }
 
-    fn pick_process(&self) -> &ProcMacroServerProcess {
+    fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
         self.workers
             .iter()
+            .filter(|w| w.exited().is_none())
             .min_by_key(|w| w.number_of_active_req())
-            .expect("worker pool must not be empty")
+            .ok_or_else(|| ServerError {
+                message: "all proc-macro server workers have exited".into(),
+                io: None,
+            })
     }
 
     pub(crate) fn load_dylib(
@@ -81,7 +85,7 @@ impl ProcMacroServerPool {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
-        let process = self.pick_process();
+        let process = self.pick_process()?;
 
         let (mut subtree, mut attr) = (subtree, attr);
         let (mut subtree_changed, mut attr_changed);

From 0936597b3ea2944869d1a0b2058746e295eeb16d Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 4 Jan 2026 05:02:15 +0530
Subject: [PATCH 085/319] add version to pool

---
 .../src/bidirectional_protocol.rs             |  4 ++--
 .../proc-macro-api/src/legacy_protocol.rs     |  4 ++--
 .../crates/proc-macro-api/src/lib.rs          | 18 ++++++++++++--
 .../crates/proc-macro-api/src/pool.rs         | 24 +++++++------------
 4 files changed, 28 insertions(+), 22 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index 25266c46fe89..b5f43e1d3726 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -182,7 +182,7 @@ pub(crate) fn expand(
         BidirectionalMessage::Response(Response::ExpandMacro(it)) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -195,7 +195,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index 412d20730324..eedf66d46086 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -120,7 +120,7 @@ pub(crate) fn expand(
         Response::ExpandMacro(it) => Ok(it
             .map(|tree| {
                 let mut expanded = FlatTree::to_subtree_resolved(tree, version, &span_data_table);
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
@@ -133,7 +133,7 @@ pub(crate) fn expand(
                     version,
                     &deserialize_span_data_index_map(&resp.span_data_table),
                 );
-                if proc_macro.needs_fixup_change(process) {
+                if proc_macro.needs_fixup_change() {
                     proc_macro.change_fixup_to_match_old_server(&mut expanded);
                 }
                 expanded
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 09999ea5081a..4874e63244c4 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
@@ -209,8 +209,8 @@ impl ProcMacro {
         self.kind
     }
 
-    fn needs_fixup_change(&self, process: &ProcMacroServerProcess) -> bool {
-        let version = process.version();
+    fn needs_fixup_change(&self) -> bool {
+        let version = self.pool.version();
         (version::RUST_ANALYZER_SPAN_SUPPORT..version::HASHED_AST_ID).contains(&version)
     }
 
@@ -240,6 +240,20 @@ impl ProcMacro {
         current_dir: String,
         callback: Option>,
     ) -> Result, ServerError> {
+        let (mut subtree, mut attr) = (subtree, attr);
+        let (mut subtree_changed, mut attr_changed);
+        if self.needs_fixup_change() {
+            subtree_changed = tt::TopSubtree::from_subtree(subtree);
+            self.change_fixup_to_match_old_server(&mut subtree_changed);
+            subtree = subtree_changed.view();
+
+            if let Some(attr) = &mut attr {
+                attr_changed = tt::TopSubtree::from_subtree(*attr);
+                self.change_fixup_to_match_old_server(&mut attr_changed);
+                *attr = attr_changed.view();
+            }
+        }
+
         self.pool.expand(
             self,
             subtree,
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 c75e9742a5d1..fd8b726f820e 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,4 +1,4 @@
-//! This module represents Process Pool
+//! A pool of proc-macro server processes
 use std::sync::Arc;
 
 use tt::Span;
@@ -11,11 +11,13 @@ use crate::{
 #[derive(Debug, Clone)]
 pub(crate) struct ProcMacroServerPool {
     workers: Arc<[ProcMacroServerProcess]>,
+    version: u32,
 }
 
 impl ProcMacroServerPool {
     pub(crate) fn new(workers: Vec) -> Self {
-        Self { workers: workers.into() }
+        let version = workers[0].version();
+        Self { workers: workers.into(), version }
     }
 }
 
@@ -87,20 +89,6 @@ impl ProcMacroServerPool {
     ) -> Result, ServerError> {
         let process = self.pick_process()?;
 
-        let (mut subtree, mut attr) = (subtree, attr);
-        let (mut subtree_changed, mut attr_changed);
-        if proc_macro.needs_fixup_change(process) {
-            subtree_changed = tt::TopSubtree::from_subtree(subtree);
-            proc_macro.change_fixup_to_match_old_server(&mut subtree_changed);
-            subtree = subtree_changed.view();
-
-            if let Some(attr) = &mut attr {
-                attr_changed = tt::TopSubtree::from_subtree(*attr);
-                proc_macro.change_fixup_to_match_old_server(&mut attr_changed);
-                *attr = attr_changed.view();
-            }
-        }
-
         process.expand(
             proc_macro,
             subtree,
@@ -113,6 +101,10 @@ impl ProcMacroServerPool {
             callback,
         )
     }
+
+    pub(crate) fn version(&self) -> u32 {
+        self.version
+    }
 }
 
 pub(crate) fn default_pool_size() -> usize {

From 263015a4a4d4cbbbc1e7cd0e118c10e0a8e740df Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 4 Jan 2026 05:50:16 +0530
Subject: [PATCH 086/319] remove expand from pool

---
 .../crates/proc-macro-api/src/lib.rs          |  2 +-
 .../crates/proc-macro-api/src/pool.rs         | 31 +------------------
 2 files changed, 2 insertions(+), 31 deletions(-)

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 4874e63244c4..ad462ff31a1b 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
@@ -254,7 +254,7 @@ impl ProcMacro {
             }
         }
 
-        self.pool.expand(
+        self.pool.pick_process()?.expand(
             self,
             subtree,
             attr,
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 fd8b726f820e..13a4b5ee8f9e 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,8 +1,6 @@
 //! A pool of proc-macro server processes
 use std::sync::Arc;
 
-use tt::Span;
-
 use crate::{
     MacroDylib, ProcMacro, ServerError, bidirectional_protocol::SubCallback,
     process::ProcMacroServerProcess,
@@ -29,7 +27,7 @@ impl ProcMacroServerPool {
         self.workers[0].exited()
     }
 
-    fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
+    pub(crate) fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
         self.workers
             .iter()
             .filter(|w| w.exited().is_none())
@@ -75,33 +73,6 @@ impl ProcMacroServerPool {
             .collect())
     }
 
-    pub(crate) fn expand(
-        &self,
-        proc_macro: &ProcMacro,
-        subtree: tt::SubtreeView<'_>,
-        attr: Option>,
-        env: Vec<(String, String)>,
-        def_site: Span,
-        call_site: Span,
-        mixed_site: Span,
-        current_dir: String,
-        callback: Option>,
-    ) -> Result, ServerError> {
-        let process = self.pick_process()?;
-
-        process.expand(
-            proc_macro,
-            subtree,
-            attr,
-            env,
-            def_site,
-            call_site,
-            mixed_site,
-            current_dir,
-            callback,
-        )
-    }
-
     pub(crate) fn version(&self) -> u32 {
         self.version
     }

From e3e7c2905442499a6c3eb778be14730dc16d82af Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:41:39 +0530
Subject: [PATCH 087/319] remove default pool size from pool

---
 src/tools/rust-analyzer/crates/proc-macro-api/src/pool.rs | 4 ----
 1 file changed, 4 deletions(-)

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 13a4b5ee8f9e..0cb505aa40a9 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
@@ -77,7 +77,3 @@ impl ProcMacroServerPool {
         self.version
     }
 }
-
-pub(crate) fn default_pool_size() -> usize {
-    std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1).min(4)
-}

From 96ecd1773c56e11def32667a3d70f4b29563f137 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:44:15 +0530
Subject: [PATCH 088/319] add num process in config

---
 .../crates/rust-analyzer/src/config.rs          | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 28ac94e4deb6..cb6552c32ffb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -380,6 +380,8 @@ config_data! {
         /// The number of worker threads in the main loop. The default `null` means to pick
         /// automatically.
         numThreads: Option = None,
+        /// The number of proc-macro-srv processes 
+        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
         procMacro_attributes_enable: bool = true,
@@ -2641,6 +2643,13 @@ impl Config {
         }
     }
 
+    pub fn proc_macro_num_processes(&self) -> usize {
+        match self.proc_macro_processes() {
+            NumProcesses::Concrete(0) | NumProcesses::Physical => num_cpus::get_physical(),
+            &NumProcesses::Concrete(n) => n,
+        }
+    }
+
     pub fn main_loop_num_threads(&self) -> usize {
         match self.numThreads() {
             Some(NumThreads::Concrete(0)) | None | Some(NumThreads::Physical) => {
@@ -3077,6 +3086,14 @@ pub enum NumThreads {
     Concrete(usize),
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
+#[serde(rename_all = "snake_case")]
+pub enum NumProcesses {
+    Physical,
+    #[serde(untagged)]
+    Concrete(usize),
+}
+
 macro_rules! _default_val {
     ($default:expr, $ty:ty) => {{
         let default_: $ty = $default;

From 9d5e60005addf5eb8635884f051771a8119f0bb2 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:44:44 +0530
Subject: [PATCH 089/319] add proc_macro_processes in load config

---
 .../crates/load-cargo/src/lib.rs              | 24 ++++++++++++++-----
 1 file changed, 18 insertions(+), 6 deletions(-)

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 ccc9aa4291ec..c2935d94a8a7 100644
--- a/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/load-cargo/src/lib.rs
@@ -45,6 +45,7 @@ pub struct LoadCargoConfig {
     pub load_out_dirs_from_check: bool,
     pub with_proc_macro_server: ProcMacroServerChoice,
     pub prefill_caches: bool,
+    pub proc_macro_processes: usize,
 }
 
 #[derive(Debug, Clone, PartialEq, Eq)]
@@ -113,15 +114,25 @@ pub fn load_workspace_into_db(
     let proc_macro_server = match &load_config.with_proc_macro_server {
         ProcMacroServerChoice::Sysroot => ws.find_sysroot_proc_macro_srv().map(|it| {
             it.and_then(|it| {
-                ProcMacroClient::spawn(&it, extra_env, ws.toolchain.as_ref()).map_err(Into::into)
+                ProcMacroClient::spawn(
+                    &it,
+                    extra_env,
+                    ws.toolchain.as_ref(),
+                    load_config.proc_macro_processes,
+                )
+                .map_err(Into::into)
             })
             .map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str()))
         }),
-        ProcMacroServerChoice::Explicit(path) => {
-            Some(ProcMacroClient::spawn(path, extra_env, ws.toolchain.as_ref()).map_err(|e| {
-                ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())
-            }))
-        }
+        ProcMacroServerChoice::Explicit(path) => Some(
+            ProcMacroClient::spawn(
+                path,
+                extra_env,
+                ws.toolchain.as_ref(),
+                load_config.proc_macro_processes,
+            )
+            .map_err(|e| ProcMacroLoadingError::ProcMacroSrvError(e.to_string().into_boxed_str())),
+        ),
         ProcMacroServerChoice::None => Some(Err(ProcMacroLoadingError::Disabled)),
     };
     match &proc_macro_server {
@@ -657,6 +668,7 @@ mod tests {
             load_out_dirs_from_check: false,
             with_proc_macro_server: ProcMacroServerChoice::None,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (db, _vfs, _proc_macro) =
             load_workspace_at(path, &cargo_config, &load_cargo_config, &|_| {}).unwrap();

From 721361f2898b4e3c299e3687b8b4581b2e6c2b48 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:45:06 +0530
Subject: [PATCH 090/319] update all cli workflows

---
 .../crates/proc-macro-api/src/lib.rs          |  5 ++---
 .../rust-analyzer/src/cli/analysis_stats.rs   |  1 +
 .../rust-analyzer/src/cli/diagnostics.rs      |  1 +
 .../crates/rust-analyzer/src/cli/lsif.rs      |  1 +
 .../rust-analyzer/src/cli/prime_caches.rs     |  1 +
 .../crates/rust-analyzer/src/cli/run_tests.rs |  1 +
 .../rust-analyzer/src/cli/rustc_tests.rs      |  1 +
 .../crates/rust-analyzer/src/cli/scip.rs      |  1 +
 .../crates/rust-analyzer/src/cli/ssr.rs       |  2 ++
 .../src/cli/unresolved_references.rs          |  1 +
 .../src/integrated_benchmarks.rs              |  3 +++
 .../crates/rust-analyzer/src/reload.rs        | 20 +++++++++++--------
 12 files changed, 27 insertions(+), 11 deletions(-)

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 ad462ff31a1b..2c0008ae1d82 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
@@ -29,9 +29,7 @@ use std::{fmt, io, sync::Arc, time::SystemTime};
 
 pub use crate::transport::codec::Codec;
 use crate::{
-    bidirectional_protocol::SubCallback,
-    pool::{ProcMacroServerPool, default_pool_size},
-    process::ProcMacroServerProcess,
+    bidirectional_protocol::SubCallback, pool::ProcMacroServerPool, process::ProcMacroServerProcess,
 };
 
 /// The versions of the server protocol
@@ -155,6 +153,7 @@ impl ProcMacroClient {
             Item = (impl AsRef, &'a Option>),
         > + Clone,
         version: Option<&Version>,
+        num_process: usize,
     ) -> io::Result {
         let process = ProcMacroServerProcess::spawn(process_path, env, version)?;
         Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
index a02d1a78564f..1995d3889891 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/analysis_stats.rs
@@ -91,6 +91,7 @@ impl flags::AnalysisStats {
                 }
             },
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
 
         let build_scripts_time = if self.disable_build_scripts {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
index 776069f155f0..575c77f8428c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/diagnostics.rs
@@ -41,6 +41,7 @@ impl flags::Diagnostics {
             load_out_dirs_from_check: !self.disable_build_scripts,
             with_proc_macro_server,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (db, _vfs, _proc_macro) =
             load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
index f3b0699d5515..e5e238db6361 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/lsif.rs
@@ -293,6 +293,7 @@ impl flags::Lsif {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let path = AbsPathBuf::assert_utf8(env::current_dir()?.join(self.path));
         let root = ProjectManifest::discover_single(&path)?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs
index 467d8a53884a..d5da6791797b 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/prime_caches.rs
@@ -38,6 +38,7 @@ impl flags::PrimeCaches {
             // we want to ensure that this command, not `load_workspace_at`,
             // is responsible for that work.
             prefill_caches: false,
+            proc_macro_processes: config.proc_macro_num_processes(),
         };
 
         let root = AbsPathBuf::assert_utf8(std::env::current_dir()?.join(root));
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
index 82ace8c8b315..d4a56d773e7d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/run_tests.rs
@@ -23,6 +23,7 @@ impl flags::RunTests {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (ref db, _vfs, _proc_macro) =
             load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
index 249566d2ac16..e8c6c5f4d4f7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/rustc_tests.rs
@@ -103,6 +103,7 @@ impl Tester {
             load_out_dirs_from_check: false,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (db, _vfs, _proc_macro) =
             load_workspace(workspace, &cargo_config.extra_env, &load_cargo_config)?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
index 271d2507bcfe..ed0476697c9c 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/scip.rs
@@ -52,6 +52,7 @@ impl flags::Scip {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: true,
+            proc_macro_processes: config.proc_macro_num_processes(),
         };
         let cargo_config = config.cargo(None);
         let (db, vfs, _) = load_workspace_at(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
index 39186831459c..5c69bda723fb 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/ssr.rs
@@ -20,6 +20,7 @@ impl flags::Ssr {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (ref db, vfs, _proc_macro) = load_workspace_at(
             &std::env::current_dir()?,
@@ -56,6 +57,7 @@ impl flags::Search {
             load_out_dirs_from_check: true,
             with_proc_macro_server: ProcMacroServerChoice::Sysroot,
             prefill_caches: false,
+            proc_macro_processes: 1,
         };
         let (ref db, _vfs, _proc_macro) = load_workspace_at(
             &std::env::current_dir()?,
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
index 294add682d01..49c6fcb91ebf 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/cli/unresolved_references.rs
@@ -44,6 +44,7 @@ impl flags::UnresolvedReferences {
             load_out_dirs_from_check: !self.disable_build_scripts,
             with_proc_macro_server,
             prefill_caches: false,
+            proc_macro_processes: config.proc_macro_num_processes(),
         };
         let (db, vfs, _proc_macro) =
             load_workspace_at(&self.path, &cargo_config, &load_cargo_config, &|_| {})?;
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
index c61825b99fec..d16ca2fb48ac 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/integrated_benchmarks.rs
@@ -53,6 +53,7 @@ fn integrated_highlighting_benchmark() {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: false,
+        proc_macro_processes: 1,
     };
 
     let (db, vfs, _proc_macro) = {
@@ -121,6 +122,7 @@ fn integrated_completion_benchmark() {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
+        proc_macro_processes: 1,
     };
 
     let (db, vfs, _proc_macro) = {
@@ -322,6 +324,7 @@ fn integrated_diagnostics_benchmark() {
         load_out_dirs_from_check: true,
         with_proc_macro_server: ProcMacroServerChoice::Sysroot,
         prefill_caches: true,
+        proc_macro_processes: 1,
     };
 
     let (db, vfs, _proc_macro) = {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
index ccafbd7b30b9..83f4a19b39fa 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/reload.rs
@@ -701,15 +701,19 @@ impl GlobalState {
                     _ => Default::default(),
                 };
                 info!("Using proc-macro server at {path}");
+                let num_process = self.config.proc_macro_num_processes();
 
-                Some(ProcMacroClient::spawn(&path, &env, ws.toolchain.as_ref()).map_err(|err| {
-                    tracing::error!(
-                        "Failed to run proc-macro server from path {path}, error: {err:?}",
-                    );
-                    anyhow::format_err!(
-                        "Failed to run proc-macro server from path {path}, error: {err:?}",
-                    )
-                }))
+                Some(
+                    ProcMacroClient::spawn(&path, &env, ws.toolchain.as_ref(), num_process)
+                        .map_err(|err| {
+                            tracing::error!(
+                                "Failed to run proc-macro server from path {path}, error: {err:?}",
+                            );
+                            anyhow::format_err!(
+                                "Failed to run proc-macro server from path {path}, error: {err:?}",
+                            )
+                        }),
+                )
             }))
         }
 

From 0587cbdd6fc2321564394828d6172fd268fc6617 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 09:56:02 +0530
Subject: [PATCH 091/319] optimize pick_process to short circuit and return as
 early as possible if a valid process is found

---
 .../crates/proc-macro-api/src/pool.rs         | 28 +++++++++++++------
 .../crates/rust-analyzer/src/config.rs        |  2 +-
 2 files changed, 21 insertions(+), 9 deletions(-)

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 0cb505aa40a9..a637bc0e480a 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
@@ -28,14 +28,26 @@ impl ProcMacroServerPool {
     }
 
     pub(crate) fn pick_process(&self) -> Result<&ProcMacroServerProcess, ServerError> {
-        self.workers
-            .iter()
-            .filter(|w| w.exited().is_none())
-            .min_by_key(|w| w.number_of_active_req())
-            .ok_or_else(|| ServerError {
-                message: "all proc-macro server workers have exited".into(),
-                io: None,
-            })
+        let mut best: Option<&ProcMacroServerProcess> = None;
+        let mut best_load = u32::MAX;
+
+        for w in self.workers.iter().filter(|w| w.exited().is_none()) {
+            let load = w.number_of_active_req();
+
+            if load == 0 {
+                return Ok(w);
+            }
+
+            if load < best_load {
+                best = Some(w);
+                best_load = load;
+            }
+        }
+
+        best.ok_or_else(|| ServerError {
+            message: "all proc-macro server workers have exited".into(),
+            io: None,
+        })
     }
 
     pub(crate) fn load_dylib(
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index cb6552c32ffb..409f2468a7b1 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -380,7 +380,7 @@ config_data! {
         /// The number of worker threads in the main loop. The default `null` means to pick
         /// automatically.
         numThreads: Option = None,
-        /// The number of proc-macro-srv processes 
+        /// The number of proc-macro-srv processes
         proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.

From b49417eea9fcb252ffe62d35068375534d553af0 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 10:22:49 +0530
Subject: [PATCH 092/319] fix test and update some autogen files

---
 .../crates/rust-analyzer/src/config.rs        | 24 ++++++++++++++++--
 .../docs/book/src/configuration_generated.md  | 10 ++++++++
 .../rust-analyzer/editors/code/package.json   | 25 +++++++++++++++++++
 3 files changed, 57 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 409f2468a7b1..015e6df96f2f 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -380,8 +380,6 @@ config_data! {
         /// The number of worker threads in the main loop. The default `null` means to pick
         /// automatically.
         numThreads: Option = None,
-        /// The number of proc-macro-srv processes
-        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
 
         /// Expand attribute macros. Requires `#rust-analyzer.procMacro.enable#` to be set.
         procMacro_attributes_enable: bool = true,
@@ -392,6 +390,12 @@ config_data! {
         /// Internal config, path to proc-macro server executable.
         procMacro_server: Option = None,
 
+        /// Number of proc-macro server processes to spawn.
+        ///
+        /// Controls how many independent `proc-macro-srv` processes rust-analyzer
+        /// runs in parallel to handle macro expansion.
+        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
+
         /// The path where to save memory profiling output.
         ///
         /// **Note:** Memory profiling is not enabled by default in rust-analyzer builds, you need to build
@@ -3920,6 +3924,22 @@ fn field_props(field: &str, ty: &str, doc: &[&str], default: &str) -> serde_json
                 },
             ],
         },
+        "NumProcesses" => set! {
+            "anyOf": [
+                {
+                    "type": "number",
+                    "minimum": 0,
+                    "maximum": 255
+                },
+                {
+                    "type": "string",
+                    "enum": ["physical"],
+                    "enumDescriptions": [
+                        "Use the number of physical cores",
+                    ],
+                },
+            ],
+        },
         "Option" => set! {
             "anyOf": [
                 {
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index c4124aaae075..d3f41fb152e1 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -1325,6 +1325,16 @@ Default: `null`
 Internal config, path to proc-macro server executable.
 
 
+## rust-analyzer.proc.macro.processes {#proc.macro.processes}
+
+Default: `1`
+
+Number of proc-macro server processes to spawn.
+
+Controls how many independent `proc-macro-srv` processes rust-analyzer
+runs in parallel to handle macro expansion.
+
+
 ## rust-analyzer.profiling.memoryProfile {#profiling.memoryProfile}
 
 Default: `null`
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index 0d91378706a4..a1266c4a67ed 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -2783,6 +2783,31 @@
                     }
                 }
             },
+            {
+                "title": "Proc",
+                "properties": {
+                    "rust-analyzer.proc.macro.processes": {
+                        "markdownDescription": "Number of proc-macro server processes to spawn.\n\nControls how many independent `proc-macro-srv` processes rust-analyzer\nruns in parallel to handle macro expansion.",
+                        "default": 1,
+                        "anyOf": [
+                            {
+                                "type": "number",
+                                "minimum": 0,
+                                "maximum": 255
+                            },
+                            {
+                                "type": "string",
+                                "enum": [
+                                    "physical"
+                                ],
+                                "enumDescriptions": [
+                                    "Use the number of physical cores"
+                                ]
+                            }
+                        ]
+                    }
+                }
+            },
             {
                 "title": "Profiling",
                 "properties": {

From a81da31f4096bf754eed0c40b384ef2f5b4d854c Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 5 Jan 2026 10:32:00 +0530
Subject: [PATCH 093/319] rename from proc_macro_processes to
 procMacro_processes

---
 .../crates/rust-analyzer/src/config.rs        | 10 +++----
 .../docs/book/src/configuration_generated.md  | 16 +++++------
 .../rust-analyzer/editors/code/package.json   | 28 +++++++++----------
 3 files changed, 27 insertions(+), 27 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 015e6df96f2f..98495f6150da 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -387,14 +387,14 @@ config_data! {
         /// Enable support for procedural macros, implies `#rust-analyzer.cargo.buildScripts.enable#`.
         procMacro_enable: bool = true,
 
-        /// Internal config, path to proc-macro server executable.
-        procMacro_server: Option = None,
-
         /// Number of proc-macro server processes to spawn.
         ///
         /// Controls how many independent `proc-macro-srv` processes rust-analyzer
         /// runs in parallel to handle macro expansion.
-        proc_macro_processes: NumProcesses = NumProcesses::Concrete(1),
+        procMacro_processes: NumProcesses = NumProcesses::Concrete(1),
+
+        /// Internal config, path to proc-macro server executable.
+        procMacro_server: Option = None,
 
         /// The path where to save memory profiling output.
         ///
@@ -2648,7 +2648,7 @@ impl Config {
     }
 
     pub fn proc_macro_num_processes(&self) -> usize {
-        match self.proc_macro_processes() {
+        match self.procMacro_processes() {
             NumProcesses::Concrete(0) | NumProcesses::Physical => num_cpus::get_physical(),
             &NumProcesses::Concrete(n) => n,
         }
diff --git a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
index d3f41fb152e1..5b1a2e111196 100644
--- a/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
+++ b/src/tools/rust-analyzer/docs/book/src/configuration_generated.md
@@ -1318,14 +1318,7 @@ These proc-macros will be ignored when trying to expand them.
 This config takes a map of crate names with the exported proc-macro names to ignore as values.
 
 
-## rust-analyzer.procMacro.server {#procMacro.server}
-
-Default: `null`
-
-Internal config, path to proc-macro server executable.
-
-
-## rust-analyzer.proc.macro.processes {#proc.macro.processes}
+## rust-analyzer.procMacro.processes {#procMacro.processes}
 
 Default: `1`
 
@@ -1335,6 +1328,13 @@ Controls how many independent `proc-macro-srv` processes rust-analyzer
 runs in parallel to handle macro expansion.
 
 
+## rust-analyzer.procMacro.server {#procMacro.server}
+
+Default: `null`
+
+Internal config, path to proc-macro server executable.
+
+
 ## rust-analyzer.profiling.memoryProfile {#profiling.memoryProfile}
 
 Default: `null`
diff --git a/src/tools/rust-analyzer/editors/code/package.json b/src/tools/rust-analyzer/editors/code/package.json
index a1266c4a67ed..406e41767f6d 100644
--- a/src/tools/rust-analyzer/editors/code/package.json
+++ b/src/tools/rust-analyzer/editors/code/package.json
@@ -2773,20 +2773,7 @@
             {
                 "title": "Proc Macro",
                 "properties": {
-                    "rust-analyzer.procMacro.server": {
-                        "markdownDescription": "Internal config, path to proc-macro server executable.",
-                        "default": null,
-                        "type": [
-                            "null",
-                            "string"
-                        ]
-                    }
-                }
-            },
-            {
-                "title": "Proc",
-                "properties": {
-                    "rust-analyzer.proc.macro.processes": {
+                    "rust-analyzer.procMacro.processes": {
                         "markdownDescription": "Number of proc-macro server processes to spawn.\n\nControls how many independent `proc-macro-srv` processes rust-analyzer\nruns in parallel to handle macro expansion.",
                         "default": 1,
                         "anyOf": [
@@ -2808,6 +2795,19 @@
                     }
                 }
             },
+            {
+                "title": "Proc Macro",
+                "properties": {
+                    "rust-analyzer.procMacro.server": {
+                        "markdownDescription": "Internal config, path to proc-macro server executable.",
+                        "default": null,
+                        "type": [
+                            "null",
+                            "string"
+                        ]
+                    }
+                }
+            },
             {
                 "title": "Profiling",
                 "properties": {

From 8da5de0ca02c316fee3fb97c11052e3d70a32bef Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 11 Jan 2026 15:46:03 +0530
Subject: [PATCH 094/319] rebased changes

---
 .../crates/proc-macro-api/src/lib.rs          | 26 +++++++++++++++----
 .../crates/proc-macro-api/src/process.rs      | 13 +++-------
 2 files changed, 25 insertions(+), 14 deletions(-)

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 2c0008ae1d82..3acd0b292a31 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
@@ -155,8 +155,15 @@ impl ProcMacroClient {
         version: Option<&Version>,
         num_process: usize,
     ) -> io::Result {
-        let process = ProcMacroServerProcess::spawn(process_path, env, version)?;
-        Ok(ProcMacroClient { process: Arc::new(process), path: process_path.to_owned() })
+        let pool_size = num_process;
+        let mut workers = Vec::with_capacity(pool_size);
+        for _ in 0..pool_size {
+            let worker = ProcMacroServerProcess::spawn(process_path, env.clone(), version)?;
+            workers.push(worker);
+        }
+
+        let pool = ProcMacroServerPool::new(workers);
+        Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() })
     }
 
     /// Invokes `spawn` and returns a client connected to the resulting read and write handles.
@@ -170,11 +177,20 @@ impl ProcMacroClient {
             Box,
             Box,
             Box,
-        )>,
+        )> + Clone,
         version: Option<&Version>,
+        num_process: usize,
     ) -> io::Result {
-        let process = ProcMacroServerProcess::run(spawn, version, || "".to_owned())?;
-        Ok(ProcMacroClient { worker: Arc::new(process), path: process_path.to_owned() })
+        let pool_size = num_process;
+        let mut workers = Vec::with_capacity(pool_size);
+        for _ in 0..pool_size {
+            let worker =
+                ProcMacroServerProcess::run(spawn.clone(), version, || "".to_owned())?;
+            workers.push(worker);
+        }
+
+        let pool = ProcMacroServerPool::new(workers);
+        Ok(ProcMacroClient { pool: Arc::new(pool), path: process_path.to_owned() })
     }
 
     /// Returns the absolute path to the proc-macro server.
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 c1b95fa7f10e..2f5bef69abd5 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
@@ -210,9 +210,8 @@ impl ProcMacroServerProcess {
         callback: Option>,
     ) -> Result, String>, ServerError> {
         match self.protocol {
-            Protocol::LegacyJson { .. } | Protocol::LegacyPostcard { .. } => {
-                legacy_protocol::find_proc_macros(self, dylib_path)
-            }
+            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)
@@ -279,7 +278,7 @@ impl ProcMacroServerProcess {
         let result = match self.protocol {
             Protocol::LegacyJson { .. } => legacy_protocol::expand(
                 proc_macro,
-                    self,
+                self,
                 subtree,
                 attr,
                 env,
@@ -344,11 +343,7 @@ impl ProcMacroServerProcess {
                 match state.process.exit_err() {
                     None => e,
                     Some(server_error) => {
-                        proc_macro_worker
-                            .get_exited()
-                            .get_or_init(|| AssertUnwindSafe(server_error))
-                            .0
-                            .clone()
+                        self.exited.get_or_init(|| AssertUnwindSafe(server_error)).0.clone()
                     }
                 }
             } else {

From c31698b6958b5c818e4f4c86d3e4d12e128152f9 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 17 Jan 2026 19:24:19 +0800
Subject: [PATCH 095/319] Improve move_guard redundanted block

Example
---
```rust
fn main() {
    match 92 {
        x $0if x > 10 => {
            let _ = true;
            false
        },
        _ => true
    }
}
```

**Before this PR**

```rust
fn main() {
    match 92 {
        x => if x > 10 {
            {
                let _ = true;
                false
            }
        },
        _ => true
    }
}
```

**After this PR**

```rust
fn main() {
    match 92 {
        x => if x > 10 {
            let _ = true;
            false
        },
        _ => true
    }
}
```
---
 .../ide-assists/src/handlers/move_guard.rs    | 35 ++++++++++++++++---
 .../crates/ide-assists/src/utils.rs           | 11 ++++++
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
index 31baa63372ff..84f02bdfdba6 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/move_guard.rs
@@ -49,7 +49,7 @@ pub(crate) fn move_guard_to_arm_body(acc: &mut Assists, ctx: &AssistContext<'_>)
 
     let guard_condition = guard.condition()?.reset_indent();
     let arm_expr = match_arm.expr()?;
-    let then_branch = make::block_expr(None, Some(arm_expr.reset_indent().indent(1.into())));
+    let then_branch = crate::utils::wrap_block(&arm_expr);
     let if_expr = make::expr_if(guard_condition, then_branch, None).indent(arm_expr.indent_level());
 
     let target = guard.syntax().text_range();
@@ -344,6 +344,35 @@ fn main() {
         );
     }
 
+    #[test]
+    fn move_guard_to_block_arm_body_works() {
+        check_assist(
+            move_guard_to_arm_body,
+            r#"
+fn main() {
+    match 92 {
+        x $0if x > 10 => {
+            let _ = true;
+            false
+        },
+        _ => true
+    }
+}
+"#,
+            r#"
+fn main() {
+    match 92 {
+        x => if x > 10 {
+            let _ = true;
+            false
+        },
+        _ => true
+    }
+}
+"#,
+        );
+    }
+
     #[test]
     fn move_let_guard_to_arm_body_works() {
         check_assist(
@@ -395,9 +424,7 @@ fn main() {
             && true
             && true {
             {
-                {
-                    false
-                }
+                false
             }
         },
         _ => true
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 9a96374c00af..4b8c19305793 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/utils.rs
@@ -86,6 +86,17 @@ pub fn extract_trivial_expression(block_expr: &ast::BlockExpr) -> Option ast::BlockExpr {
+    if let ast::Expr::BlockExpr(block) = expr
+        && let Some(first) = block.syntax().first_token()
+        && first.kind() == T!['{']
+    {
+        block.reset_indent()
+    } else {
+        make::block_expr(None, Some(expr.reset_indent().indent(1.into())))
+    }
+}
+
 /// This is a method with a heuristics to support test methods annotated with custom test annotations, such as
 /// `#[test_case(...)]`, `#[tokio::test]` and similar.
 /// Also a regular `#[test]` annotation is supported.

From cbad6dd11772b3d02a4eaca4e049f66907bb72a1 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 16 Jan 2026 16:23:56 +0100
Subject: [PATCH 096/319] fix: Do not show sysroot dependencies in symbol
 search

---
 src/tools/rust-analyzer/crates/base-db/src/input.rs       | 3 ++-
 src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs | 8 +++++++-
 .../rust-analyzer/crates/project-model/src/workspace.rs   | 6 +++++-
 3 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/base-db/src/input.rs b/src/tools/rust-analyzer/crates/base-db/src/input.rs
index 240f1264917a..94793a3618e1 100644
--- a/src/tools/rust-analyzer/crates/base-db/src/input.rs
+++ b/src/tools/rust-analyzer/crates/base-db/src/input.rs
@@ -221,6 +221,7 @@ pub enum LangCrateOrigin {
     ProcMacro,
     Std,
     Test,
+    Dependency,
     Other,
 }
 
@@ -245,7 +246,7 @@ impl fmt::Display for LangCrateOrigin {
             LangCrateOrigin::ProcMacro => "proc_macro",
             LangCrateOrigin::Std => "std",
             LangCrateOrigin::Test => "test",
-            LangCrateOrigin::Other => "other",
+            LangCrateOrigin::Other | LangCrateOrigin::Dependency => "other",
         };
         f.write_str(text)
     }
diff --git a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
index d7f4c66f465b..183f6b649537 100644
--- a/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
+++ b/src/tools/rust-analyzer/crates/ide-db/src/symbol_index.rs
@@ -27,7 +27,7 @@ use std::{
     ops::ControlFlow,
 };
 
-use base_db::{LibraryRoots, LocalRoots, RootQueryDb, SourceRootId};
+use base_db::{CrateOrigin, LangCrateOrigin, LibraryRoots, LocalRoots, RootQueryDb, SourceRootId};
 use fst::{Automaton, Streamer, raw::IndexedValue};
 use hir::{
     Crate, Module,
@@ -446,6 +446,12 @@ impl<'db> SymbolIndex<'db> {
                     {
                         continue;
                     }
+                    if let CrateOrigin::Lang(LangCrateOrigin::Dependency | LangCrateOrigin::Other) =
+                        krate.origin(db)
+                    {
+                        // don't show dependencies of the sysroot
+                        continue;
+                    }
                     collector.push_crate_root(krate);
                 }
 
diff --git a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
index fa3a79e041e0..8f15f7e1507c 100644
--- a/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
+++ b/src/tools/rust-analyzer/crates/project-model/src/workspace.rs
@@ -1161,6 +1161,8 @@ fn project_json_to_crate_graph(
                                 name: Some(name.canonical_name().to_owned()),
                             }
                         }
+                    } else if is_sysroot {
+                        CrateOrigin::Lang(LangCrateOrigin::Dependency)
                     } else {
                         CrateOrigin::Local { repo: None, name: None }
                     },
@@ -1294,6 +1296,8 @@ fn cargo_to_crate_graph(
                             name: Some(Symbol::intern(&pkg_data.name)),
                         }
                     }
+                } else if cargo.is_sysroot() {
+                    CrateOrigin::Lang(LangCrateOrigin::Dependency)
                 } else {
                     CrateOrigin::Library {
                         repo: pkg_data.repository.clone(),
@@ -1717,7 +1721,7 @@ fn extend_crate_graph_with_sysroot(
                     !matches!(lang_crate, LangCrateOrigin::Test | LangCrateOrigin::Alloc),
                 )),
                 LangCrateOrigin::ProcMacro => libproc_macro = Some(cid),
-                LangCrateOrigin::Other => (),
+                LangCrateOrigin::Other | LangCrateOrigin::Dependency => (),
             }
         }
     }

From bfbee86a2de8e281db70b08367ce8245082431a2 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 16 Jan 2026 13:35:31 +0100
Subject: [PATCH 097/319] feat: Trigger flycheck if non-workspace files get
 modified

Co-authored-by: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Co-authored-by: dino 
---
 .../crates/rust-analyzer/src/config.rs              |  6 ++++++
 .../rust-analyzer/src/handlers/notification.rs      | 13 +++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
index 28ac94e4deb6..7382edfa96f7 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/config.rs
@@ -1043,6 +1043,7 @@ pub struct Config {
     /// The workspace roots as registered by the LSP client
     workspace_roots: Vec,
     caps: ClientCapabilities,
+    /// The LSP root path, deprecated in favor of `workspace_roots`
     root_path: AbsPathBuf,
     snippets: Vec,
     client_info: Option,
@@ -1366,6 +1367,10 @@ impl Config {
 
         self.discovered_projects_from_command.push(ProjectJsonFromCommand { data, buildfile });
     }
+
+    pub fn workspace_roots(&self) -> &[AbsPathBuf] {
+        &self.workspace_roots
+    }
 }
 
 #[derive(Default, Debug)]
@@ -1742,6 +1747,7 @@ impl Config {
     }
 
     pub fn root_path(&self) -> &AbsPathBuf {
+        // We should probably use `workspace_roots` here if set
         &self.root_path
     }
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
index 6cc40677fb51..138310b78f62 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/handlers/notification.rs
@@ -289,11 +289,24 @@ pub(crate) fn handle_did_change_watched_files(
     state: &mut GlobalState,
     params: DidChangeWatchedFilesParams,
 ) -> anyhow::Result<()> {
+    // we want to trigger flycheck if a file outside of our workspaces has changed,
+    // as to reduce stale diagnostics when outside changes happen
+    let mut trigger_flycheck = false;
     for change in params.changes.iter().unique_by(|&it| &it.uri) {
         if let Ok(path) = from_proto::abs_path(&change.uri) {
+            if !trigger_flycheck {
+                trigger_flycheck =
+                    state.config.workspace_roots().iter().any(|root| !path.starts_with(root));
+            }
             state.loader.handle.invalidate(path);
         }
     }
+
+    if trigger_flycheck && state.config.check_on_save(None) {
+        for flycheck in state.flycheck.iter() {
+            flycheck.restart_workspace(None);
+        }
+    }
     Ok(())
 }
 

From ebcbff2a2e79d0c36442bb19a9eb154dc202f2d5 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 04:50:23 +0200
Subject: [PATCH 098/319] Do not mix the order of builtin/regular derives in
 "Expand macro recursively"

---
 .../rust-analyzer/crates/hir/src/semantics.rs   |  7 +++++--
 .../crates/ide/src/expand_macro.rs              | 17 ++++++++++++++++-
 2 files changed, 21 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index e55b693ef018..98f5739600f3 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -641,11 +641,14 @@ impl<'db> SemanticsImpl<'db> {
         })
     }
 
-    pub fn expand_derive_macro(&self, attr: &ast::Attr) -> Option>> {
+    pub fn expand_derive_macro(
+        &self,
+        attr: &ast::Attr,
+    ) -> Option>>> {
         let res: Vec<_> = self
             .derive_macro_calls(attr)?
             .into_iter()
-            .flat_map(|call| {
+            .map(|call| {
                 let file_id = call?.left()?;
                 let ExpandResult { value, err } = self.db.parse_macro_expansion(file_id);
                 let root_node = value.0.syntax_node();
diff --git a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
index ba8b3aa9cafe..44285d9315af 100644
--- a/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/expand_macro.rs
@@ -63,7 +63,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
             .take_while(|it| it != &token)
             .filter(|it| it.kind() == T![,])
             .count();
-        let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone();
+        let ExpandResult { err, value: expansion } = expansions.get(idx)?.clone()?;
         let expansion_file_id = sema.hir_file_for(&expansion).macro_file()?;
         let expansion_span_map = db.expansion_span_map(expansion_file_id);
         let mut expansion = format(
@@ -848,4 +848,19 @@ struct S {
                 u32"#]],
         );
     }
+
+    #[test]
+    fn regression_21489() {
+        check(
+            r#"
+//- proc_macros: derive_identity
+//- minicore: derive, fmt
+#[derive(Debug, proc_macros::DeriveIdentity$0)]
+struct Foo;
+        "#,
+            expect![[r#"
+                proc_macros::DeriveIdentity
+                struct Foo;"#]],
+        );
+    }
 }

From 4dad9b90819bac5aeb50182d77ee0a90c5a196d5 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 05:21:54 +0200
Subject: [PATCH 099/319] Insert type vars and normalize for the type of a used
 `static`

They have their own special path, so they slipped through.
---
 .../crates/hir-ty/src/infer/path.rs           |  1 +
 .../crates/hir-ty/src/tests/simple.rs         | 43 +++++++++++++++++++
 2 files changed, 44 insertions(+)

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 b11650bbcd9a..ef1a610a323d 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
@@ -93,6 +93,7 @@ impl<'db> InferenceContext<'_, 'db> {
         if let GenericDefId::StaticId(_) = generic_def {
             // `Static` is the kind of item that can never be generic currently. We can just skip the binders to get its type.
             let ty = self.db.value_ty(value_def)?.skip_binder();
+            let ty = self.process_remote_user_written_ty(ty);
             return Some(ValuePathResolution::NonGeneric(ty));
         };
 
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 28759bcbae61..d2a4149bc630 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
@@ -3997,3 +3997,46 @@ extern "C" fn foo() -> ! {
     "#,
     );
 }
+
+#[test]
+fn regression_21478() {
+    check_infer(
+        r#"
+//- minicore: unsize, coerce_unsized
+struct LazyLock(T);
+
+impl LazyLock {
+    const fn new() -> Self {
+        loop {}
+    }
+
+    fn force(this: &Self) -> &T {
+        loop {}
+    }
+}
+
+static VALUES_LAZY_LOCK: LazyLock<[u32; { 0 }]> = LazyLock::new();
+
+fn foo() {
+    let _ = LazyLock::force(&VALUES_LAZY_LOCK);
+}
+    "#,
+        expect![[r#"
+            73..96 '{     ...     }': LazyLock
+            83..90 'loop {}': !
+            88..90 '{}': ()
+            111..115 'this': &'? LazyLock
+            130..153 '{     ...     }': &'? T
+            140..147 'loop {}': !
+            145..147 '{}': ()
+            207..220 'LazyLock::new': fn new<[u32; _]>() -> LazyLock<[u32; _]>
+            207..222 'LazyLock::new()': LazyLock<[u32; _]>
+            234..285 '{     ...CK); }': ()
+            244..245 '_': &'? [u32; _]
+            248..263 'LazyLock::force': fn force<[u32; _]>(&'? LazyLock<[u32; _]>) -> &'? [u32; _]
+            248..282 'LazyLo..._LOCK)': &'? [u32; _]
+            264..281 '&VALUE...Y_LOCK': &'? LazyLock<[u32; _]>
+            265..281 'VALUES...Y_LOCK': LazyLock<[u32; _]>
+        "#]],
+    );
+}

From 1285b1b13c28e5e11637884e741b6b7e8f36efc8 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 07:14:48 +0200
Subject: [PATCH 100/319] Ensure correct capturing of async fn params even when
 they use weird patterns

rustc does the same.
---
 .../crates/hir-def/src/expr_store/lower.rs    | 66 ++++++++++++++-----
 .../hir-def/src/expr_store/tests/body.rs      | 18 +++++
 .../crates/hir-ty/src/tests/regression.rs     |  1 -
 .../crates/hir-ty/src/tests/simple.rs         |  1 -
 .../crates/hir-ty/src/tests/traits.rs         |  3 -
 5 files changed, 68 insertions(+), 21 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 4ae4271b92f5..79222615929f 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
@@ -150,6 +150,7 @@ pub(super) fn lower_body(
     };
 
     let body_expr = collector.collect(
+        &mut params,
         body,
         if is_async_fn {
             Awaitable::Yes
@@ -903,24 +904,57 @@ impl<'db> ExprCollector<'db> {
         })
     }
 
-    fn collect(&mut self, expr: Option, awaitable: Awaitable) -> ExprId {
+    /// An `async fn` needs to capture all parameters in the generated `async` block, even if they have
+    /// non-captured patterns such as wildcards (to ensure consistent drop order).
+    fn lower_async_fn(&mut self, params: &mut Vec, body: ExprId) -> ExprId {
+        let mut statements = Vec::new();
+        for param in params {
+            let name = match self.store.pats[*param] {
+                Pat::Bind { id, .. }
+                    if matches!(
+                        self.store.bindings[id].mode,
+                        BindingAnnotation::Unannotated | BindingAnnotation::Mutable
+                    ) =>
+                {
+                    // If this is a direct binding, we can leave it as-is, as it'll always be captured anyway.
+                    continue;
+                }
+                Pat::Bind { id, .. } => {
+                    // If this is a `ref` binding, we can't leave it as is but we can at least reuse the name, for better display.
+                    self.store.bindings[id].name.clone()
+                }
+                _ => self.generate_new_name(),
+            };
+            let binding_id =
+                self.alloc_binding(name.clone(), BindingAnnotation::Mutable, HygieneId::ROOT);
+            let pat_id = self.alloc_pat_desugared(Pat::Bind { id: binding_id, subpat: None });
+            let expr = self.alloc_expr_desugared(Expr::Path(name.into()));
+            statements.push(Statement::Let {
+                pat: *param,
+                type_ref: None,
+                initializer: Some(expr),
+                else_branch: None,
+            });
+            *param = pat_id;
+        }
+
+        self.alloc_expr_desugared(Expr::Async {
+            id: None,
+            statements: statements.into_boxed_slice(),
+            tail: Some(body),
+        })
+    }
+
+    fn collect(
+        &mut self,
+        params: &mut Vec,
+        expr: Option,
+        awaitable: Awaitable,
+    ) -> ExprId {
         self.awaitable_context.replace(awaitable);
         self.with_label_rib(RibKind::Closure, |this| {
-            if awaitable == Awaitable::Yes {
-                match expr {
-                    Some(e) => {
-                        let syntax_ptr = AstPtr::new(&e);
-                        let expr = this.collect_expr(e);
-                        this.alloc_expr_desugared_with_ptr(
-                            Expr::Async { id: None, statements: Box::new([]), tail: Some(expr) },
-                            syntax_ptr,
-                        )
-                    }
-                    None => this.missing_expr(),
-                }
-            } else {
-                this.collect_expr_opt(expr)
-            }
+            let body = this.collect_expr_opt(expr);
+            if awaitable == Awaitable::Yes { this.lower_async_fn(params, body) } else { body }
         })
     }
 
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
index 504c310684d6..8f857aeeff95 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/tests/body.rs
@@ -659,3 +659,21 @@ fn main() {
         }"#]]
     .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
 }
+
+#[test]
+fn async_fn_weird_param_patterns() {
+    let (db, body, def) = lower(
+        r#"
+async fn main(&self, param1: i32, ref mut param2: i32, _: i32, param4 @ _: i32, 123: i32) {}
+"#,
+    );
+
+    expect![[r#"
+        fn main(self, param1, mut param2, mut 0, param4 @ _, mut 1) async {
+            let ref mut param2 = param2;
+            let _ = 0;
+            let 123 = 1;
+            {}
+        }"#]]
+    .assert_eq(&body.pretty_print(&db, def, Edition::CURRENT))
+}
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 a04c46f8eabd..4f1480c39366 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
@@ -2235,7 +2235,6 @@ async fn f() -> Bar {}
 "#,
         expect![[r#"
             64..66 '{}': ()
-            64..66 '{}': impl Future
         "#]],
     );
 }
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 28759bcbae61..80e21450c7a9 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
@@ -2139,7 +2139,6 @@ async fn main() {
         "#,
         expect![[r#"
             16..193 '{     ...2 }; }': ()
-            16..193 '{     ...2 }; }': impl Future
             26..27 'x': i32
             30..43 'unsafe { 92 }': i32
             39..41 '92': i32
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
index b825a0a8f0e5..390553c0d7a9 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/traits.rs
@@ -4869,7 +4869,6 @@ async fn baz i32>(c: T) {
         expect![[r#"
             37..38 'a': T
             43..83 '{     ...ait; }': ()
-            43..83 '{     ...ait; }': impl Future
             53..57 'fut1': >::CallRefFuture<'?>
             60..61 'a': T
             60..64 'a(0)': >::CallRefFuture<'?>
@@ -4878,7 +4877,6 @@ async fn baz i32>(c: T) {
             70..80 'fut1.await': i32
             124..129 'mut b': T
             134..174 '{     ...ait; }': ()
-            134..174 '{     ...ait; }': impl Future
             144..148 'fut2': >::CallRefFuture<'?>
             151..152 'b': T
             151..155 'b(0)': >::CallRefFuture<'?>
@@ -4887,7 +4885,6 @@ async fn baz i32>(c: T) {
             161..171 'fut2.await': i32
             216..217 'c': T
             222..262 '{     ...ait; }': ()
-            222..262 '{     ...ait; }': impl Future
             232..236 'fut3': >::CallOnceFuture
             239..240 'c': T
             239..243 'c(0)': >::CallOnceFuture

From 740eb6b59fa663e9f1b721d7f6995dcec4af4bc9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 16 Jan 2026 01:16:57 +0530
Subject: [PATCH 101/319] remove non-describing field annotation from
 bidirectional message definition

---
 .../proc-macro-api/src/bidirectional_protocol/msg.rs   | 10 ----------
 1 file changed, 10 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index c56ed5191694..2644cd406b63 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -84,18 +84,14 @@ pub struct ExpandMacroData {
     pub macro_body: FlatTree,
     pub macro_name: String,
     pub attributes: Option,
-    #[serde(skip_serializing_if = "ExpnGlobals::skip_serializing_if")]
     #[serde(default)]
     pub has_global_spans: ExpnGlobals,
-
-    #[serde(skip_serializing_if = "Vec::is_empty")]
     #[serde(default)]
     pub span_data_table: Vec,
 }
 
 #[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)]
 pub struct ExpnGlobals {
-    #[serde(skip_serializing)]
     #[serde(default)]
     pub serialize: bool,
     pub def_site: usize,
@@ -103,10 +99,4 @@ pub struct ExpnGlobals {
     pub mixed_site: usize,
 }
 
-impl ExpnGlobals {
-    fn skip_serializing_if(&self) -> bool {
-        !self.serialize
-    }
-}
-
 impl Message for BidirectionalMessage {}

From ffa2dadf0eeab0a484688373cffe9f1939173084 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 19:34:03 +0530
Subject: [PATCH 102/319] remove serialize from Expn Globals

---
 .../crates/proc-macro-api/src/bidirectional_protocol.rs   | 8 +-------
 .../proc-macro-api/src/bidirectional_protocol/msg.rs      | 2 --
 2 files changed, 1 insertion(+), 9 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index b5f43e1d3726..a13bff7d7d02 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -23,7 +23,6 @@ use crate::{
     },
     process::ProcMacroServerProcess,
     transport::codec::postcard::PostcardProtocol,
-    version,
 };
 
 pub mod msg;
@@ -159,12 +158,7 @@ pub(crate) fn expand(
             macro_name: proc_macro.name.to_string(),
             attributes: attr
                 .map(|subtree| FlatTree::from_subtree(subtree, version, &mut span_data_table)),
-            has_global_spans: ExpnGlobals {
-                serialize: version >= version::HAS_GLOBAL_SPANS,
-                def_site,
-                call_site,
-                mixed_site,
-            },
+            has_global_spans: ExpnGlobals { def_site, call_site, mixed_site },
             span_data_table: if process.rust_analyzer_spans() {
                 serialize_span_data_index_map(&span_data_table)
             } else {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index 2644cd406b63..d030498e59c4 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -92,8 +92,6 @@ pub struct ExpandMacroData {
 
 #[derive(Clone, Copy, Default, Debug, Serialize, Deserialize)]
 pub struct ExpnGlobals {
-    #[serde(default)]
-    pub serialize: bool,
     pub def_site: usize,
     pub call_site: usize,
     pub mixed_site: usize,

From 8fd55694389332c648a76c0ad533ba9e01ecc1fe Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:23:30 +0530
Subject: [PATCH 103/319] add bidirectional flow

---
 .../proc-macro-srv-cli/tests/common/utils.rs  | 570 +++++++++++-------
 1 file changed, 357 insertions(+), 213 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
index 722e92eec7e5..63b3a74aa4e8 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
@@ -1,213 +1,357 @@
-use std::{
-    collections::VecDeque,
-    io::{self, BufRead, Read, Write},
-    sync::{Arc, Condvar, Mutex},
-    thread,
-};
-
-use paths::Utf8PathBuf;
-use proc_macro_api::{
-    legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap},
-    transport::codec::json::JsonProtocol,
-};
-use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange};
-use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder};
-
-/// Shared state for an in-memory byte channel.
-#[derive(Default)]
-struct ChannelState {
-    buffer: VecDeque,
-    closed: bool,
-}
-
-type InMemoryChannel = Arc<(Mutex, Condvar)>;
-
-/// Writer end of an in-memory channel.
-pub(crate) struct ChannelWriter {
-    state: InMemoryChannel,
-}
-
-impl Write for ChannelWriter {
-    fn write(&mut self, buf: &[u8]) -> io::Result {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-        if state.closed {
-            return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed"));
-        }
-        state.buffer.extend(buf);
-        cvar.notify_all();
-        Ok(buf.len())
-    }
-
-    fn flush(&mut self) -> io::Result<()> {
-        Ok(())
-    }
-}
-
-impl Drop for ChannelWriter {
-    fn drop(&mut self) {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-        state.closed = true;
-        cvar.notify_all();
-    }
-}
-
-/// Reader end of an in-memory channel.
-pub(crate) struct ChannelReader {
-    state: InMemoryChannel,
-    internal_buf: Vec,
-}
-
-impl Read for ChannelReader {
-    fn read(&mut self, buf: &mut [u8]) -> io::Result {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-
-        while state.buffer.is_empty() && !state.closed {
-            state = cvar.wait(state).unwrap();
-        }
-
-        if state.buffer.is_empty() && state.closed {
-            return Ok(0);
-        }
-
-        let to_read = buf.len().min(state.buffer.len());
-        for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) {
-            *dst = src;
-        }
-        Ok(to_read)
-    }
-}
-
-impl BufRead for ChannelReader {
-    fn fill_buf(&mut self) -> io::Result<&[u8]> {
-        let (lock, cvar) = &*self.state;
-        let mut state = lock.lock().unwrap();
-
-        while state.buffer.is_empty() && !state.closed {
-            state = cvar.wait(state).unwrap();
-        }
-
-        self.internal_buf.clear();
-        self.internal_buf.extend(&state.buffer);
-        Ok(&self.internal_buf)
-    }
-
-    fn consume(&mut self, amt: usize) {
-        let (lock, _) = &*self.state;
-        let mut state = lock.lock().unwrap();
-        let to_drain = amt.min(state.buffer.len());
-        drop(state.buffer.drain(..to_drain));
-    }
-}
-
-/// Creates a connected pair of channels for bidirectional communication.
-fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) {
-    // Channel for client -> server communication
-    let client_to_server = Arc::new((
-        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
-        Condvar::new(),
-    ));
-    let client_writer = ChannelWriter { state: client_to_server.clone() };
-    let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() };
-
-    // Channel for server -> client communication
-    let server_to_client = Arc::new((
-        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
-        Condvar::new(),
-    ));
-
-    let server_writer = ChannelWriter { state: server_to_client.clone() };
-    let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() };
-
-    (client_writer, client_reader, server_writer, server_reader)
-}
-
-pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf {
-    let path = proc_macro_test::PROC_MACRO_TEST_LOCATION;
-    if path.is_empty() {
-        panic!("proc-macro-test dylib not available (requires nightly toolchain)");
-    }
-    path.into()
-}
-
-/// Runs a test with the server in a background thread.
-pub(crate) fn with_server(test_fn: F) -> R
-where
-    F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R,
-{
-    let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) =
-        create_channel_pair();
-
-    let server_handle = thread::spawn(move || {
-        proc_macro_srv_cli::main_loop::run(
-            &mut server_reader,
-            &mut server_writer,
-            proc_macro_api::ProtocolFormat::JsonLegacy,
-        )
-    });
-
-    let result = test_fn(&mut client_writer, &mut client_reader);
-
-    // Close the client writer to signal the server to stop
-    drop(client_writer);
-
-    // Wait for server to finish
-    match server_handle.join() {
-        Ok(Ok(())) => {}
-        Ok(Err(e)) => {
-            // IO error from server is expected when client disconnects
-            if matches!(
-                e.kind(),
-                io::ErrorKind::BrokenPipe
-                    | io::ErrorKind::UnexpectedEof
-                    | io::ErrorKind::InvalidData
-            ) {
-                panic!("Server error: {e}");
-            }
-        }
-        Err(e) => std::panic::resume_unwind(e),
-    }
-
-    result
-}
-
-/// Sends a request and reads the response using JSON protocol.
-pub(crate) fn request(
-    writer: &mut dyn Write,
-    reader: &mut dyn BufRead,
-    request: Request,
-) -> Response {
-    request.write::(writer).expect("failed to write request");
-
-    let mut buf = String::new();
-    Response::read::(reader, &mut buf)
-        .expect("failed to read response")
-        .expect("no response received")
-}
-
-/// Creates a simple empty token tree suitable for testing.
-pub(crate) fn create_empty_token_tree(
-    version: u32,
-    span_data_table: &mut SpanDataIndexMap,
-) -> FlatTree {
-    let anchor = SpanAnchor {
-        file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT),
-        ast_id: span::ROOT_ERASED_FILE_AST_ID,
-    };
-    let span = Span {
-        range: TextRange::empty(0.into()),
-        anchor,
-        ctx: SyntaxContext::root(Edition::CURRENT),
-    };
-
-    let builder = TopSubtreeBuilder::new(Delimiter {
-        open: span,
-        close: span,
-        kind: DelimiterKind::Invisible,
-    });
-    let tt = builder.build();
-
-    FlatTree::from_subtree(tt.view(), version, span_data_table)
-}
+use std::{
+    collections::VecDeque,
+    io::{self, BufRead, Read, Write},
+    sync::{Arc, Condvar, Mutex},
+    thread,
+};
+
+use paths::Utf8PathBuf;
+use proc_macro_api::{
+    ServerError,
+    bidirectional_protocol::msg::{
+        BidirectionalMessage, Request as BiRequest, Response as BiResponse, SubRequest, SubResponse,
+    },
+    legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap},
+    transport::codec::{json::JsonProtocol, postcard::PostcardProtocol},
+};
+use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange};
+use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder};
+
+/// Shared state for an in-memory byte channel.
+#[derive(Default)]
+struct ChannelState {
+    buffer: VecDeque,
+    closed: bool,
+}
+
+type InMemoryChannel = Arc<(Mutex, Condvar)>;
+
+/// Writer end of an in-memory channel.
+pub(crate) struct ChannelWriter {
+    state: InMemoryChannel,
+}
+
+impl Write for ChannelWriter {
+    fn write(&mut self, buf: &[u8]) -> io::Result {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+        if state.closed {
+            return Err(io::Error::new(io::ErrorKind::BrokenPipe, "channel closed"));
+        }
+        state.buffer.extend(buf);
+        cvar.notify_all();
+        Ok(buf.len())
+    }
+
+    fn flush(&mut self) -> io::Result<()> {
+        Ok(())
+    }
+}
+
+impl Drop for ChannelWriter {
+    fn drop(&mut self) {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+        state.closed = true;
+        cvar.notify_all();
+    }
+}
+
+/// Reader end of an in-memory channel.
+pub(crate) struct ChannelReader {
+    state: InMemoryChannel,
+    internal_buf: Vec,
+}
+
+impl Read for ChannelReader {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+
+        while state.buffer.is_empty() && !state.closed {
+            state = cvar.wait(state).unwrap();
+        }
+
+        if state.buffer.is_empty() && state.closed {
+            return Ok(0);
+        }
+
+        let to_read = buf.len().min(state.buffer.len());
+        for (dst, src) in buf.iter_mut().zip(state.buffer.drain(..to_read)) {
+            *dst = src;
+        }
+        Ok(to_read)
+    }
+}
+
+impl BufRead for ChannelReader {
+    fn fill_buf(&mut self) -> io::Result<&[u8]> {
+        let (lock, cvar) = &*self.state;
+        let mut state = lock.lock().unwrap();
+
+        while state.buffer.is_empty() && !state.closed {
+            state = cvar.wait(state).unwrap();
+        }
+
+        self.internal_buf.clear();
+        self.internal_buf.extend(&state.buffer);
+        Ok(&self.internal_buf)
+    }
+
+    fn consume(&mut self, amt: usize) {
+        let (lock, _) = &*self.state;
+        let mut state = lock.lock().unwrap();
+        let to_drain = amt.min(state.buffer.len());
+        drop(state.buffer.drain(..to_drain));
+    }
+}
+
+/// Creates a connected pair of channels for bidirectional communication.
+fn create_channel_pair() -> (ChannelWriter, ChannelReader, ChannelWriter, ChannelReader) {
+    // Channel for client -> server communication
+    let client_to_server = Arc::new((
+        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
+        Condvar::new(),
+    ));
+    let client_writer = ChannelWriter { state: client_to_server.clone() };
+    let server_reader = ChannelReader { state: client_to_server, internal_buf: Vec::new() };
+
+    // Channel for server -> client communication
+    let server_to_client = Arc::new((
+        Mutex::new(ChannelState { buffer: VecDeque::new(), closed: false }),
+        Condvar::new(),
+    ));
+
+    let server_writer = ChannelWriter { state: server_to_client.clone() };
+    let client_reader = ChannelReader { state: server_to_client, internal_buf: Vec::new() };
+
+    (client_writer, client_reader, server_writer, server_reader)
+}
+
+pub(crate) fn proc_macro_test_dylib_path() -> Utf8PathBuf {
+    let path = proc_macro_test::PROC_MACRO_TEST_LOCATION;
+    if path.is_empty() {
+        panic!("proc-macro-test dylib not available (requires nightly toolchain)");
+    }
+    path.into()
+}
+
+/// Creates a simple empty token tree suitable for testing.
+pub(crate) fn create_empty_token_tree(
+    version: u32,
+    span_data_table: &mut SpanDataIndexMap,
+) -> FlatTree {
+    let anchor = SpanAnchor {
+        file_id: EditionedFileId::new(FileId::from_raw(0), Edition::CURRENT),
+        ast_id: span::ROOT_ERASED_FILE_AST_ID,
+    };
+    let span = Span {
+        range: TextRange::empty(0.into()),
+        anchor,
+        ctx: SyntaxContext::root(Edition::CURRENT),
+    };
+
+    let builder = TopSubtreeBuilder::new(Delimiter {
+        open: span,
+        close: span,
+        kind: DelimiterKind::Invisible,
+    });
+    let tt = builder.build();
+
+    FlatTree::from_subtree(tt.view(), version, span_data_table)
+}
+
+pub(crate) fn with_server(format: proc_macro_api::ProtocolFormat, test_fn: F) -> R
+where
+    F: FnOnce(&mut dyn Write, &mut dyn BufRead) -> R,
+{
+    let (mut client_writer, mut client_reader, mut server_writer, mut server_reader) =
+        create_channel_pair();
+
+    let server_handle = thread::spawn(move || {
+        proc_macro_srv_cli::main_loop::run(&mut server_reader, &mut server_writer, format)
+    });
+
+    let result = test_fn(&mut client_writer, &mut client_reader);
+
+    drop(client_writer);
+
+    match server_handle.join() {
+        Ok(Ok(())) => {}
+        Ok(Err(e)) => {
+            if !matches!(
+                e.kind(),
+                io::ErrorKind::BrokenPipe
+                    | io::ErrorKind::UnexpectedEof
+                    | io::ErrorKind::InvalidData
+            ) {
+                panic!("Server error: {e}");
+            }
+        }
+        Err(e) => std::panic::resume_unwind(e),
+    }
+
+    result
+}
+
+trait TestProtocol {
+    type Request;
+    type Response;
+
+    fn send(&self, writer: &mut dyn Write, req: Self::Request);
+    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response;
+}
+
+struct JsonLegacy;
+
+impl TestProtocol for JsonLegacy {
+    type Request = Request;
+    type Response = Response;
+
+    fn send(&self, writer: &mut dyn Write, req: Request) {
+        req.write::(writer).expect("failed to write request");
+    }
+
+    fn drive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response {
+        let mut buf = String::new();
+        Response::read::(reader, &mut buf)
+            .expect("failed to read response")
+            .expect("no response received")
+    }
+}
+
+struct PostcardBidirectional
+where
+    F: Fn(SubRequest) -> Result,
+{
+    callback: F,
+}
+
+impl TestProtocol for PostcardBidirectional
+where
+    F: Fn(SubRequest) -> Result,
+{
+    type Request = BiRequest;
+    type Response = BiResponse;
+
+    fn send(&self, writer: &mut dyn Write, req: BiRequest) {
+        let msg = BidirectionalMessage::Request(req);
+        msg.write::(writer).expect("failed to write request");
+    }
+
+    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse {
+        let mut buf = Vec::new();
+
+        loop {
+            let msg = BidirectionalMessage::read::(reader, &mut buf)
+                .expect("failed to read message")
+                .expect("no message received");
+
+            match msg {
+                BidirectionalMessage::Response(resp) => return resp,
+                BidirectionalMessage::SubRequest(sr) => {
+                    let reply = (self.callback)(sr).expect("subrequest callback failed");
+                    let msg = BidirectionalMessage::SubResponse(reply);
+                    msg.write::(writer).expect("failed to write subresponse");
+                }
+                other => panic!("unexpected message: {other:?}"),
+            }
+        }
+    }
+}
+
+pub(crate) fn request(
+    writer: &mut dyn Write,
+    reader: &mut dyn BufRead,
+    request: impl Into,
+    callback: Option<&dyn Fn(SubRequest) -> Result>,
+) -> AutoResponse {
+    let protocol = match callback {
+        None => AutoProtocol::Legacy(JsonLegacy),
+        Some(cb) => AutoProtocol::Bidirectional(PostcardBidirectional { callback: cb }),
+    };
+
+    protocol.send(writer, request.into());
+    protocol.drive(reader, writer)
+}
+
+enum AutoProtocol
+where
+    F: Fn(SubRequest) -> Result,
+{
+    Legacy(JsonLegacy),
+    Bidirectional(PostcardBidirectional),
+}
+
+impl TestProtocol for AutoProtocol
+where
+    F: Fn(SubRequest) -> Result,
+{
+    type Request = AutoRequest;
+    type Response = AutoResponse;
+
+    fn send(&self, writer: &mut dyn Write, req: AutoRequest) {
+        match (self, req) {
+            (AutoProtocol::Legacy(p), AutoRequest::Legacy(r)) => {
+                p.send(writer, r);
+            }
+            (AutoProtocol::Bidirectional(p), AutoRequest::Bidirectional(r)) => {
+                p.send(writer, r);
+            }
+            (AutoProtocol::Legacy(_), AutoRequest::Bidirectional(_)) => {
+                panic!("bidirectional request used with legacy protocol");
+            }
+            (AutoProtocol::Bidirectional(_), AutoRequest::Legacy(_)) => {
+                panic!("legacy request used with bidirectional protocol");
+            }
+        }
+    }
+
+    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> AutoResponse {
+        match self {
+            AutoProtocol::Legacy(p) => AutoResponse::Legacy(p.drive(reader, writer)),
+            AutoProtocol::Bidirectional(p) => AutoResponse::Bidirectional(p.drive(reader, writer)),
+        }
+    }
+}
+
+pub(crate) enum AutoRequest {
+    Legacy(Request),
+    Bidirectional(BiRequest),
+}
+
+#[derive(Debug)]
+pub(crate) enum AutoResponse {
+    Legacy(Response),
+    Bidirectional(BiResponse),
+}
+
+impl From for AutoRequest {
+    fn from(req: Request) -> AutoRequest {
+        AutoRequest::Legacy(req)
+    }
+}
+
+impl From for AutoRequest {
+    fn from(req: BiRequest) -> AutoRequest {
+        AutoRequest::Bidirectional(req)
+    }
+}
+
+impl From for Response {
+    fn from(res: AutoResponse) -> Response {
+        match res {
+            AutoResponse::Legacy(res) => res,
+            _ => panic!("Should be legacy response"),
+        }
+    }
+}
+
+impl From for BiResponse {
+    fn from(res: AutoResponse) -> BiResponse {
+        match res {
+            AutoResponse::Bidirectional(res) => res,
+            _ => panic!("Should be bidirectional response"),
+        }
+    }
+}

From ee35fd6cb206e00b0e43b7e676a1aed8eb013684 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:23:40 +0530
Subject: [PATCH 104/319] add bidirectional test

---
 .../tests/bidirectional_postcard.rs           | 223 ++++++++++++++++++
 1 file changed, 223 insertions(+)
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
new file mode 100644
index 000000000000..08e44bad3723
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
@@ -0,0 +1,223 @@
+#![cfg(feature = "sysroot-abi")]
+
+mod common {
+    pub(crate) mod utils;
+}
+
+use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use expect_test::expect;
+use proc_macro_api::{
+    ProtocolFormat::BidirectionalPostcardPrototype,
+    bidirectional_protocol::{
+        msg::{ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response},
+        reject_subrequests,
+    },
+    legacy_protocol::msg::{PanicMessage, ServerConfig, SpanDataIndexMap, SpanMode},
+    version::CURRENT_API_VERSION,
+};
+
+#[test]
+fn test_bidi_version_check_bidirectional() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let response =
+            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+
+        match response {
+            Response::ApiVersionCheck(version) => {
+                assert_eq!(version, CURRENT_API_VERSION);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_list_macros() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+        let response =
+            request(writer, reader, Request::ListMacros { dylib_path }, Some(&reject_subrequests))
+                .into();
+
+        let Response::ListMacros(Ok(macros)) = response else {
+            panic!("expected successful ListMacros response");
+        };
+
+        let mut macro_list: Vec<_> =
+            macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect();
+        macro_list.sort();
+        let macro_list_str = macro_list.join("\n");
+
+        expect![[r#"
+            DeriveEmpty [CustomDerive]
+            DeriveError [CustomDerive]
+            DerivePanic [CustomDerive]
+            DeriveReemit [CustomDerive]
+            attr_error [Attr]
+            attr_noop [Attr]
+            attr_panic [Attr]
+            fn_like_clone_tokens [Bang]
+            fn_like_error [Bang]
+            fn_like_mk_idents [Bang]
+            fn_like_mk_literals [Bang]
+            fn_like_noop [Bang]
+            fn_like_panic [Bang]
+            fn_like_span_join [Bang]
+            fn_like_span_line_column [Bang]
+            fn_like_span_ops [Bang]"#]]
+        .assert_eq(¯o_list_str);
+    });
+}
+
+#[test]
+fn test_bidi_list_macros_invalid_path() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let response = request(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
+            Some(&reject_subrequests),
+        )
+        .into();
+
+        match response {
+            Response::ListMacros(Err(e)) => assert!(
+                e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"),
+                "{e}"
+            ),
+            other => panic!("expected error response, got: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_set_config() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::Id };
+        let response =
+            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::Id);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_set_config_rust_analyzer_mode() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
+        let response =
+            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_expand_macro_panic() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body =
+            common::utils::create_empty_token_tree(CURRENT_API_VERSION, &mut span_data_table);
+
+        let request1 = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "fn_like_panic".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, request1, Some(&reject_subrequests)).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                assert!(msg.contains("fn_like_panic"), "panic message should mention macro name");
+            }
+            other => panic!("expected panic response, got: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_bidi_basic_call_flow() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let response1 =
+            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+        assert!(matches!(response1, Response::ApiVersionCheck(_)));
+
+        let response2 = request(
+            writer,
+            reader,
+            Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
+            Some(&reject_subrequests),
+        )
+        .into();
+        assert!(matches!(response2, Response::SetConfig(_)));
+
+        let response3 = request(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path: dylib_path.clone() },
+            Some(&reject_subrequests),
+        )
+        .into();
+        assert!(matches!(response3, Response::ListMacros(Ok(_))));
+    });
+}
+
+#[test]
+fn test_bidi_expand_nonexistent_macro() {
+    with_server(BidirectionalPostcardPrototype, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let version_response =
+            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+        let Response::ApiVersionCheck(version) = version_response else {
+            panic!("expected version check response");
+        };
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body = create_empty_token_tree(version, &mut span_data_table);
+
+        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "NonexistentMacro".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals { def_site: 0, call_site: 0, mixed_site: 0 },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, expand_request, Some(&reject_subrequests)).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg)
+            }
+            other => panic!("expected error for nonexistent macro, got: {other:?}"),
+        }
+    });
+}

From a151d7dc5617ecd8c0cf8fbb688654c66e9776b6 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:23:49 +0530
Subject: [PATCH 105/319] adapt json test

---
 .../proc-macro-srv-cli/tests/legacy_json.rs   | 457 +++++++++---------
 1 file changed, 233 insertions(+), 224 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
index 1fa886219a8a..8daee7b2bceb 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
@@ -1,224 +1,233 @@
-//! Integration tests for the proc-macro-srv-cli main loop.
-//!
-//! These tests exercise the full client-server RPC procedure using in-memory
-//! channels without needing to spawn the actual server and client processes.
-
-#![cfg(feature = "sysroot-abi")]
-
-mod common {
-    pub(crate) mod utils;
-}
-
-use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
-use expect_test::expect;
-use proc_macro_api::{
-    legacy_protocol::msg::{
-        ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig,
-        SpanDataIndexMap, SpanMode,
-    },
-    version::CURRENT_API_VERSION,
-};
-
-#[test]
-fn test_version_check() {
-    with_server(|writer, reader| {
-        let response = request(writer, reader, Request::ApiVersionCheck {});
-
-        match response {
-            Response::ApiVersionCheck(version) => {
-                assert_eq!(version, CURRENT_API_VERSION);
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_list_macros() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-        let response = request(writer, reader, Request::ListMacros { dylib_path });
-
-        let Response::ListMacros(Ok(macros)) = response else {
-            panic!("expected successful ListMacros response");
-        };
-
-        let mut macro_list: Vec<_> =
-            macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect();
-        macro_list.sort();
-        let macro_list_str = macro_list.join("\n");
-
-        expect![[r#"
-            DeriveEmpty [CustomDerive]
-            DeriveError [CustomDerive]
-            DerivePanic [CustomDerive]
-            DeriveReemit [CustomDerive]
-            attr_error [Attr]
-            attr_noop [Attr]
-            attr_panic [Attr]
-            fn_like_clone_tokens [Bang]
-            fn_like_error [Bang]
-            fn_like_mk_idents [Bang]
-            fn_like_mk_literals [Bang]
-            fn_like_noop [Bang]
-            fn_like_panic [Bang]
-            fn_like_span_join [Bang]
-            fn_like_span_line_column [Bang]
-            fn_like_span_ops [Bang]"#]]
-        .assert_eq(¯o_list_str);
-    });
-}
-
-#[test]
-fn test_list_macros_invalid_path() {
-    with_server(|writer, reader| {
-        let response = request(
-            writer,
-            reader,
-            Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
-        );
-
-        match response {
-            Response::ListMacros(Err(e)) => assert!(
-                e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"),
-                "{e}"
-            ),
-            other => panic!("expected error response, got: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_set_config() {
-    with_server(|writer, reader| {
-        let config = ServerConfig { span_mode: SpanMode::Id };
-        let response = request(writer, reader, Request::SetConfig(config));
-
-        match response {
-            Response::SetConfig(returned_config) => {
-                assert_eq!(returned_config.span_mode, SpanMode::Id);
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_set_config_rust_analyzer_mode() {
-    with_server(|writer, reader| {
-        let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
-        let response = request(writer, reader, Request::SetConfig(config));
-
-        match response {
-            Response::SetConfig(returned_config) => {
-                assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer);
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_expand_macro_panic() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-
-        let version_response = request(writer, reader, Request::ApiVersionCheck {});
-        let Response::ApiVersionCheck(version) = version_response else {
-            panic!("expected version check response");
-        };
-
-        let mut span_data_table = SpanDataIndexMap::default();
-        let macro_body = create_empty_token_tree(version, &mut span_data_table);
-
-        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
-            lib: dylib_path,
-            env: vec![],
-            current_dir: None,
-            data: ExpandMacroData {
-                macro_body,
-                macro_name: "fn_like_panic".to_owned(),
-                attributes: None,
-                has_global_spans: ExpnGlobals {
-                    serialize: version >= 3,
-                    def_site: 0,
-                    call_site: 0,
-                    mixed_site: 0,
-                },
-                span_data_table: vec![],
-            },
-        }));
-
-        let response = request(writer, reader, expand_request);
-
-        match response {
-            Response::ExpandMacro(Err(PanicMessage(msg))) => {
-                assert!(msg.contains("fn_like_panic"), "panic message should mention the macro");
-            }
-            Response::ExpandMacro(Ok(_)) => {
-                panic!("expected panic, but macro succeeded");
-            }
-            other => panic!("unexpected response: {other:?}"),
-        }
-    });
-}
-
-#[test]
-fn test_basic_call_flow() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-
-        let response1 = request(writer, reader, Request::ApiVersionCheck {});
-        assert!(matches!(response1, Response::ApiVersionCheck(_)));
-
-        let response2 =
-            request(writer, reader, Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }));
-        assert!(matches!(response2, Response::SetConfig(_)));
-
-        let response3 =
-            request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() });
-        assert!(matches!(response3, Response::ListMacros(Ok(_))));
-    });
-}
-
-#[test]
-fn test_expand_nonexistent_macro() {
-    with_server(|writer, reader| {
-        let dylib_path = proc_macro_test_dylib_path();
-
-        let version_response = request(writer, reader, Request::ApiVersionCheck {});
-        let Response::ApiVersionCheck(version) = version_response else {
-            panic!("expected version check response");
-        };
-
-        let mut span_data_table = SpanDataIndexMap::default();
-        let macro_body = create_empty_token_tree(version, &mut span_data_table);
-
-        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
-            lib: dylib_path,
-            env: vec![],
-            current_dir: None,
-            data: ExpandMacroData {
-                macro_body,
-                macro_name: "NonexistentMacro".to_owned(),
-                attributes: None,
-                has_global_spans: ExpnGlobals {
-                    serialize: version >= 3,
-                    def_site: 0,
-                    call_site: 0,
-                    mixed_site: 0,
-                },
-                span_data_table: vec![],
-            },
-        }));
-
-        let response = request(writer, reader, expand_request);
-
-        match response {
-            Response::ExpandMacro(Err(PanicMessage(msg))) => {
-                expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg)
-            }
-            other => panic!("expected error for nonexistent macro, got: {other:?}"),
-        }
-    });
-}
+//! Integration tests for the proc-macro-srv-cli main loop.
+//!
+//! These tests exercise the full client-server RPC procedure using in-memory
+//! channels without needing to spawn the actual server and client processes.
+
+#![cfg(feature = "sysroot-abi")]
+
+mod common {
+    pub(crate) mod utils;
+}
+
+use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use expect_test::expect;
+use proc_macro_api::{
+    ProtocolFormat::JsonLegacy,
+    legacy_protocol::msg::{
+        ExpandMacro, ExpandMacroData, ExpnGlobals, PanicMessage, Request, Response, ServerConfig,
+        SpanDataIndexMap, SpanMode,
+    },
+    version::CURRENT_API_VERSION,
+};
+
+#[test]
+fn test_version_check() {
+    with_server(JsonLegacy, |writer, reader| {
+        let response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+
+        match response {
+            Response::ApiVersionCheck(version) => {
+                assert_eq!(version, CURRENT_API_VERSION);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_list_macros() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+        let response = request(writer, reader, Request::ListMacros { dylib_path }, None).into();
+
+        let Response::ListMacros(Ok(macros)) = response else {
+            panic!("expected successful ListMacros response");
+        };
+
+        let mut macro_list: Vec<_> =
+            macros.iter().map(|(name, kind)| format!("{name} [{kind:?}]")).collect();
+        macro_list.sort();
+        let macro_list_str = macro_list.join("\n");
+
+        expect![[r#"
+            DeriveEmpty [CustomDerive]
+            DeriveError [CustomDerive]
+            DerivePanic [CustomDerive]
+            DeriveReemit [CustomDerive]
+            attr_error [Attr]
+            attr_noop [Attr]
+            attr_panic [Attr]
+            fn_like_clone_tokens [Bang]
+            fn_like_error [Bang]
+            fn_like_mk_idents [Bang]
+            fn_like_mk_literals [Bang]
+            fn_like_noop [Bang]
+            fn_like_panic [Bang]
+            fn_like_span_join [Bang]
+            fn_like_span_line_column [Bang]
+            fn_like_span_ops [Bang]"#]]
+        .assert_eq(¯o_list_str);
+    });
+}
+
+#[test]
+fn test_list_macros_invalid_path() {
+    with_server(JsonLegacy, |writer, reader| {
+        let response = request(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
+            None,
+        )
+        .into();
+
+        match response {
+            Response::ListMacros(Err(e)) => assert!(
+                e.starts_with("Cannot create expander for /nonexistent/path/to/dylib.so"),
+                "{e}"
+            ),
+            other => panic!("expected error response, got: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_set_config() {
+    with_server(JsonLegacy, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::Id };
+        let response = request(writer, reader, Request::SetConfig(config), None).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::Id);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_set_config_rust_analyzer_mode() {
+    with_server(JsonLegacy, |writer, reader| {
+        let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
+        let response = request(writer, reader, Request::SetConfig(config), None).into();
+
+        match response {
+            Response::SetConfig(returned_config) => {
+                assert_eq!(returned_config.span_mode, SpanMode::RustAnalyzer);
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_expand_macro_panic() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let Response::ApiVersionCheck(version) = version_response else {
+            panic!("expected version check response");
+        };
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body = create_empty_token_tree(version, &mut span_data_table);
+
+        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "fn_like_panic".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals {
+                    serialize: version >= 3,
+                    def_site: 0,
+                    call_site: 0,
+                    mixed_site: 0,
+                },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, expand_request, None).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                assert!(msg.contains("fn_like_panic"), "panic message should mention the macro");
+            }
+            Response::ExpandMacro(Ok(_)) => {
+                panic!("expected panic, but macro succeeded");
+            }
+            other => panic!("unexpected response: {other:?}"),
+        }
+    });
+}
+
+#[test]
+fn test_basic_call_flow() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let response1 = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        assert!(matches!(response1, Response::ApiVersionCheck(_)));
+
+        let response2 = request(
+            writer,
+            reader,
+            Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
+            None,
+        )
+        .into();
+        assert!(matches!(response2, Response::SetConfig(_)));
+
+        let response3 =
+            request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, None)
+                .into();
+        assert!(matches!(response3, Response::ListMacros(Ok(_))));
+    });
+}
+
+#[test]
+fn test_expand_nonexistent_macro() {
+    with_server(JsonLegacy, |writer, reader| {
+        let dylib_path = proc_macro_test_dylib_path();
+
+        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let Response::ApiVersionCheck(version) = version_response else {
+            panic!("expected version check response");
+        };
+
+        let mut span_data_table = SpanDataIndexMap::default();
+        let macro_body = create_empty_token_tree(version, &mut span_data_table);
+
+        let expand_request = Request::ExpandMacro(Box::new(ExpandMacro {
+            lib: dylib_path,
+            env: vec![],
+            current_dir: None,
+            data: ExpandMacroData {
+                macro_body,
+                macro_name: "NonexistentMacro".to_owned(),
+                attributes: None,
+                has_global_spans: ExpnGlobals {
+                    serialize: version >= 3,
+                    def_site: 0,
+                    call_site: 0,
+                    mixed_site: 0,
+                },
+                span_data_table: vec![],
+            },
+        }));
+
+        let response = request(writer, reader, expand_request, None).into();
+
+        match response {
+            Response::ExpandMacro(Err(PanicMessage(msg))) => {
+                expect!["proc-macro `NonexistentMacro` is missing"].assert_eq(&msg)
+            }
+            other => panic!("expected error for nonexistent macro, got: {other:?}"),
+        }
+    });
+}

From 095b0138028d51e45ca987e0b0b1dcf4e16a876d Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Sun, 18 Jan 2026 20:38:58 +0530
Subject: [PATCH 106/319] rename send and drive to request and receive and
 remove auto*

---
 .../tests/bidirectional_postcard.rs           |  48 +++----
 .../proc-macro-srv-cli/tests/common/utils.rs  | 118 ++++--------------
 .../proc-macro-srv-cli/tests/legacy_json.rs   |  37 +++---
 3 files changed, 67 insertions(+), 136 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
index 08e44bad3723..33ca1d791de7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs
@@ -4,7 +4,9 @@ mod common {
     pub(crate) mod utils;
 }
 
-use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use common::utils::{
+    create_empty_token_tree, proc_macro_test_dylib_path, request_bidirectional, with_server,
+};
 use expect_test::expect;
 use proc_macro_api::{
     ProtocolFormat::BidirectionalPostcardPrototype,
@@ -20,7 +22,7 @@ use proc_macro_api::{
 fn test_bidi_version_check_bidirectional() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let response =
-            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests);
 
         match response {
             Response::ApiVersionCheck(version) => {
@@ -35,9 +37,12 @@ fn test_bidi_version_check_bidirectional() {
 fn test_bidi_list_macros() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
-        let response =
-            request(writer, reader, Request::ListMacros { dylib_path }, Some(&reject_subrequests))
-                .into();
+        let response = request_bidirectional(
+            writer,
+            reader,
+            Request::ListMacros { dylib_path },
+            &reject_subrequests,
+        );
 
         let Response::ListMacros(Ok(macros)) = response else {
             panic!("expected successful ListMacros response");
@@ -72,13 +77,12 @@ fn test_bidi_list_macros() {
 #[test]
 fn test_bidi_list_macros_invalid_path() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
-        let response = request(
+        let response = request_bidirectional(
             writer,
             reader,
             Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
-            Some(&reject_subrequests),
-        )
-        .into();
+            reject_subrequests,
+        );
 
         match response {
             Response::ListMacros(Err(e)) => assert!(
@@ -95,7 +99,7 @@ fn test_bidi_set_config() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::Id };
         let response =
-            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests);
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -111,7 +115,7 @@ fn test_bidi_set_config_rust_analyzer_mode() {
     with_server(BidirectionalPostcardPrototype, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
         let response =
-            request(writer, reader, Request::SetConfig(config), Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::SetConfig(config), reject_subrequests);
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -144,7 +148,7 @@ fn test_bidi_expand_macro_panic() {
             },
         }));
 
-        let response = request(writer, reader, request1, Some(&reject_subrequests)).into();
+        let response = request_bidirectional(writer, reader, request1, reject_subrequests);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {
@@ -161,25 +165,23 @@ fn test_bidi_basic_call_flow() {
         let dylib_path = proc_macro_test_dylib_path();
 
         let response1 =
-            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests);
         assert!(matches!(response1, Response::ApiVersionCheck(_)));
 
-        let response2 = request(
+        let response2 = request_bidirectional(
             writer,
             reader,
             Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
-            Some(&reject_subrequests),
-        )
-        .into();
+            reject_subrequests,
+        );
         assert!(matches!(response2, Response::SetConfig(_)));
 
-        let response3 = request(
+        let response3 = request_bidirectional(
             writer,
             reader,
             Request::ListMacros { dylib_path: dylib_path.clone() },
-            Some(&reject_subrequests),
-        )
-        .into();
+            reject_subrequests,
+        );
         assert!(matches!(response3, Response::ListMacros(Ok(_))));
     });
 }
@@ -190,7 +192,7 @@ fn test_bidi_expand_nonexistent_macro() {
         let dylib_path = proc_macro_test_dylib_path();
 
         let version_response =
-            request(writer, reader, Request::ApiVersionCheck {}, Some(&reject_subrequests)).into();
+            request_bidirectional(writer, reader, Request::ApiVersionCheck {}, reject_subrequests);
         let Response::ApiVersionCheck(version) = version_response else {
             panic!("expected version check response");
         };
@@ -211,7 +213,7 @@ fn test_bidi_expand_nonexistent_macro() {
             },
         }));
 
-        let response = request(writer, reader, expand_request, Some(&reject_subrequests)).into();
+        let response = request_bidirectional(writer, reader, expand_request, reject_subrequests);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
index 63b3a74aa4e8..85c394734b33 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs
@@ -198,21 +198,22 @@ trait TestProtocol {
     type Request;
     type Response;
 
-    fn send(&self, writer: &mut dyn Write, req: Self::Request);
-    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response;
+    fn request(&self, writer: &mut dyn Write, req: Self::Request);
+    fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> Self::Response;
 }
 
+#[allow(dead_code)]
 struct JsonLegacy;
 
 impl TestProtocol for JsonLegacy {
     type Request = Request;
     type Response = Response;
 
-    fn send(&self, writer: &mut dyn Write, req: Request) {
+    fn request(&self, writer: &mut dyn Write, req: Request) {
         req.write::(writer).expect("failed to write request");
     }
 
-    fn drive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response {
+    fn receive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response {
         let mut buf = String::new();
         Response::read::(reader, &mut buf)
             .expect("failed to read response")
@@ -220,6 +221,7 @@ impl TestProtocol for JsonLegacy {
     }
 }
 
+#[allow(dead_code)]
 struct PostcardBidirectional
 where
     F: Fn(SubRequest) -> Result,
@@ -234,12 +236,12 @@ where
     type Request = BiRequest;
     type Response = BiResponse;
 
-    fn send(&self, writer: &mut dyn Write, req: BiRequest) {
+    fn request(&self, writer: &mut dyn Write, req: BiRequest) {
         let msg = BidirectionalMessage::Request(req);
         msg.write::(writer).expect("failed to write request");
     }
 
-    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse {
+    fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse {
         let mut buf = Vec::new();
 
         loop {
@@ -260,98 +262,28 @@ where
     }
 }
 
-pub(crate) fn request(
+#[allow(dead_code)]
+pub(crate) fn request_legacy(
     writer: &mut dyn Write,
     reader: &mut dyn BufRead,
-    request: impl Into,
-    callback: Option<&dyn Fn(SubRequest) -> Result>,
-) -> AutoResponse {
-    let protocol = match callback {
-        None => AutoProtocol::Legacy(JsonLegacy),
-        Some(cb) => AutoProtocol::Bidirectional(PostcardBidirectional { callback: cb }),
-    };
-
-    protocol.send(writer, request.into());
-    protocol.drive(reader, writer)
+    request: Request,
+) -> Response {
+    let protocol = JsonLegacy;
+    protocol.request(writer, request);
+    protocol.receive(reader, writer)
 }
 
-enum AutoProtocol
+#[allow(dead_code)]
+pub(crate) fn request_bidirectional(
+    writer: &mut dyn Write,
+    reader: &mut dyn BufRead,
+    request: BiRequest,
+    callback: F,
+) -> BiResponse
 where
     F: Fn(SubRequest) -> Result,
 {
-    Legacy(JsonLegacy),
-    Bidirectional(PostcardBidirectional),
-}
-
-impl TestProtocol for AutoProtocol
-where
-    F: Fn(SubRequest) -> Result,
-{
-    type Request = AutoRequest;
-    type Response = AutoResponse;
-
-    fn send(&self, writer: &mut dyn Write, req: AutoRequest) {
-        match (self, req) {
-            (AutoProtocol::Legacy(p), AutoRequest::Legacy(r)) => {
-                p.send(writer, r);
-            }
-            (AutoProtocol::Bidirectional(p), AutoRequest::Bidirectional(r)) => {
-                p.send(writer, r);
-            }
-            (AutoProtocol::Legacy(_), AutoRequest::Bidirectional(_)) => {
-                panic!("bidirectional request used with legacy protocol");
-            }
-            (AutoProtocol::Bidirectional(_), AutoRequest::Legacy(_)) => {
-                panic!("legacy request used with bidirectional protocol");
-            }
-        }
-    }
-
-    fn drive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> AutoResponse {
-        match self {
-            AutoProtocol::Legacy(p) => AutoResponse::Legacy(p.drive(reader, writer)),
-            AutoProtocol::Bidirectional(p) => AutoResponse::Bidirectional(p.drive(reader, writer)),
-        }
-    }
-}
-
-pub(crate) enum AutoRequest {
-    Legacy(Request),
-    Bidirectional(BiRequest),
-}
-
-#[derive(Debug)]
-pub(crate) enum AutoResponse {
-    Legacy(Response),
-    Bidirectional(BiResponse),
-}
-
-impl From for AutoRequest {
-    fn from(req: Request) -> AutoRequest {
-        AutoRequest::Legacy(req)
-    }
-}
-
-impl From for AutoRequest {
-    fn from(req: BiRequest) -> AutoRequest {
-        AutoRequest::Bidirectional(req)
-    }
-}
-
-impl From for Response {
-    fn from(res: AutoResponse) -> Response {
-        match res {
-            AutoResponse::Legacy(res) => res,
-            _ => panic!("Should be legacy response"),
-        }
-    }
-}
-
-impl From for BiResponse {
-    fn from(res: AutoResponse) -> BiResponse {
-        match res {
-            AutoResponse::Bidirectional(res) => res,
-            _ => panic!("Should be bidirectional response"),
-        }
-    }
+    let protocol = PostcardBidirectional { callback };
+    protocol.request(writer, request);
+    protocol.receive(reader, writer)
 }
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
index 8daee7b2bceb..c0dbfd1679f7 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
@@ -9,7 +9,9 @@ mod common {
     pub(crate) mod utils;
 }
 
-use common::utils::{create_empty_token_tree, proc_macro_test_dylib_path, request, with_server};
+use common::utils::{
+    create_empty_token_tree, proc_macro_test_dylib_path, request_legacy, with_server,
+};
 use expect_test::expect;
 use proc_macro_api::{
     ProtocolFormat::JsonLegacy,
@@ -23,7 +25,7 @@ use proc_macro_api::{
 #[test]
 fn test_version_check() {
     with_server(JsonLegacy, |writer, reader| {
-        let response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let response = request_legacy(writer, reader, Request::ApiVersionCheck {});
 
         match response {
             Response::ApiVersionCheck(version) => {
@@ -38,7 +40,7 @@ fn test_version_check() {
 fn test_list_macros() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
-        let response = request(writer, reader, Request::ListMacros { dylib_path }, None).into();
+        let response = request_legacy(writer, reader, Request::ListMacros { dylib_path });
 
         let Response::ListMacros(Ok(macros)) = response else {
             panic!("expected successful ListMacros response");
@@ -73,13 +75,11 @@ fn test_list_macros() {
 #[test]
 fn test_list_macros_invalid_path() {
     with_server(JsonLegacy, |writer, reader| {
-        let response = request(
+        let response = request_legacy(
             writer,
             reader,
             Request::ListMacros { dylib_path: "/nonexistent/path/to/dylib.so".into() },
-            None,
-        )
-        .into();
+        );
 
         match response {
             Response::ListMacros(Err(e)) => assert!(
@@ -95,7 +95,7 @@ fn test_list_macros_invalid_path() {
 fn test_set_config() {
     with_server(JsonLegacy, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::Id };
-        let response = request(writer, reader, Request::SetConfig(config), None).into();
+        let response = request_legacy(writer, reader, Request::SetConfig(config));
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -110,7 +110,7 @@ fn test_set_config() {
 fn test_set_config_rust_analyzer_mode() {
     with_server(JsonLegacy, |writer, reader| {
         let config = ServerConfig { span_mode: SpanMode::RustAnalyzer };
-        let response = request(writer, reader, Request::SetConfig(config), None).into();
+        let response = request_legacy(writer, reader, Request::SetConfig(config));
 
         match response {
             Response::SetConfig(returned_config) => {
@@ -126,7 +126,7 @@ fn test_expand_macro_panic() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
 
-        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {});
         let Response::ApiVersionCheck(version) = version_response else {
             panic!("expected version check response");
         };
@@ -152,7 +152,7 @@ fn test_expand_macro_panic() {
             },
         }));
 
-        let response = request(writer, reader, expand_request, None).into();
+        let response = request_legacy(writer, reader, expand_request);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {
@@ -171,21 +171,18 @@ fn test_basic_call_flow() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
 
-        let response1 = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let response1 = request_legacy(writer, reader, Request::ApiVersionCheck {});
         assert!(matches!(response1, Response::ApiVersionCheck(_)));
 
-        let response2 = request(
+        let response2 = request_legacy(
             writer,
             reader,
             Request::SetConfig(ServerConfig { span_mode: SpanMode::Id }),
-            None,
-        )
-        .into();
+        );
         assert!(matches!(response2, Response::SetConfig(_)));
 
         let response3 =
-            request(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() }, None)
-                .into();
+            request_legacy(writer, reader, Request::ListMacros { dylib_path: dylib_path.clone() });
         assert!(matches!(response3, Response::ListMacros(Ok(_))));
     });
 }
@@ -195,7 +192,7 @@ fn test_expand_nonexistent_macro() {
     with_server(JsonLegacy, |writer, reader| {
         let dylib_path = proc_macro_test_dylib_path();
 
-        let version_response = request(writer, reader, Request::ApiVersionCheck {}, None).into();
+        let version_response = request_legacy(writer, reader, Request::ApiVersionCheck {});
         let Response::ApiVersionCheck(version) = version_response else {
             panic!("expected version check response");
         };
@@ -221,7 +218,7 @@ fn test_expand_nonexistent_macro() {
             },
         }));
 
-        let response = request(writer, reader, expand_request, None).into();
+        let response = request_legacy(writer, reader, expand_request);
 
         match response {
             Response::ExpandMacro(Err(PanicMessage(msg))) => {

From 29aad0493b082c5d36a006fa39656d61b2c3ddc3 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 19 Jan 2026 09:58:15 +0200
Subject: [PATCH 107/319] Cache `Clauses::empty()`

---
 .../rust-analyzer/crates/hir-ty/src/next_solver/predicate.rs | 5 ++---
 1 file changed, 2 insertions(+), 3 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 5758e2dc7e93..6f4fae707317 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
@@ -273,9 +273,8 @@ impl<'db> std::fmt::Debug for Clauses<'db> {
 
 impl<'db> Clauses<'db> {
     #[inline]
-    pub fn empty(_interner: DbInterner<'db>) -> Self {
-        // FIXME: Get from a static.
-        Self::new_from_slice(&[])
+    pub fn empty(interner: DbInterner<'db>) -> Self {
+        interner.default_types().empty.clauses
     }
 
     #[inline]

From 635bacf46627409cb4569b93d38bdf23282966f3 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Sat, 15 Nov 2025 10:01:56 +0100
Subject: [PATCH 108/319] const-eval: always do mem-to-mem copies if there
 might be padding involved

---
 .../rustc_const_eval/src/interpret/place.rs   | 25 +++++++++++--
 .../src/interpret/projection.rs               |  2 +-
 tests/ui/consts/const-eval/ptr_fragments.rs   |  1 +
 .../const-eval/ptr_fragments_in_final.rs      | 36 +++++++++++++++++++
 .../const-eval/ptr_fragments_in_final.stderr  | 10 +++++-
 5 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index d472c14253b5..70d10751dbb8 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -884,10 +884,29 @@ where
                 dest.layout().ty,
             );
         }
+        // If the source has padding, we want to always do the mem-to-mem copy to ensure consistent
+        // padding in the target independent of layout choices.
+        let src_has_padding = match src.layout().backend_repr {
+            BackendRepr::Scalar(_) => false,
+            BackendRepr::ScalarPair(left, right) => {
+                let left_size = left.size(self);
+                let right_size = right.size(self);
+                // We have padding if the sizes don't add up to the total.
+                left_size + right_size != src.layout().size
+            }
+            // Everything else can only exist in memory anyway.
+            _ => true,
+        };
 
-        // Let us see if the layout is simple so we take a shortcut,
-        // avoid force_allocation.
-        let src = match self.read_immediate_raw(src)? {
+        let src_val = if src_has_padding {
+            // Do our best to get an mplace. If there's no mplace, then this is stored as an
+            // "optimized" local, so its padding is definitely uninitialized and we are fine.
+            src.to_op(self)?.as_mplace_or_imm()
+        } else {
+            // Do our best to get an immediate, to avoid having to force_allocate the destination.
+            self.read_immediate_raw(src)?
+        };
+        let src = match src_val {
             Right(src_val) => {
                 assert!(!src.layout().is_unsized());
                 assert!(!dest.layout().is_unsized());
diff --git a/compiler/rustc_const_eval/src/interpret/projection.rs b/compiler/rustc_const_eval/src/interpret/projection.rs
index 027e634ef7f7..db72c02e308c 100644
--- a/compiler/rustc_const_eval/src/interpret/projection.rs
+++ b/compiler/rustc_const_eval/src/interpret/projection.rs
@@ -97,7 +97,7 @@ pub trait Projectable<'tcx, Prov: Provenance>: Sized + std::fmt::Debug {
     }
 
     /// Convert this to an `OpTy`. This might be an irreversible transformation, but is useful for
-    /// reading from this thing.
+    /// reading from this thing. This will never actually do a read from memory!
     fn to_op>(
         &self,
         ecx: &InterpCx<'tcx, M>,
diff --git a/tests/ui/consts/const-eval/ptr_fragments.rs b/tests/ui/consts/const-eval/ptr_fragments.rs
index d27804084d73..f4d92127e189 100644
--- a/tests/ui/consts/const-eval/ptr_fragments.rs
+++ b/tests/ui/consts/const-eval/ptr_fragments.rs
@@ -70,6 +70,7 @@ const _PARTIAL_OVERWRITE: () = {
 #[allow(dead_code)]
 #[cfg(not(target_arch = "s390x"))] // u128 is less aligned on s390x, removing the padding
 fn fragment_in_dst_padding_gets_overwritten() {
+    // We can't use `repr(align)` here as that would make this not a `ScalarPair` any more.
     #[repr(C)]
     struct Pair {
         x: u128,
diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs
index 4037a2d23722..76f292c3f8b5 100644
--- a/tests/ui/consts/const-eval/ptr_fragments_in_final.rs
+++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.rs
@@ -37,4 +37,40 @@ const MIXED_PTR: MaybeUninit<*const u8> = { //~ERROR: partial pointer in final v
     }
 };
 
+/// This has pointer bytes in the padding of the memory that the final value is read from.
+/// To ensure consistent behavior, we want to *always* copy that padding, even if the value
+/// could be represented as a more efficient ScalarPair. Hence this must fail to compile.
+fn fragment_in_padding() -> impl Copy {
+    // We can't use `repr(align)` here as that would make this not a `ScalarPair` any more.
+    #[repr(C)]
+    #[derive(Clone, Copy)]
+    struct Thing {
+        x: u128,
+        y: usize,
+        // at least one pointer worth of padding
+    }
+    // Ensure there is indeed padding.
+    const _: () = assert!(mem::size_of::() > 16 + mem::size_of::());
+
+    #[derive(Clone, Copy)]
+    union PreservePad {
+        thing: Thing,
+        bytes: [u8; mem::size_of::()],
+    }
+
+    const A: Thing = unsafe { //~ERROR: partial pointer in final value
+        let mut buffer = [PreservePad { bytes: [0u8; mem::size_of::()] }; 2];
+        // The offset half a pointer from the end, so that copying a `Thing` copies exactly
+        // half the pointer.
+        let offset = mem::size_of::() - mem::size_of::()/2;
+        // Ensure this is inside the padding.
+        assert!(offset >= std::mem::offset_of!(Thing, y) + mem::size_of::());
+
+        (&raw mut buffer).cast::<&i32>().byte_add(offset).write_unaligned(&1);
+        buffer[0].thing
+    };
+
+    A
+}
+
 fn main() {}
diff --git a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr
index 41a822416581..de0cd4db7e15 100644
--- a/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr
+++ b/tests/ui/consts/const-eval/ptr_fragments_in_final.stderr
@@ -14,5 +14,13 @@ LL | const MIXED_PTR: MaybeUninit<*const u8> = {
    |
    = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
 
-error: aborting due to 2 previous errors
+error: encountered partial pointer in final value of constant
+  --> $DIR/ptr_fragments_in_final.rs:61:5
+   |
+LL |     const A: Thing = unsafe {
+   |     ^^^^^^^^^^^^^^
+   |
+   = note: while pointers can be broken apart into individual bytes during const-evaluation, only complete pointers (with all their bytes in the right order) are supported in the final value
+
+error: aborting due to 3 previous errors
 

From 5a76a60d016d7ed5cd5fcecc131010dbce8148e0 Mon Sep 17 00:00:00 2001
From: Ralf Jung 
Date: Mon, 17 Nov 2025 09:10:45 +0100
Subject: [PATCH 109/319] add fast-path for wide pointers

---
 .../rustc_const_eval/src/interpret/place.rs     | 17 +++++++++++++----
 1 file changed, 13 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_const_eval/src/interpret/place.rs b/compiler/rustc_const_eval/src/interpret/place.rs
index 70d10751dbb8..fb07d5f0d0d6 100644
--- a/compiler/rustc_const_eval/src/interpret/place.rs
+++ b/compiler/rustc_const_eval/src/interpret/place.rs
@@ -5,8 +5,8 @@
 use either::{Either, Left, Right};
 use rustc_abi::{BackendRepr, HasDataLayout, Size};
 use rustc_data_structures::assert_matches;
-use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::TyAndLayout;
+use rustc_middle::ty::{self, Ty};
 use rustc_middle::{bug, mir, span_bug};
 use tracing::field::Empty;
 use tracing::{instrument, trace};
@@ -884,18 +884,27 @@ where
                 dest.layout().ty,
             );
         }
-        // If the source has padding, we want to always do the mem-to-mem copy to ensure consistent
+        // If the source has padding, we want to always do a mem-to-mem copy to ensure consistent
         // padding in the target independent of layout choices.
         let src_has_padding = match src.layout().backend_repr {
             BackendRepr::Scalar(_) => false,
+            BackendRepr::ScalarPair(left, right)
+                if matches!(src.layout().ty.kind(), ty::Ref(..) | ty::RawPtr(..)) =>
+            {
+                // Wide pointers never have padding, so we can avoid calling `size()`.
+                debug_assert_eq!(left.size(self) + right.size(self), src.layout().size);
+                false
+            }
             BackendRepr::ScalarPair(left, right) => {
                 let left_size = left.size(self);
                 let right_size = right.size(self);
                 // We have padding if the sizes don't add up to the total.
                 left_size + right_size != src.layout().size
             }
-            // Everything else can only exist in memory anyway.
-            _ => true,
+            // Everything else can only exist in memory anyway, so it doesn't matter.
+            BackendRepr::SimdVector { .. }
+            | BackendRepr::ScalableVector { .. }
+            | BackendRepr::Memory { .. } => true,
         };
 
         let src_val = if src_has_padding {

From e07da1fb86ce0ccbe6c5b542f545fd304efb2e3f Mon Sep 17 00:00:00 2001
From: Youseok Yang 
Date: Tue, 20 Jan 2026 07:16:59 +0000
Subject: [PATCH 110/319] feat(hir-ty): add method references_only_ty_error to
 detect type errors

Add a new method `references_only_ty_error` to the `Ty` implementation
to determine if a type contains only type errors, ignoring const and
lifetime errors. Enhance test suite for const generic method resolution.
---
 .../hir-ty/src/method_resolution/probe.rs     |  6 +-
 .../crates/hir-ty/src/next_solver/ty.rs       | 19 +++++
 .../hir-ty/src/tests/regression/new_solver.rs | 71 ++++++++++++++++++-
 3 files changed, 92 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
index 42a590e8b4cb..fdd501723fb5 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
@@ -1246,9 +1246,9 @@ impl<'a, 'db, Choice: ProbeChoice<'db>> ProbeContext<'a, 'db, Choice> {
             .filter(|step| step.reachable_via_deref)
             .filter(|step| {
                 debug!("pick_all_method: step={:?}", step);
-                // skip types that are from a type error or that would require dereferencing
-                // a raw pointer
-                !step.self_ty.value.value.references_non_lt_error() && !step.from_unsafe_deref
+                // Skip types with type errors (but not const/lifetime errors, which are
+                // often spurious due to incomplete const evaluation) and raw pointer derefs.
+                !step.self_ty.value.value.references_only_ty_error() && !step.from_unsafe_deref
             })
             .try_for_each(|step| {
                 let InferOk { value: self_ty, obligations: instantiate_self_ty_obligations } = self
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
index 66a24d394990..1173028a1092 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/ty.rs
@@ -508,6 +508,11 @@ impl<'db> Ty<'db> {
         references_non_lt_error(&self)
     }
 
+    /// Whether the type contains a type error (ignoring const and lifetime errors).
+    pub fn references_only_ty_error(self) -> bool {
+        references_only_ty_error(&self)
+    }
+
     pub fn callable_sig(self, interner: DbInterner<'db>) -> Option>> {
         match self.kind() {
             TyKind::FnDef(callable, args) => {
@@ -777,6 +782,20 @@ impl<'db> TypeVisitor> for ReferencesNonLifetimeError {
     }
 }
 
+pub fn references_only_ty_error<'db, T: TypeVisitableExt>>(t: &T) -> bool {
+    t.references_error() && t.visit_with(&mut ReferencesOnlyTyError).is_break()
+}
+
+struct ReferencesOnlyTyError;
+
+impl<'db> TypeVisitor> for ReferencesOnlyTyError {
+    type Result = ControlFlow<()>;
+
+    fn visit_ty(&mut self, ty: Ty<'db>) -> Self::Result {
+        if ty.is_ty_error() { ControlFlow::Break(()) } else { ty.super_visit_with(self) }
+    }
+}
+
 impl<'db> std::fmt::Debug for Ty<'db> {
     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         self.inner().internee.fmt(f)
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
index be6ab23ad761..f47a26d429fd 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/regression/new_solver.rs
@@ -471,7 +471,76 @@ fn foo() {
             244..246 '_x': {unknown}
             249..257 'to_bytes': fn to_bytes() -> [u8; _]
             249..259 'to_bytes()': [u8; _]
-            249..268 'to_byt..._vec()': {unknown}
+            249..268 'to_byt..._vec()': Vec<<[u8; _] as Foo>::Item>
+        "#]],
+    );
+}
+
+#[test]
+fn regression_21315() {
+    check_infer(
+        r#"
+struct Consts;
+impl Consts { const MAX: usize = 0; }
+
+struct Between(T);
+
+impl Between {
+    fn sep_once(self, _sep: &str, _other: Self) -> Self {
+        self
+    }
+}
+
+trait Parser: Sized {
+    fn at_least(self) -> Between {
+        Between(self)
+    }
+    fn at_most(self) -> Between<0, N, Self> {
+        Between(self)
+    }
+}
+
+impl Parser for char {}
+
+fn test_at_least() {
+    let num = '9'.at_least::<1>();
+    let _ver = num.sep_once(".", num);
+}
+
+fn test_at_most() {
+    let num = '9'.at_most::<1>();
+}
+    "#,
+        expect![[r#"
+            48..49 '0': usize
+            182..186 'self': Between
+            188..192 '_sep': &'? str
+            200..206 '_other': Between
+            222..242 '{     ...     }': Between
+            232..236 'self': Between
+            300..304 'self': Self
+            343..372 '{     ...     }': Between
+            353..360 'Between': fn Between(Self) -> Between
+            353..366 'Between(self)': Between
+            361..365 'self': Self
+            404..408 'self': Self
+            433..462 '{     ...     }': Between<0, N, Self>
+            443..450 'Between': fn Between<0, N, Self>(Self) -> Between<0, N, Self>
+            443..456 'Between(self)': Between<0, N, Self>
+            451..455 'self': Self
+            510..587 '{     ...um); }': ()
+            520..523 'num': Between<1, _, char>
+            526..529 ''9'': char
+            526..545 ''9'.at...:<1>()': Between<1, _, char>
+            555..559 '_ver': Between<1, _, char>
+            562..565 'num': Between<1, _, char>
+            562..584 'num.se..., num)': Between<1, _, char>
+            575..578 '"."': &'static str
+            580..583 'num': Between<1, _, char>
+            607..644 '{     ...>(); }': ()
+            617..620 'num': Between<0, 1, char>
+            623..626 ''9'': char
+            623..641 ''9'.at...:<1>()': Between<0, 1, char>
         "#]],
     );
 }

From 61b9b33c1cd8857c2e79caba782ed13edb654100 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Tue, 20 Jan 2026 23:02:34 +0800
Subject: [PATCH 111/319] Fix demorgan applicable on pattern conditional

Example
---
```rust
fn f() {
    if let 1 = 1 &&$0 true { }
}
```

**Before this PR**

```rust
fn f() {
    if !(!let 1 = 1 || false) { }
}
```

**After this PR**

Assist not applicable
---
 .../ide-assists/src/handlers/apply_demorgan.rs   | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
index d193e8a9d8dc..80d0a6da1243 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/apply_demorgan.rs
@@ -3,7 +3,7 @@ use std::collections::VecDeque;
 use ide_db::{
     assists::GroupLabel,
     famous_defs::FamousDefs,
-    syntax_helpers::node_ext::{for_each_tail_expr, walk_expr},
+    syntax_helpers::node_ext::{for_each_tail_expr, is_pattern_cond, walk_expr},
 };
 use syntax::{
     NodeOrToken, SyntaxKind, T,
@@ -69,6 +69,10 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext<'_>) -> Opti
         }
     }
 
+    if is_pattern_cond(bin_expr.clone().into()) {
+        return None;
+    }
+
     let op = bin_expr.op_kind()?;
     let (inv_token, prec) = match op {
         ast::BinaryOp::LogicOp(ast::LogicOp::And) => (SyntaxKind::PIPE2, ExprPrecedence::LOr),
@@ -375,6 +379,16 @@ fn f() { !(S <= S || S < S) }
         )
     }
 
+    #[test]
+    fn demorgan_doesnt_handles_pattern() {
+        check_assist_not_applicable(
+            apply_demorgan,
+            r#"
+fn f() { if let 1 = 1 &&$0 true { } }
+"#,
+        );
+    }
+
     #[test]
     fn demorgan_on_not() {
         check_assist(

From 8e937d4f4a105d01603e0e2d9b8a94ebce9f1079 Mon Sep 17 00:00:00 2001
From: Roberto Aloi 
Date: Wed, 21 Jan 2026 10:52:07 +0100
Subject: [PATCH 112/319] Bump notify from 8.0.0. to 8.2.0

---
 src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
index bd6c8331e66c..ce7ea53b5373 100644
--- a/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
+++ b/src/tools/rust-analyzer/crates/vfs-notify/Cargo.toml
@@ -16,7 +16,7 @@ doctest = false
 tracing.workspace = true
 walkdir = "2.5.0"
 crossbeam-channel.workspace = true
-notify = "8.0.0"
+notify = "8.2.0"
 rayon = "1.10.0"
 
 stdx.workspace = true

From 6753155bd67536b5469113e957adb93eff75dd27 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Thu, 22 Jan 2026 04:25:38 +0000
Subject: [PATCH 113/319] Prepare for merging from rust-lang/rust

This updates the rust-version file to 004d710faff53f8764a1cf69d87a5a5963850b60.
---
 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 a6ccd9bab393..1fe86330b4a8 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-b6fdaf2a15736cbccf248b532f48e33179614d40
+004d710faff53f8764a1cf69d87a5a5963850b60

From 4e9e37ee5889dbcfd8af30f6693adfe1c61c6f95 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Fri, 23 Jan 2026 11:57:14 +0100
Subject: [PATCH 114/319] internal: Add tests for rust-lang/rust#146972

---
 .../crates/hir-def/src/nameres/tests.rs       |  2 +-
 .../hir-def/src/nameres/tests/imports.rs      | 63 +++++++++++++++++++
 .../hir-def/src/nameres/tests/primitives.rs   | 23 -------
 3 files changed, 64 insertions(+), 24 deletions(-)
 create mode 100644 src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs
 delete mode 100644 src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
index 23d60d58f085..fe55252e2540 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests.rs
@@ -1,8 +1,8 @@
 mod globs;
+mod imports;
 mod incremental;
 mod macros;
 mod mod_resolution;
-mod primitives;
 
 use base_db::RootQueryDb;
 use expect_test::{Expect, expect};
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs
new file mode 100644
index 000000000000..b1960b785a83
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/imports.rs
@@ -0,0 +1,63 @@
+use super::*;
+
+#[test]
+fn kw_path_renames() {
+    check(
+        r#"
+macro_rules! m {
+    () => {
+        pub use $crate as dollar_crate;
+        pub use $crate::{self as self_dollar_crate};
+    };
+}
+
+pub use self as this;
+pub use crate as krate;
+
+pub use crate::{self as self_krate};
+m!();
+
+mod foo {
+    pub use super as zuper;
+    pub use super::{self as self_zuper};
+}
+"#,
+        expect![[r#"
+            crate
+            - dollar_crate : type (import)
+            - foo : type
+            - krate : type (import)
+            - self_dollar_crate : type (import)
+            - self_krate : type (import)
+            - this : type (import)
+            - (legacy) m : macro!
+
+            crate::foo
+            - self_zuper : type (import)
+            - zuper : type (import)
+            - (legacy) m : macro!
+        "#]],
+    );
+}
+
+#[test]
+fn primitive_reexport() {
+    check(
+        r#"
+//- /lib.rs
+mod foo;
+use foo::int;
+
+//- /foo.rs
+pub use i32 as int;
+"#,
+        expect![[r#"
+            crate
+            - foo : type
+            - int : type (import)
+
+            crate::foo
+            - int : type (import)
+        "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs b/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs
deleted file mode 100644
index 861690238d47..000000000000
--- a/src/tools/rust-analyzer/crates/hir-def/src/nameres/tests/primitives.rs
+++ /dev/null
@@ -1,23 +0,0 @@
-use super::*;
-
-#[test]
-fn primitive_reexport() {
-    check(
-        r#"
-//- /lib.rs
-mod foo;
-use foo::int;
-
-//- /foo.rs
-pub use i32 as int;
-"#,
-        expect![[r#"
-            crate
-            - foo : type
-            - int : type (import)
-
-            crate::foo
-            - int : type (import)
-        "#]],
-    );
-}

From 36c0fb5a2dbb8b33407d814585e610d05d555030 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez 
Date: Fri, 23 Jan 2026 15:57:18 +0100
Subject: [PATCH 115/319] Add new `byte_value` and `char_value` methods to
 `proc_macro::Literal`

---
 library/proc_macro/src/lib.rs | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs
index a005f743ddfa..2a75c4489095 100644
--- a/library/proc_macro/src/lib.rs
+++ b/library/proc_macro/src/lib.rs
@@ -54,7 +54,9 @@ use std::{error, fmt};
 pub use diagnostic::{Diagnostic, Level, MultiSpan};
 #[unstable(feature = "proc_macro_value", issue = "136652")]
 pub use rustc_literal_escaper::EscapeError;
-use rustc_literal_escaper::{MixedUnit, unescape_byte_str, unescape_c_str, unescape_str};
+use rustc_literal_escaper::{
+    MixedUnit, unescape_byte, unescape_byte_str, unescape_c_str, unescape_char, unescape_str,
+};
 #[unstable(feature = "proc_macro_totokens", issue = "130977")]
 pub use to_tokens::ToTokens;
 
@@ -1443,6 +1445,28 @@ impl Literal {
         })
     }
 
+    /// Returns the unescaped char value if the current literal is a char.
+    #[unstable(feature = "proc_macro_value", issue = "136652")]
+    pub fn byte_value(&self) -> Result {
+        self.0.symbol.with(|symbol| match self.0.kind {
+            bridge::LitKind::Char => {
+                unescape_byte(symbol).map_err(ConversionErrorKind::FailedToUnescape)
+            }
+            _ => Err(ConversionErrorKind::InvalidLiteralKind),
+        })
+    }
+
+    /// Returns the unescaped char value if the current literal is a char.
+    #[unstable(feature = "proc_macro_value", issue = "136652")]
+    pub fn char_value(&self) -> Result {
+        self.0.symbol.with(|symbol| match self.0.kind {
+            bridge::LitKind::Char => {
+                unescape_char(symbol).map_err(ConversionErrorKind::FailedToUnescape)
+            }
+            _ => Err(ConversionErrorKind::InvalidLiteralKind),
+        })
+    }
+
     /// Returns the unescaped string value if the current literal is a string or a string literal.
     #[unstable(feature = "proc_macro_value", issue = "136652")]
     pub fn str_value(&self) -> Result {

From 33b77c8886ebc14cea9965eabd724265c2955266 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= 
Date: Fri, 23 Jan 2026 17:53:48 +0100
Subject: [PATCH 116/319] Pass on the `feedable` query modifier to macros

---
 compiler/rustc_macros/src/query.rs | 1 +
 1 file changed, 1 insertion(+)

diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs
index 1ad9bbc3b4b3..b13361b3e6a5 100644
--- a/compiler/rustc_macros/src/query.rs
+++ b/compiler/rustc_macros/src/query.rs
@@ -373,6 +373,7 @@ pub(super) fn rustc_queries(input: TokenStream) -> TokenStream {
             no_hash,
             anon,
             eval_always,
+            feedable,
             depth_limit,
             separate_provide_extern,
             return_result_from_ensure_ok,

From bd91c887b5873e5fbc94f3b492c1af3355d10fe9 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 24 Jan 2026 11:39:14 +0800
Subject: [PATCH 117/319] Improve extract_function name

If the name contains `_`, it is likely to be descriptive

Example
---
```rust
fn foo(kind: i32) {
    let is_complex = $0kind != 0$0;
}
```

**Before this PR**

```rust
fn foo(kind: i32) {
    let is_complex = fun_name(kind);
}

fn fun_name(kind: i32) -> bool {
    kind != 0
}
```

**After this PR**

```rust
fn foo(kind: i32) {
    let is_complex = is_complex(kind);
}

fn is_complex(kind: i32) -> bool {
    kind != 0
}
```
---
 .../src/handlers/extract_function.rs          | 26 +++++++++++++++----
 1 file changed, 21 insertions(+), 5 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 294e5f7da8b3..2230c391cbad 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -120,7 +120,7 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
 
             let params = body.extracted_function_params(ctx, &container_info, locals_used);
 
-            let name = make_function_name(&semantics_scope);
+            let name = make_function_name(&semantics_scope, &body);
 
             let fun = Function {
                 name,
@@ -241,7 +241,10 @@ pub(crate) fn extract_function(acc: &mut Assists, ctx: &AssistContext<'_>) -> Op
     )
 }
 
-fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef {
+fn make_function_name(
+    semantics_scope: &hir::SemanticsScope<'_>,
+    body: &FunctionBody,
+) -> ast::NameRef {
     let mut names_in_scope = vec![];
     semantics_scope.process_all_names(&mut |name, _| {
         names_in_scope.push(
@@ -252,7 +255,10 @@ fn make_function_name(semantics_scope: &hir::SemanticsScope<'_>) -> ast::NameRef
 
     let default_name = "fun_name";
 
-    let mut name = default_name.to_owned();
+    let mut name = body
+        .suggest_name()
+        .filter(|name| name.contains('_'))
+        .unwrap_or_else(|| default_name.to_owned());
     let mut counter = 0;
     while names_in_scope.contains(&name) {
         counter += 1;
@@ -779,6 +785,16 @@ impl FunctionBody {
     fn contains_node(&self, node: &SyntaxNode) -> bool {
         self.contains_range(node.text_range())
     }
+
+    fn suggest_name(&self) -> Option {
+        if let Some(ast::Pat::IdentPat(pat)) = self.parent().and_then(ast::LetStmt::cast)?.pat()
+            && let Some(name) = pat.name().and_then(|it| it.ident_token())
+        {
+            Some(name.text().to_owned())
+        } else {
+            None
+        }
+    }
 }
 
 impl FunctionBody {
@@ -5430,12 +5446,12 @@ impl Struct {
 
 impl Trait for Struct {
     fn bar(&self) -> i32 {
-        let three_squared = fun_name();
+        let three_squared = three_squared();
         self.0 + three_squared
     }
 }
 
-fn $0fun_name() -> i32 {
+fn $0three_squared() -> i32 {
     3 * 3
 }
 "#,

From c0c6e2166d586d8b4b99afd3005725d9aa15f9ea Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Thu, 22 Jan 2026 20:22:06 -0800
Subject: [PATCH 118/319] make generic test invariant of function order

---
 tests/codegen-llvm/autodiff/generic.rs | 43 +++++++++++++++++---------
 1 file changed, 28 insertions(+), 15 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/generic.rs b/tests/codegen-llvm/autodiff/generic.rs
index 6f56460a2b6d..b31468c91c9c 100644
--- a/tests/codegen-llvm/autodiff/generic.rs
+++ b/tests/codegen-llvm/autodiff/generic.rs
@@ -1,6 +1,14 @@
 //@ compile-flags: -Zautodiff=Enable -Zautodiff=NoPostopt -C opt-level=3 -Clto=fat
 //@ no-prefer-dynamic
 //@ needs-enzyme
+//@ revisions: F32 F64 Main
+
+// Here we verify that the function `square` can be differentiated over f64.
+// This is interesting to test, since the user never calls `square` with f64, so on it's own rustc
+// would have no reason to monomorphize it that way. However, Enzyme needs the f64 version of
+// `square` in order to be able to differentiate it, so we have logic to enforce the
+// monomorphization. Here, we test this logic.
+
 #![feature(autodiff)]
 
 use std::autodiff::autodiff_reverse;
@@ -12,32 +20,37 @@ fn square + Copy>(x: &T) -> T {
 }
 
 // Ensure that `d_square::` code is generated
-//
-// CHECK: ; generic::square
-// CHECK-NEXT: ; Function Attrs: {{.*}}
-// CHECK-NEXT: define internal {{.*}} float
-// CHECK-NEXT: start:
-// CHECK-NOT: ret
-// CHECK: fmul float
+
+// F32-LABEL: ; generic::square::
+// F32-NEXT: ; Function Attrs: {{.*}}
+// F32-NEXT: define internal {{.*}} float
+// F32-NEXT: start:
+// F32-NOT: ret
+// F32: fmul float
 
 // Ensure that `d_square::` code is generated even if `square::` was never called
-//
-// CHECK: ; generic::square
-// CHECK-NEXT: ; Function Attrs:
-// CHECK-NEXT: define internal {{.*}} double
-// CHECK-NEXT: start:
-// CHECK-NOT: ret
-// CHECK: fmul double
+
+// F64-LABEL: ; generic::d_square::
+// F64-NEXT: ; Function Attrs: {{.*}}
+// F64-NEXT: define internal {{.*}} void
+// F64-NEXT: start:
+// F64-NEXT:   {{(tail )?}}call {{(fastcc )?}}void @diffe_{{.*}}(double {{.*}}, ptr {{.*}})
+// F64-NEXT: ret void
+
+// Main-LABEL: ; generic::main
+// Main: ; call generic::square::
+// Main: ; call generic::d_square::
 
 fn main() {
     let xf32: f32 = std::hint::black_box(3.0);
     let xf64: f64 = std::hint::black_box(3.0);
+    let seed: f64 = std::hint::black_box(1.0);
 
     let outputf32 = square::(&xf32);
     assert_eq!(9.0, outputf32);
 
     let mut df_dxf64: f64 = std::hint::black_box(0.0);
 
-    let output_f64 = d_square::(&xf64, &mut df_dxf64, 1.0);
+    let output_f64 = d_square::(&xf64, &mut df_dxf64, seed);
     assert_eq!(6.0, df_dxf64);
 }

From d7877615b41be6bccfad2e6858b9abc7e1834f6e Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 23 Jan 2026 20:37:58 -0800
Subject: [PATCH 119/319] Update test after new mangling scheme, make test more
 robust

---
 tests/codegen-llvm/autodiff/identical_fnc.rs | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/identical_fnc.rs b/tests/codegen-llvm/autodiff/identical_fnc.rs
index 1c18e7acc4b6..a8b186c302ea 100644
--- a/tests/codegen-llvm/autodiff/identical_fnc.rs
+++ b/tests/codegen-llvm/autodiff/identical_fnc.rs
@@ -8,7 +8,7 @@
 // merged placeholder function anymore, and compilation would fail. We prevent this by disabling
 // LLVM's merge_function pass before AD. Here we implicetely test that our solution keeps working.
 // We also explicetly test that we keep running merge_function after AD, by checking for two
-// identical function calls in the LLVM-IR, while having two different calls in the Rust code.
+// identical function calls in the LLVM-IR, despite having two different calls in the Rust code.
 #![feature(autodiff)]
 
 use std::autodiff::autodiff_reverse;
@@ -27,14 +27,14 @@ fn square2(x: &f64) -> f64 {
 
 // CHECK:; identical_fnc::main
 // CHECK-NEXT:; Function Attrs:
-// CHECK-NEXT:define internal void @_ZN13identical_fnc4main17h6009e4f751bf9407E()
+// CHECK-NEXT:define internal void
 // CHECK-NEXT:start:
 // CHECK-NOT:br
 // CHECK-NOT:ret
 // CHECK:; call identical_fnc::d_square
-// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1)
+// CHECK-NEXT:call fastcc void @[[HASH:.+]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx1)
 // CHECK:; call identical_fnc::d_square
-// CHECK-NEXT:call fastcc void @_ZN13identical_fnc8d_square[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2)
+// CHECK-NEXT:call fastcc void @[[HASH]](double %x.val, ptr noalias noundef align 8 dereferenceable(8) %dx2)
 
 fn main() {
     let x = std::hint::black_box(3.0);

From f5cdd5d86e1269cba321033e51e6dcd8c513b240 Mon Sep 17 00:00:00 2001
From: Brian Cain 
Date: Fri, 23 Jan 2026 15:00:11 -0600
Subject: [PATCH 120/319] Update hexagon target linker configurations

* hexagon-unknown-qurt: Use hexagon-clang from Hexagon SDK instead of
rust-lld
* hexagon-unknown-linux-musl: Use hexagon-unknown-linux-musl-clang from
the open source toolchain instead of rust-lld.
* hexagon-unknown-none-elf: Keep rust-lld but fix the linker flavor.

rust-lld is appropriate for a baremetal target but for traditional
programs that depend on libc, using clang's driver makes the most
sense.
---
 .../targets/hexagon_unknown_linux_musl.rs     |  6 ++---
 .../spec/targets/hexagon_unknown_none_elf.rs  |  5 +++-
 .../src/spec/targets/hexagon_unknown_qurt.rs  |  4 ++--
 .../hexagon-unknown-linux-musl.md             | 23 +++++++++++--------
 .../platform-support/hexagon-unknown-qurt.md  | 13 +++++++----
 5 files changed, 32 insertions(+), 19 deletions(-)

diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs
index 82811cda00ce..92c477f53f8f 100644
--- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs
+++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_linux_musl.rs
@@ -1,4 +1,4 @@
-use crate::spec::{Arch, Cc, LinkerFlavor, Target, TargetMetadata, base};
+use crate::spec::{Arch, Cc, LinkerFlavor, Lld, Target, TargetMetadata, base};
 
 pub(crate) fn target() -> Target {
     let mut base = base::linux_musl::opts();
@@ -8,8 +8,8 @@ pub(crate) fn target() -> Target {
     base.features = "-small-data,+hvx-length128b".into();
 
     base.has_rpath = true;
-    base.linker = Some("rust-lld".into());
-    base.linker_flavor = LinkerFlavor::Unix(Cc::Yes);
+    base.linker = Some("hexagon-unknown-linux-musl-clang".into());
+    base.linker_flavor = LinkerFlavor::Gnu(Cc::Yes, Lld::No);
 
     base.c_enum_min_bits = Some(8);
 
diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs
index 55ec3697a15e..3809057e255c 100644
--- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs
+++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_none_elf.rs
@@ -1,4 +1,6 @@
-use crate::spec::{Arch, PanicStrategy, Target, TargetMetadata, TargetOptions};
+use crate::spec::{
+    Arch, Cc, LinkerFlavor, Lld, PanicStrategy, Target, TargetMetadata, TargetOptions,
+};
 
 pub(crate) fn target() -> Target {
     Target {
@@ -28,6 +30,7 @@ pub(crate) fn target() -> Target {
             emit_debug_gdb_scripts: false,
             c_enum_min_bits: Some(8),
             linker: Some("rust-lld".into()),
+            linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
             ..Default::default()
         },
     }
diff --git a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs
index 746e0cb11dcb..dcc92b4bdcc4 100644
--- a/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs
+++ b/compiler/rustc_target/src/spec/targets/hexagon_unknown_qurt.rs
@@ -24,8 +24,8 @@ pub(crate) fn target() -> Target {
             os: Os::Qurt,
             vendor: "unknown".into(),
             cpu: "hexagonv69".into(),
-            linker: Some("rust-lld".into()),
-            linker_flavor: LinkerFlavor::Gnu(Cc::No, Lld::Yes),
+            linker: Some("hexagon-clang".into()),
+            linker_flavor: LinkerFlavor::Gnu(Cc::Yes, Lld::No),
             exe_suffix: ".elf".into(),
             dynamic_linking: true,
             executables: true,
diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
index d74dd843eb25..eefe82133906 100644
--- a/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
+++ b/src/doc/rustc/src/platform-support/hexagon-unknown-linux-musl.md
@@ -36,14 +36,19 @@ Also included in that toolchain is the C library that can be used when creating
 dynamically linked executables.
 
 ```text
-# /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello
+# /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/qemu-hexagon -L /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr/ ./hello
 ```
 
 ## Linking
 
-This target selects `rust-lld` by default.  Another option to use is
-[eld](https://github.com/qualcomm/eld), which is also provided with
-the opensource hexagon toolchain and the Hexagon SDK.
+This target uses `hexagon-unknown-linux-musl-clang` as the default linker.
+The linker is available from [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon/releases)
+at paths like `/opt/clang+llvm-21.1.8-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/hexagon-unknown-linux-musl-clang`.
+
+Alternative linkers include:
+- [eld](https://github.com/qualcomm/eld), which is provided with
+  the opensource hexagon toolchain and the Hexagon SDK
+- `rust-lld` can be used by specifying `-C linker=rust-lld`
 
 ## Building the target
 Because it is Tier 3, rust does not yet ship pre-compiled artifacts for this
@@ -63,9 +68,9 @@ cxx = "hexagon-unknown-linux-musl-clang++"
 linker = "hexagon-unknown-linux-musl-clang"
 ar = "hexagon-unknown-linux-musl-ar"
 ranlib = "hexagon-unknown-linux-musl-ranlib"
-musl-root = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+musl-root = "/opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 llvm-libunwind = 'in-tree'
-qemu-rootfs = "/opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+qemu-rootfs = "/opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 ```
 
 
@@ -88,7 +93,7 @@ target = "hexagon-unknown-linux-musl"
 [target.hexagon-unknown-linux-musl]
 linker = "hexagon-unknown-linux-musl-clang"
 ar = "hexagon-unknown-linux-musl-ar"
-runner = "qemu-hexagon -L /opt/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+runner = "qemu-hexagon -L /opt/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 ```
 
 Edit the "runner" in `.cargo/config` to point to the path to your toolchain's
@@ -96,13 +101,13 @@ C library.
 
 ```text
 ...
-runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
+runner = "qemu-hexagon -L /path/to/my/inst/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/target/hexagon-unknown-linux-musl/usr"
 ...
 ```
 
 Build/run your rust program with `qemu-hexagon` in your `PATH`:
 
 ```text
-export PATH=/path/to/my/inst/clang+llvm-18.1.0-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH
+export PATH=/path/to/my/inst/clang+llvm-VERSION-cross-hexagon-unknown-linux-musl/x86_64-linux-gnu/bin/:$PATH
 cargo run -Zbuild-std -Zbuild-std-features=llvm-libunwind
 ```
diff --git a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md
index 7928804d0954..d33a90bf188c 100644
--- a/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md
+++ b/src/doc/rustc/src/platform-support/hexagon-unknown-qurt.md
@@ -33,10 +33,15 @@ required for building programs for this target.
 
 ## Linking
 
-This target selects `rust-lld` by default.  Another option to use is
-[eld](https://github.com/qualcomm/eld), which is also provided with
-[the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon)
-and the Hexagon SDK.
+This target uses `hexagon-clang` from the Hexagon SDK as the default linker.
+The linker is available at paths like
+`/opt/Hexagon_SDK/6.4.0.2/tools/HEXAGON_Tools/19.0.04/Tools/bin/hexagon-clang`.
+
+Alternative linkers include:
+- [eld](https://github.com/qualcomm/eld), which is provided with both
+  [the opensource hexagon toolchain](https://github.com/quic/toolchain_for_hexagon)
+  and the Hexagon SDK
+- `rust-lld` can be used by specifying `-C linker=rust-lld`
 
 ## Building the target
 

From 7bcc8a705383710fe8ae1105799d4e1cb852f0e0 Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 23 Jan 2026 21:54:04 -0800
Subject: [PATCH 121/319] update abi handling test

---
 tests/codegen-llvm/autodiff/abi_handling.rs | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/abi_handling.rs b/tests/codegen-llvm/autodiff/abi_handling.rs
index 5c8126898a8d..a8bc482fc293 100644
--- a/tests/codegen-llvm/autodiff/abi_handling.rs
+++ b/tests/codegen-llvm/autodiff/abi_handling.rs
@@ -38,14 +38,14 @@ fn square(x: f32) -> f32 {
 // CHECK-LABEL: ; abi_handling::df1
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal { float, float }
-// debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0)
+// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%bx_0)
 // release-NEXT: define internal fastcc float
 // release-SAME: (float %x.0.val, float %x.4.val)
 
 // CHECK-LABEL: ; abi_handling::f1
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal float
-// debug-SAME: (ptr align 4 %x)
+// debug-SAME: (ptr {{.*}}%x)
 // release-NEXT: define internal fastcc noundef float
 // release-SAME: (float %x.0.val, float %x.4.val)
 #[autodiff_forward(df1, Dual, Dual)]
@@ -58,7 +58,7 @@ fn f1(x: &[f32; 2]) -> f32 {
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal { float, float }
 // debug-SAME: (ptr %f, float %x, float %dret)
-// release-NEXT: define internal fastcc float
+// release-NEXT: define internal fastcc noundef float
 // release-SAME: (float noundef %x)
 
 // CHECK-LABEL: ; abi_handling::f2
@@ -77,13 +77,13 @@ fn f2(f: fn(f32) -> f32, x: f32) -> f32 {
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal { float, float }
 // debug-SAME: (ptr align 4 %x, ptr align 4 %bx_0, ptr align 4 %y, ptr align 4 %by_0)
-// release-NEXT: define internal fastcc { float, float }
+// release-NEXT: define internal fastcc float
 // release-SAME: (float %x.0.val)
 
 // CHECK-LABEL: ; abi_handling::f3
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal float
-// debug-SAME: (ptr align 4 %x, ptr align 4 %y)
+// debug-SAME: (ptr {{.*}}%x, ptr {{.*}}%y)
 // release-NEXT: define internal fastcc noundef float
 // release-SAME: (float %x.0.val)
 #[autodiff_forward(df3, Dual, Dual, Dual)]
@@ -160,7 +160,7 @@ fn f6(i: NestedInput) -> f32 {
 // CHECK-LABEL: ; abi_handling::f7
 // CHECK-NEXT: Function Attrs
 // debug-NEXT: define internal float
-// debug-SAME: (ptr align 4 %x.0, ptr align 4 %x.1)
+// debug-SAME: (ptr {{.*}}%x.0, ptr {{.*}}%x.1)
 // release-NEXT: define internal fastcc noundef float
 // release-SAME: (float %x.0.0.val, float %x.1.0.val)
 #[autodiff_forward(df7, Dual, Dual)]

From b6d567c12cb16a0c02928dfea0db24523ddb9c5f Mon Sep 17 00:00:00 2001
From: Manuel Drehwald 
Date: Fri, 23 Jan 2026 23:18:52 -0800
Subject: [PATCH 122/319] Shorten the autodiff batching test, to make it more
 reliable

---
 tests/codegen-llvm/autodiff/batched.rs | 83 +++++---------------------
 1 file changed, 16 insertions(+), 67 deletions(-)

diff --git a/tests/codegen-llvm/autodiff/batched.rs b/tests/codegen-llvm/autodiff/batched.rs
index 0ff6134bc07d..5a723ff04183 100644
--- a/tests/codegen-llvm/autodiff/batched.rs
+++ b/tests/codegen-llvm/autodiff/batched.rs
@@ -1,13 +1,11 @@
 //@ compile-flags: -Zautodiff=Enable,NoTT,NoPostopt -C opt-level=3  -Clto=fat
 //@ no-prefer-dynamic
 //@ needs-enzyme
-//
-// In Enzyme, we test against a large range of LLVM versions (5+) and don't have overly many
-// breakages. One benefit is that we match the IR generated by Enzyme only after running it
-// through LLVM's O3 pipeline, which will remove most of the noise.
-// However, our integration test could also be affected by changes in how rustc lowers MIR into
-// LLVM-IR, which could cause additional noise and thus breakages. If that's the case, we should
-// reduce this test to only match the first lines and the ret instructions.
+
+// This test combines two features of Enzyme, automatic differentiation and batching. As such, it is
+// especially prone to breakages. I reduced it therefore to a minimal check matches argument/return
+// types. Based on the original batching author, implementing the batching feature over MLIR instead
+// of LLVM should give significantly more reliable performance.
 
 #![feature(autodiff)]
 
@@ -22,69 +20,20 @@ fn square(x: &f32) -> f32 {
     x * x
 }
 
+// The base ("scalar") case d_square3, without batching.
+// CHECK: define internal fastcc float @fwddiffesquare(float %x.0.val, float %"x'.0.val")
+// CHECK:   %0 = fadd fast float %"x'.0.val", %"x'.0.val"
+// CHECK-NEXT:   %1 = fmul fast float %0, %x.0.val
+// CHECK-NEXT:   ret float %1
+// CHECK-NEXT: }
+
 // d_square2
-// CHECK: define internal [4 x float] @fwddiffe4square(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'")
-// CHECK-NEXT: start:
-// CHECK-NEXT:   %0 = extractvalue [4 x ptr] %"x'", 0
-// CHECK-NEXT:   %"_2'ipl" = load float, ptr %0, align 4
-// CHECK-NEXT:   %1 = extractvalue [4 x ptr] %"x'", 1
-// CHECK-NEXT:   %"_2'ipl1" = load float, ptr %1, align 4
-// CHECK-NEXT:   %2 = extractvalue [4 x ptr] %"x'", 2
-// CHECK-NEXT:   %"_2'ipl2" = load float, ptr %2, align 4
-// CHECK-NEXT:   %3 = extractvalue [4 x ptr] %"x'", 3
-// CHECK-NEXT:   %"_2'ipl3" = load float, ptr %3, align 4
-// CHECK-NEXT:   %_2 = load float, ptr %x, align 4
-// CHECK-NEXT:   %4 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %5 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %6 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %7 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %8 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %9 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %10 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %11 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %12 = fadd fast float %4, %8
-// CHECK-NEXT:   %13 = insertvalue [4 x float] undef, float %12, 0
-// CHECK-NEXT:   %14 = fadd fast float %5, %9
-// CHECK-NEXT:   %15 = insertvalue [4 x float] %13, float %14, 1
-// CHECK-NEXT:   %16 = fadd fast float %6, %10
-// CHECK-NEXT:   %17 = insertvalue [4 x float] %15, float %16, 2
-// CHECK-NEXT:   %18 = fadd fast float %7, %11
-// CHECK-NEXT:   %19 = insertvalue [4 x float] %17, float %18, 3
-// CHECK-NEXT:   ret [4 x float] %19
+// CHECK: define internal fastcc [4 x float] @fwddiffe4square(float %x.0.val, [4 x ptr] %"x'")
+// CHECK:   ret [4 x float]
 // CHECK-NEXT:   }
 
-// d_square3, the extra float is the original return value (x * x)
-// CHECK: define internal { float, [4 x float] } @fwddiffe4square.1(ptr noalias noundef readonly align 4 captures(none) dereferenceable(4) %x, [4 x ptr] %"x'")
-// CHECK-NEXT: start:
-// CHECK-NEXT:   %0 = extractvalue [4 x ptr] %"x'", 0
-// CHECK-NEXT:   %"_2'ipl" = load float, ptr %0, align 4
-// CHECK-NEXT:   %1 = extractvalue [4 x ptr] %"x'", 1
-// CHECK-NEXT:   %"_2'ipl1" = load float, ptr %1, align 4
-// CHECK-NEXT:   %2 = extractvalue [4 x ptr] %"x'", 2
-// CHECK-NEXT:   %"_2'ipl2" = load float, ptr %2, align 4
-// CHECK-NEXT:   %3 = extractvalue [4 x ptr] %"x'", 3
-// CHECK-NEXT:   %"_2'ipl3" = load float, ptr %3, align 4
-// CHECK-NEXT:   %_2 = load float, ptr %x, align 4
-// CHECK-NEXT:   %_0 = fmul float %_2, %_2
-// CHECK-NEXT:   %4 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %5 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %6 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %7 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %8 = fmul fast float %"_2'ipl", %_2
-// CHECK-NEXT:   %9 = fmul fast float %"_2'ipl1", %_2
-// CHECK-NEXT:   %10 = fmul fast float %"_2'ipl2", %_2
-// CHECK-NEXT:   %11 = fmul fast float %"_2'ipl3", %_2
-// CHECK-NEXT:   %12 = fadd fast float %4, %8
-// CHECK-NEXT:   %13 = insertvalue [4 x float] undef, float %12, 0
-// CHECK-NEXT:   %14 = fadd fast float %5, %9
-// CHECK-NEXT:   %15 = insertvalue [4 x float] %13, float %14, 1
-// CHECK-NEXT:   %16 = fadd fast float %6, %10
-// CHECK-NEXT:   %17 = insertvalue [4 x float] %15, float %16, 2
-// CHECK-NEXT:   %18 = fadd fast float %7, %11
-// CHECK-NEXT:   %19 = insertvalue [4 x float] %17, float %18, 3
-// CHECK-NEXT:   %20 = insertvalue { float, [4 x float] } undef, float %_0, 0
-// CHECK-NEXT:   %21 = insertvalue { float, [4 x float] } %20, [4 x float] %19, 1
-// CHECK-NEXT:   ret { float, [4 x float] } %21
+// CHECK: define internal fastcc { float, [4 x float] } @fwddiffe4square.{{.*}}(float %x.0.val, [4 x ptr] %"x'")
+// CHECK:   ret { float, [4 x float] }
 // CHECK-NEXT:   }
 
 fn main() {

From 3f3959310695b6bb7b55d980ddb3ff8fcdbdecba Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Sat, 24 Jan 2026 12:42:33 +0800
Subject: [PATCH 123/319] fix: Fix incorrect continue for
 convert_range_for_to_while

Example
---
```rust
fn foo() {
    $0for mut i in 3..7 {
        foo(i);
        continue;
        bar(i);
    }
}
```

**Before this PR**

This may cause an infinite loop

```rust
fn foo() {
    let mut i = 3;
    while i < 7 {
        foo(i);
        continue;
        bar(i);
        i += 1;
    }
}
```

**After this PR**

```rust
fn foo() {
    let mut i = 3;
    while i < 7 {
        'cont: {
            foo(i);
            break 'cont;
            bar(i);
        }
        i += 1;
    }
}
```
---
 .../handlers/convert_range_for_to_while.rs    | 157 +++++++++++++++++-
 1 file changed, 151 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
index ba577b217df7..2e649f14be26 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/convert_range_for_to_while.rs
@@ -1,13 +1,15 @@
 use ide_db::assists::AssistId;
 use itertools::Itertools;
 use syntax::{
-    AstNode, T,
+    AstNode, SyntaxElement,
+    SyntaxKind::WHITESPACE,
+    T,
     algo::previous_non_trivia_token,
     ast::{
         self, HasArgList, HasLoopBody, HasName, RangeItem, edit::AstNodeEdit, make,
         syntax_factory::SyntaxFactory,
     },
-    syntax_editor::{Element, Position},
+    syntax_editor::{Element, Position, SyntaxEditor},
 };
 
 use crate::assist_context::{AssistContext, Assists};
@@ -40,8 +42,8 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<
     let iterable = for_.iterable()?;
     let (start, end, step, inclusive) = extract_range(&iterable)?;
     let name = pat.name()?;
-    let body = for_.loop_body()?;
-    let last = previous_non_trivia_token(body.stmt_list()?.r_curly_token()?)?;
+    let body = for_.loop_body()?.stmt_list()?;
+    let label = for_.label();
 
     let description = if end.is_some() {
         "Replace with while expression"
@@ -90,8 +92,10 @@ pub(crate) fn convert_range_for_to_while(acc: &mut Assists, ctx: &AssistContext<
             );
 
             let op = ast::BinaryOp::Assignment { op: Some(ast::ArithOp::Add) };
-            edit.insert_all(
-                Position::after(last),
+            process_loop_body(
+                body,
+                label,
+                &mut edit,
                 vec![
                     make.whitespace(&format!("\n{}", indent + 1)).syntax_element(),
                     make.expr_bin(var_expr, op, step).syntax().syntax_element(),
@@ -121,6 +125,86 @@ fn extract_range(iterable: &ast::Expr) -> Option<(ast::Expr, Option,
     })
 }
 
+fn process_loop_body(
+    body: ast::StmtList,
+    label: Option,
+    edit: &mut SyntaxEditor,
+    incrementer: Vec,
+) -> Option<()> {
+    let last = previous_non_trivia_token(body.r_curly_token()?)?.syntax_element();
+
+    let new_body = body.indent(1.into()).clone_subtree();
+    let mut continues = vec![];
+    collect_continue_to(
+        &mut continues,
+        &label.and_then(|it| it.lifetime()),
+        new_body.syntax(),
+        false,
+    );
+
+    if continues.is_empty() {
+        edit.insert_all(Position::after(last), incrementer);
+        return Some(());
+    }
+
+    let mut children = body
+        .syntax()
+        .children_with_tokens()
+        .filter(|it| !matches!(it.kind(), WHITESPACE | T!['{'] | T!['}']));
+    let first = children.next()?;
+    let block_content = first.clone()..=children.last().unwrap_or(first);
+
+    let continue_label = make::lifetime("'cont");
+    let break_expr = make::expr_break(Some(continue_label.clone()), None).clone_for_update();
+    let mut new_edit = SyntaxEditor::new(new_body.syntax().clone());
+    for continue_expr in &continues {
+        new_edit.replace(continue_expr.syntax(), break_expr.syntax());
+    }
+    let new_body = new_edit.finish().new_root().clone();
+    let elements = itertools::chain(
+        [
+            continue_label.syntax().clone_for_update().syntax_element(),
+            make::token(T![:]).syntax_element(),
+            make::tokens::single_space().syntax_element(),
+            new_body.syntax_element(),
+        ],
+        incrementer,
+    );
+    edit.replace_all(block_content, elements.collect());
+
+    Some(())
+}
+
+fn collect_continue_to(
+    acc: &mut Vec,
+    label: &Option,
+    node: &syntax::SyntaxNode,
+    only_label: bool,
+) {
+    let match_label = |it: &Option, label: &Option| match (it, label)
+    {
+        (None, _) => !only_label,
+        (Some(a), Some(b)) if a.text() == b.text() => true,
+        _ => false,
+    };
+    if let Some(expr) = ast::ContinueExpr::cast(node.clone())
+        && match_label(&expr.lifetime(), label)
+    {
+        acc.push(expr);
+    } else if let Some(any_loop) = ast::AnyHasLoopBody::cast(node.clone()) {
+        if match_label(label, &any_loop.label().and_then(|it| it.lifetime())) {
+            return;
+        }
+        for children in node.children() {
+            collect_continue_to(acc, label, &children, true);
+        }
+    } else {
+        for children in node.children() {
+            collect_continue_to(acc, label, &children, only_label);
+        }
+    }
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
@@ -219,6 +303,67 @@ fn foo() {
         );
     }
 
+    #[test]
+    fn test_convert_range_for_to_while_with_continue() {
+        check_assist(
+            convert_range_for_to_while,
+            "
+fn foo() {
+    $0for mut i in 3..7 {
+        foo(i);
+        continue;
+        loop { break; continue }
+        bar(i);
+    }
+}
+            ",
+            "
+fn foo() {
+    let mut i = 3;
+    while i < 7 {
+        'cont: {
+            foo(i);
+            break 'cont;
+            loop { break; continue }
+            bar(i);
+        }
+        i += 1;
+    }
+}
+            ",
+        );
+
+        check_assist(
+            convert_range_for_to_while,
+            "
+fn foo() {
+    'x: $0for mut i in 3..7 {
+        foo(i);
+        continue 'x;
+        loop { break; continue 'x }
+        'x: loop { continue 'x }
+        bar(i);
+    }
+}
+            ",
+            "
+fn foo() {
+    let mut i = 3;
+    'x: while i < 7 {
+        'cont: {
+            foo(i);
+            break 'cont;
+            loop { break; break 'cont }
+            'x: loop { continue 'x }
+            bar(i);
+        }
+        i += 1;
+    }
+}
+            ",
+        );
+    }
+
     #[test]
     fn test_convert_range_for_to_while_step_by() {
         check_assist(

From cb603ad5906baa50bf4e1410a7e6a13ba5da2051 Mon Sep 17 00:00:00 2001
From: Pavan Kumar Sunkara 
Date: Sun, 25 Jan 2026 06:37:15 +0530
Subject: [PATCH 124/319] internal: Use parser expect where possible

---
 .../crates/parser/src/grammar/attributes.rs            | 10 +++-------
 .../test_data/parser/err/0002_duplicate_shebang.rast   |  2 +-
 .../test_data/parser/err/0005_attribute_recover.rast   |  2 +-
 .../parser/err/0032_match_arms_inner_attrs.rast        |  6 +++---
 4 files changed, 8 insertions(+), 12 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
index ccb556b2ccac..54b5c8a275a8 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
@@ -24,15 +24,11 @@ fn attr(p: &mut Parser<'_>, inner: bool) {
         p.bump(T![!]);
     }
 
-    if p.eat(T!['[']) {
+    if p.expect(T!['[']) {
         meta(p);
-
-        if !p.eat(T![']']) {
-            p.error("expected `]`");
-        }
-    } else {
-        p.error("expected `[`");
+        p.expect(T![']']);
     }
+
     attr.complete(p, ATTR);
 }
 
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast
index 7ee1ecfbb159..60cc690f7c98 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0002_duplicate_shebang.rast
@@ -28,7 +28,7 @@ SOURCE_FILE
         NAME_REF
           IDENT "rusti"
   WHITESPACE "\n"
-error 23: expected `[`
+error 23: expected L_BRACK
 error 23: expected an item
 error 27: expected one of `*`, `::`, `{`, `self`, `super` or an identifier
 error 28: expected SEMICOLON
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast
index 6ff072e207cd..77b4d06321d5 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0005_attribute_recover.rast
@@ -58,5 +58,5 @@ SOURCE_FILE
           R_CURLY "}"
   WHITESPACE "\n"
 error 53: expected R_PAREN
-error 53: expected `]`
+error 53: expected R_BRACK
 error 53: expected an item
diff --git a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
index 327bf94a49e6..b657e9834156 100644
--- a/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
+++ b/src/tools/rust-analyzer/crates/parser/test_data/parser/err/0032_match_arms_inner_attrs.rast
@@ -192,14 +192,14 @@ SOURCE_FILE
         WHITESPACE "\n"
         R_CURLY "}"
   WHITESPACE "\n"
-error 52: expected `[`
+error 52: expected L_BRACK
 error 52: expected pattern
 error 53: expected FAT_ARROW
 error 78: expected `,`
-error 161: expected `[`
+error 161: expected L_BRACK
 error 161: expected pattern
 error 162: expected FAT_ARROW
-error 232: expected `[`
+error 232: expected L_BRACK
 error 232: expected pattern
 error 233: expected FAT_ARROW
 error 250: expected `,`

From 163a4b4caf059f662f0eec6d49ec153939560a40 Mon Sep 17 00:00:00 2001
From: Marijn Schouten 
Date: Mon, 6 Oct 2025 12:19:50 +0000
Subject: [PATCH 125/319] From: cleanup

---
 library/core/src/convert/num.rs | 87 ++++++++++++++-------------------
 1 file changed, 38 insertions(+), 49 deletions(-)

diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs
index c69026c5c9f3..75c94af30000 100644
--- a/library/core/src/convert/num.rs
+++ b/library/core/src/convert/num.rs
@@ -39,62 +39,51 @@ impl_float_to_int!(f32 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i12
 impl_float_to_int!(f64 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
 impl_float_to_int!(f128 => u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize);
 
-// Conversion traits for primitive integer and float types
-// Conversions T -> T are covered by a blanket impl and therefore excluded
-// Some conversions from and to usize/isize are not implemented due to portability concerns
-macro_rules! impl_from {
-    (bool => $Int:ty $(,)?) => {
-        impl_from!(
-            bool => $Int,
-            #[stable(feature = "from_bool", since = "1.28.0")],
-            concat!(
-                "Converts a [`bool`] to [`", stringify!($Int), "`] losslessly.\n",
-                "The resulting value is `0` for `false` and `1` for `true` values.\n",
-                "\n",
-                "# Examples\n",
-                "\n",
-                "```\n",
-                "assert_eq!(", stringify!($Int), "::from(true), 1);\n",
-                "assert_eq!(", stringify!($Int), "::from(false), 0);\n",
-                "```\n",
-            ),
-        );
-    };
-    ($Small:ty => $Large:ty, #[$attr:meta] $(,)?) => {
-        impl_from!(
-            $Small => $Large,
-            #[$attr],
-            concat!("Converts [`", stringify!($Small), "`] to [`", stringify!($Large), "`] losslessly."),
-        );
-    };
-    ($Small:ty => $Large:ty, #[$attr:meta], $doc:expr $(,)?) => {
-        #[$attr]
+/// Implement `From` for integers
+macro_rules! impl_from_bool {
+    ($($int:ty)*) => {$(
+        #[stable(feature = "from_bool", since = "1.28.0")]
         #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
-        impl const From<$Small> for $Large {
-            // Rustdocs on the impl block show a "[+] show undocumented items" toggle.
-            // Rustdocs on functions do not.
-            #[doc = $doc]
+        impl const From for $int {
+            /// Converts from [`bool`] to
+            #[doc = concat!("[`", stringify!($int), "`]")]
+            /// , by turning `false` into `0` and `true` into `1`.
+            ///
+            /// # Examples
+            ///
+            /// ```
+            #[doc = concat!("assert_eq!(", stringify!($int), "::from(false), 0);")]
+            ///
+            #[doc = concat!("assert_eq!(", stringify!($int), "::from(true), 1);")]
+            /// ```
             #[inline(always)]
-            fn from(small: $Small) -> Self {
-                small as Self
+            fn from(b: bool) -> Self {
+                b as Self
             }
         }
-    };
+    )*}
 }
 
 // boolean -> integer
-impl_from!(bool => u8);
-impl_from!(bool => u16);
-impl_from!(bool => u32);
-impl_from!(bool => u64);
-impl_from!(bool => u128);
-impl_from!(bool => usize);
-impl_from!(bool => i8);
-impl_from!(bool => i16);
-impl_from!(bool => i32);
-impl_from!(bool => i64);
-impl_from!(bool => i128);
-impl_from!(bool => isize);
+impl_from_bool!(u8 u16 u32 u64 u128 usize);
+impl_from_bool!(i8 i16 i32 i64 i128 isize);
+
+/// Implement `From<$small>` for `$large`
+macro_rules! impl_from {
+    ($small:ty => $large:ty, #[$attr:meta]) => {
+        #[$attr]
+        #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
+        impl const From<$small> for $large {
+            #[doc = concat!("Converts from [`", stringify!($small), "`] to [`", stringify!($large), "`] losslessly.")]
+            #[inline(always)]
+            fn from(small: $small) -> Self {
+                debug_assert!(<$large>::MIN as i128 <= <$small>::MIN as i128);
+                debug_assert!(<$small>::MAX as u128 <= <$large>::MAX as u128);
+                small as Self
+            }
+        }
+    }
+}
 
 // unsigned integer -> unsigned integer
 impl_from!(u8 => u16, #[stable(feature = "lossless_int_conv", since = "1.5.0")]);

From f12288ec263233265ea19b5e8147f4b87837d514 Mon Sep 17 00:00:00 2001
From: Marijn Schouten 
Date: Mon, 6 Oct 2025 16:27:02 +0000
Subject: [PATCH 126/319] TryFrom for bool

---
 library/core/src/convert/num.rs               | 36 +++++++++++++++++++
 tests/ui/try-trait/bad-interconversion.stderr |  2 +-
 2 files changed, 37 insertions(+), 1 deletion(-)

diff --git a/library/core/src/convert/num.rs b/library/core/src/convert/num.rs
index 75c94af30000..6e82e3356410 100644
--- a/library/core/src/convert/num.rs
+++ b/library/core/src/convert/num.rs
@@ -327,12 +327,48 @@ macro_rules! impl_try_from_both_bounded {
     )*}
 }
 
+/// Implement `TryFrom` for `bool`
+macro_rules! impl_try_from_integer_for_bool {
+    ($($int:ty)+) => {$(
+        #[stable(feature = "try_from", since = "1.34.0")]
+        #[rustc_const_unstable(feature = "const_convert", issue = "143773")]
+        impl const TryFrom<$int> for bool {
+            type Error = TryFromIntError;
+
+            /// Tries to create a bool from an integer type.
+            /// Returns an error if the integer is not 0 or 1.
+            ///
+            /// # Examples
+            ///
+            /// ```
+            #[doc = concat!("assert_eq!(0_", stringify!($int), ".try_into(), Ok(false));")]
+            ///
+            #[doc = concat!("assert_eq!(1_", stringify!($int), ".try_into(), Ok(true));")]
+            ///
+            #[doc = concat!("assert!(<", stringify!($int), " as TryInto>::try_into(2).is_err());")]
+            /// ```
+            #[inline]
+            fn try_from(i: $int) -> Result {
+                match i {
+                    0 => Ok(false),
+                    1 => Ok(true),
+                    _ => Err(TryFromIntError(())),
+                }
+            }
+        }
+    )*}
+}
+
 macro_rules! rev {
     ($mac:ident, $source:ty => $($target:ty),+) => {$(
         $mac!($target => $source);
     )*}
 }
 
+// integer -> bool
+impl_try_from_integer_for_bool!(u128 u64 u32 u16 u8);
+impl_try_from_integer_for_bool!(i128 i64 i32 i16 i8);
+
 // unsigned integer -> unsigned integer
 impl_try_from_upper_bounded!(u16 => u8);
 impl_try_from_upper_bounded!(u32 => u8, u16);
diff --git a/tests/ui/try-trait/bad-interconversion.stderr b/tests/ui/try-trait/bad-interconversion.stderr
index f8c0deba99ba..a566800da53e 100644
--- a/tests/ui/try-trait/bad-interconversion.stderr
+++ b/tests/ui/try-trait/bad-interconversion.stderr
@@ -22,7 +22,7 @@ help: the following other types implement trait `From`
   ::: $SRC_DIR/core/src/ascii/ascii_char.rs:LL:COL
    |
    = note: in this macro invocation
-   = note: this error originates in the macro `impl_from` which comes from the expansion of the macro `into_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
+   = note: this error originates in the macro `impl_from_bool` which comes from the expansion of the macro `into_int_impl` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error[E0277]: the `?` operator can only be used on `Result`s, not `Option`s, in a function that returns `Result`
   --> $DIR/bad-interconversion.rs:9:12

From 2a3614b5557b89da3d92b17c3497ac6e954360b8 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Mon, 26 Jan 2026 05:44:04 +0530
Subject: [PATCH 127/319] correct ungrammar path in patch

---
 src/tools/rust-analyzer/Cargo.toml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/Cargo.toml b/src/tools/rust-analyzer/Cargo.toml
index 04b513b38b58..2288933a96cc 100644
--- a/src/tools/rust-analyzer/Cargo.toml
+++ b/src/tools/rust-analyzer/Cargo.toml
@@ -42,7 +42,7 @@ debug = 2
 # lsp-server = { path = "lib/lsp-server" }
 
 
-# ungrammar = { path = "lin/ungrammar" }
+# ungrammar = { path = "lib/ungrammar" }
 
 # salsa = { path = "../salsa" }
 # salsa-macros = { path = "../salsa/components/salsa-macros" }

From 3a33ab0595044615affae9d11ba45df98e5c0dee Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sat, 24 Jan 2026 15:05:36 -0800
Subject: [PATCH 128/319] GVN: Elide more intermediate transmutes

---
 compiler/rustc_mir_transform/src/gvn.rs       |  61 ++++++--
 ...n.DataflowConstProp.32bit.panic-abort.diff |   8 +-
 ....DataflowConstProp.32bit.panic-unwind.diff |   8 +-
 ...n.DataflowConstProp.64bit.panic-abort.diff |   8 +-
 ....DataflowConstProp.64bit.panic-unwind.diff |   8 +-
 ...oxed_slice.main.GVN.32bit.panic-abort.diff |  13 +-
 ...xed_slice.main.GVN.32bit.panic-unwind.diff |  13 +-
 ...oxed_slice.main.GVN.64bit.panic-abort.diff |  13 +-
 ...xed_slice.main.GVN.64bit.panic-unwind.diff |  13 +-
 .../gvn.fn_pointers.GVN.panic-abort.diff      |  18 +--
 .../gvn.fn_pointers.GVN.panic-unwind.diff     |  18 +--
 tests/mir-opt/gvn.rs                          |  20 ++-
 ..._then_transmute_again.GVN.panic-abort.diff | 148 +++++++++++++-----
 ...then_transmute_again.GVN.panic-unwind.diff | 148 +++++++++++++-----
 .../gvn_ptr_eq_with_constant.main.GVN.diff    |   6 +-
 15 files changed, 328 insertions(+), 175 deletions(-)

diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs
index 820998eed100..476ae059649a 100644
--- a/compiler/rustc_mir_transform/src/gvn.rs
+++ b/compiler/rustc_mir_transform/src/gvn.rs
@@ -1591,10 +1591,12 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
                     (Transmute, PtrToPtr) if self.pointers_have_same_metadata(from, to) => {
                         Some(Transmute)
                     }
-                    // If would be legal to always do this, but we don't want to hide information
+                    // It would be legal to always do this, but we don't want to hide information
                     // from the backend that it'd otherwise be able to use for optimizations.
                     (Transmute, Transmute)
-                        if !self.type_may_have_niche_of_interest_to_backend(from) =>
+                        if !self.transmute_may_have_niche_of_interest_to_backend(
+                            inner_from, from, to,
+                        ) =>
                     {
                         Some(Transmute)
                     }
@@ -1642,24 +1644,65 @@ impl<'body, 'a, 'tcx> VnState<'body, 'a, 'tcx> {
         }
     }
 
-    /// Returns `false` if we know for sure that this type has no interesting niche,
-    /// and thus we can skip transmuting through it without worrying.
+    /// Returns `false` if we're confident that the middle type doesn't have an
+    /// interesting niche so we can skip that step when transmuting.
     ///
     /// The backend will emit `assume`s when transmuting between types with niches,
     /// so we want to preserve `i32 -> char -> u32` so that that data is around,
     /// but it's fine to skip whole-range-is-value steps like `A -> u32 -> B`.
-    fn type_may_have_niche_of_interest_to_backend(&self, ty: Ty<'tcx>) -> bool {
-        let Ok(layout) = self.ecx.layout_of(ty) else {
+    fn transmute_may_have_niche_of_interest_to_backend(
+        &self,
+        from_ty: Ty<'tcx>,
+        middle_ty: Ty<'tcx>,
+        to_ty: Ty<'tcx>,
+    ) -> bool {
+        let Ok(middle_layout) = self.ecx.layout_of(middle_ty) else {
             // If it's too generic or something, then assume it might be interesting later.
             return true;
         };
 
-        if layout.uninhabited {
+        if middle_layout.uninhabited {
             return true;
         }
 
-        match layout.backend_repr {
-            BackendRepr::Scalar(a) => !a.is_always_valid(&self.ecx),
+        match middle_layout.backend_repr {
+            BackendRepr::Scalar(mid) => {
+                if mid.is_always_valid(&self.ecx) {
+                    // With no niche it's never interesting, so don't bother
+                    // looking at the layout of the other two types.
+                    false
+                } else if let Ok(from_layout) = self.ecx.layout_of(from_ty)
+                    && !from_layout.uninhabited
+                    && from_layout.size == middle_layout.size
+                    && let BackendRepr::Scalar(from_a) = from_layout.backend_repr
+                    && let mid_range = mid.valid_range(&self.ecx)
+                    && let from_range = from_a.valid_range(&self.ecx)
+                    && mid_range.contains_range(from_range, middle_layout.size)
+                {
+                    // The `from_range` is a (non-strict) subset of `mid_range`
+                    // such as if we're doing `bool` -> `ascii::Char` -> `_`,
+                    // where `from_range: 0..=1` and `mid_range: 0..=127`,
+                    // and thus the middle doesn't tell us anything we don't
+                    // already know from the initial type.
+                    false
+                } else if let Ok(to_layout) = self.ecx.layout_of(to_ty)
+                    && !to_layout.uninhabited
+                    && to_layout.size == middle_layout.size
+                    && let BackendRepr::Scalar(to_a) = to_layout.backend_repr
+                    && let mid_range = mid.valid_range(&self.ecx)
+                    && let to_range = to_a.valid_range(&self.ecx)
+                    && mid_range.contains_range(to_range, middle_layout.size)
+                {
+                    // The `to_range` is a (non-strict) subset of `mid_range`
+                    // such as if we're doing `_` -> `ascii::Char` -> `bool`,
+                    // where `mid_range: 0..=127` and `to_range: 0..=1`,
+                    // and thus the middle doesn't tell us anything we don't
+                    // already know from the final type.
+                    false
+                } else {
+                    true
+                }
+            }
             BackendRepr::ScalarPair(a, b) => {
                 !a.is_always_valid(&self.ecx) || !b.is_always_valid(&self.ecx)
             }
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
index 7a60070b7074..5a3342a45cd3 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
index d13d0d962a69..2d1b05e6e987 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.32bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
index 8701e879e959..695a06e29d3d 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
index ac1c8d627baa..17714feb1432 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.DataflowConstProp.64bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,11 +44,8 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
-          _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
-          _7 = const {0x1 as *const [bool; 0]};
+          _6 = const {0x1 as *const [bool; 0]};
           _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
           _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
           StorageDead(_5);
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
index 0205d0cc3d16..7d66d3129115 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
index f6babe35b5a0..cc00bd300a3c 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.32bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
index 204e59415c6b..9380cdd6ccb4 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-abort.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
index 0cf3f43c0464..bea564376274 100644
--- a/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/dataflow-const-prop/default_boxed_slice.main.GVN.64bit.panic-unwind.diff
@@ -16,12 +16,11 @@
           scope 4 (inlined std::ptr::Unique::<[bool; 0]>::dangling) {
               let mut _5: std::ptr::NonNull<[bool; 0]>;
               scope 5 (inlined NonNull::<[bool; 0]>::dangling) {
-                  let mut _6: std::num::NonZero;
                   scope 6 {
                       scope 8 (inlined std::ptr::Alignment::as_nonzero) {
                       }
                       scope 9 (inlined NonNull::<[bool; 0]>::without_provenance) {
-                          let _7: *const [bool; 0];
+                          let _6: *const [bool; 0];
                           scope 10 {
                           }
                           scope 11 (inlined NonZero::::get) {
@@ -45,14 +44,10 @@
           StorageLive(_4);
           StorageLive(_5);
           StorageLive(_6);
--         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as std::num::NonZero (Transmute);
-+         _6 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
-          StorageLive(_7);
--         _7 = copy _6 as *const [bool; 0] (Transmute);
--         _5 = NonNull::<[bool; 0]> { pointer: copy _7 };
-+         _7 = const {0x1 as *const [bool; 0]};
+-         _6 = const std::ptr::Alignment::of::<[bool; 0]>::{constant#0} as *const [bool; 0] (Transmute);
+-         _5 = NonNull::<[bool; 0]> { pointer: copy _6 };
++         _6 = const {0x1 as *const [bool; 0]};
 +         _5 = const NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }};
-          StorageDead(_7);
           StorageDead(_6);
 -         _4 = std::ptr::Unique::<[bool; 0]> { pointer: move _5, _marker: const PhantomData::<[bool; 0]> };
 +         _4 = const std::ptr::Unique::<[bool; 0]> {{ pointer: NonNull::<[bool; 0]> {{ pointer: {0x1 as *const [bool; 0]} }}, _marker: PhantomData::<[bool; 0]> }};
diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
index 90920dd0be8f..a29d3331f380 100644
--- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
+++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-abort.diff
@@ -8,10 +8,10 @@
       let mut _3: fn(u8) -> u8;
       let _5: ();
       let mut _6: fn(u8) -> u8;
-      let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _10: ();
       let mut _11: fn();
-      let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _14: ();
       let mut _15: fn();
       scope 1 {
@@ -19,7 +19,7 @@
           let _4: fn(u8) -> u8;
           scope 2 {
               debug g => _4;
-              let _7: {closure@$DIR/gvn.rs:617:19: 617:21};
+              let _7: {closure@$DIR/gvn.rs:618:19: 618:21};
               scope 3 {
                   debug closure => _7;
                   let _8: fn();
@@ -62,16 +62,16 @@
           StorageDead(_6);
           StorageDead(_5);
 -         StorageLive(_7);
--         _7 = {closure@$DIR/gvn.rs:617:19: 617:21};
+-         _7 = {closure@$DIR/gvn.rs:618:19: 618:21};
 -         StorageLive(_8);
 +         nop;
-+         _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
++         _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
 +         nop;
           StorageLive(_9);
 -         _9 = copy _7;
 -         _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_9);
           StorageLive(_10);
           StorageLive(_11);
@@ -88,8 +88,8 @@
           StorageLive(_13);
 -         _13 = copy _7;
 -         _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_13);
           StorageLive(_14);
           StorageLive(_15);
diff --git a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
index 0aca8e508f5c..b716fcd4e74b 100644
--- a/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
+++ b/tests/mir-opt/gvn.fn_pointers.GVN.panic-unwind.diff
@@ -8,10 +8,10 @@
       let mut _3: fn(u8) -> u8;
       let _5: ();
       let mut _6: fn(u8) -> u8;
-      let mut _9: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _9: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _10: ();
       let mut _11: fn();
-      let mut _13: {closure@$DIR/gvn.rs:617:19: 617:21};
+      let mut _13: {closure@$DIR/gvn.rs:618:19: 618:21};
       let _14: ();
       let mut _15: fn();
       scope 1 {
@@ -19,7 +19,7 @@
           let _4: fn(u8) -> u8;
           scope 2 {
               debug g => _4;
-              let _7: {closure@$DIR/gvn.rs:617:19: 617:21};
+              let _7: {closure@$DIR/gvn.rs:618:19: 618:21};
               scope 3 {
                   debug closure => _7;
                   let _8: fn();
@@ -62,16 +62,16 @@
           StorageDead(_6);
           StorageDead(_5);
 -         StorageLive(_7);
--         _7 = {closure@$DIR/gvn.rs:617:19: 617:21};
+-         _7 = {closure@$DIR/gvn.rs:618:19: 618:21};
 -         StorageLive(_8);
 +         nop;
-+         _7 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
++         _7 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
 +         nop;
           StorageLive(_9);
 -         _9 = copy _7;
 -         _8 = move _9 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _9 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _8 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _9 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _8 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_9);
           StorageLive(_10);
           StorageLive(_11);
@@ -88,8 +88,8 @@
           StorageLive(_13);
 -         _13 = copy _7;
 -         _12 = move _13 as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
-+         _13 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21};
-+         _12 = const ZeroSized: {closure@$DIR/gvn.rs:617:19: 617:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
++         _13 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21};
++         _12 = const ZeroSized: {closure@$DIR/gvn.rs:618:19: 618:21} as fn() (PointerCoercion(ClosureFnPointer(Safe), AsCast));
           StorageDead(_13);
           StorageLive(_14);
           StorageLive(_15);
diff --git a/tests/mir-opt/gvn.rs b/tests/mir-opt/gvn.rs
index 3c3241fefe22..837e8ac4d9e3 100644
--- a/tests/mir-opt/gvn.rs
+++ b/tests/mir-opt/gvn.rs
@@ -9,6 +9,7 @@
 #![feature(freeze)]
 #![allow(ambiguous_wide_pointer_comparisons)]
 #![allow(unconditional_panic)]
+#![allow(unnecessary_transmutes)]
 #![allow(unused)]
 
 use std::intrinsics::mir::*;
@@ -985,7 +986,14 @@ unsafe fn aggregate_struct_then_transmute(id: u16, thin: *const u8) {
     opaque(std::intrinsics::transmute::<_, *const u8>(j));
 }
 
-unsafe fn transmute_then_transmute_again(a: u32, c: char) {
+#[repr(u8)]
+enum ZeroOneTwo {
+    Zero,
+    One,
+    Two,
+}
+
+unsafe fn transmute_then_transmute_again(a: u32, c: char, b: bool, d: u8) {
     // CHECK: [[TEMP1:_[0-9]+]] = copy _1 as char (Transmute);
     // CHECK: [[TEMP2:_[0-9]+]] = copy [[TEMP1]] as i32 (Transmute);
     // CHECK: opaque::(move [[TEMP2]])
@@ -996,6 +1004,16 @@ unsafe fn transmute_then_transmute_again(a: u32, c: char) {
     // CHECK: opaque::(move [[TEMP]])
     let x = std::intrinsics::transmute::(c);
     opaque(std::intrinsics::transmute::(x));
+
+    // CHECK: [[TEMP:_[0-9]+]] = copy _3 as u8 (Transmute);
+    // CHECK: opaque::(move [[TEMP]])
+    let x = std::intrinsics::transmute::(b);
+    opaque(std::intrinsics::transmute::(x));
+
+    // CHECK: [[TEMP:_[0-9]+]] = copy _4 as bool (Transmute);
+    // CHECK: opaque::(move [[TEMP]])
+    let x = std::intrinsics::transmute::(d);
+    opaque(std::intrinsics::transmute::(x));
 }
 
 // Transmuting can skip a pointer cast so long as it wasn't a fat-to-thin cast.
diff --git a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff
index 962fecd2586e..caed065536e3 100644
--- a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff
+++ b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-abort.diff
@@ -1,71 +1,135 @@
 - // MIR for `transmute_then_transmute_again` before GVN
 + // MIR for `transmute_then_transmute_again` after GVN
   
-  fn transmute_then_transmute_again(_1: u32, _2: char) -> () {
+  fn transmute_then_transmute_again(_1: u32, _2: char, _3: bool, _4: u8) -> () {
       debug a => _1;
       debug c => _2;
+      debug b => _3;
+      debug d => _4;
       let mut _0: ();
-      let _3: char;
-      let mut _4: u32;
-      let _5: ();
-      let mut _6: i32;
-      let mut _7: char;
+      let _5: char;
+      let mut _6: u32;
+      let _7: ();
+      let mut _8: i32;
       let mut _9: char;
-      let _10: ();
-      let mut _11: i32;
-      let mut _12: u32;
+      let mut _11: char;
+      let _12: ();
+      let mut _13: i32;
+      let mut _14: u32;
+      let mut _16: bool;
+      let _17: ();
+      let mut _18: u8;
+      let mut _19: ZeroOneTwo;
+      let mut _21: u8;
+      let _22: ();
+      let mut _23: bool;
+      let mut _24: ZeroOneTwo;
       scope 1 {
-          debug x => _3;
-          let _8: u32;
+          debug x => _5;
+          let _10: u32;
           scope 2 {
-              debug x => _8;
+              debug x => _10;
+              let _15: ZeroOneTwo;
+              scope 3 {
+                  debug x => _15;
+                  let _20: ZeroOneTwo;
+                  scope 4 {
+                      debug x => _20;
+                  }
+              }
           }
       }
   
       bb0: {
--         StorageLive(_3);
+-         StorageLive(_5);
 +         nop;
-          StorageLive(_4);
-          _4 = copy _1;
--         _3 = move _4 as char (Transmute);
-+         _3 = copy _1 as char (Transmute);
-          StorageDead(_4);
-          StorageLive(_5);
           StorageLive(_6);
+          _6 = copy _1;
+-         _5 = move _6 as char (Transmute);
++         _5 = copy _1 as char (Transmute);
+          StorageDead(_6);
           StorageLive(_7);
-          _7 = copy _3;
--         _6 = move _7 as i32 (Transmute);
-+         _6 = copy _3 as i32 (Transmute);
-          StorageDead(_7);
-          _5 = opaque::(move _6) -> [return: bb1, unwind unreachable];
+          StorageLive(_8);
+          StorageLive(_9);
+          _9 = copy _5;
+-         _8 = move _9 as i32 (Transmute);
++         _8 = copy _5 as i32 (Transmute);
+          StorageDead(_9);
+          _7 = opaque::(move _8) -> [return: bb1, unwind unreachable];
       }
   
       bb1: {
-          StorageDead(_6);
-          StorageDead(_5);
--         StorageLive(_8);
+          StorageDead(_8);
+          StorageDead(_7);
+-         StorageLive(_10);
 +         nop;
-          StorageLive(_9);
-          _9 = copy _2;
--         _8 = move _9 as u32 (Transmute);
-+         _8 = copy _2 as u32 (Transmute);
-          StorageDead(_9);
-          StorageLive(_10);
           StorageLive(_11);
+          _11 = copy _2;
+-         _10 = move _11 as u32 (Transmute);
++         _10 = copy _2 as u32 (Transmute);
+          StorageDead(_11);
           StorageLive(_12);
-          _12 = copy _8;
--         _11 = move _12 as i32 (Transmute);
-+         _11 = copy _2 as i32 (Transmute);
-          StorageDead(_12);
-          _10 = opaque::(move _11) -> [return: bb2, unwind unreachable];
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = copy _10;
+-         _13 = move _14 as i32 (Transmute);
++         _13 = copy _2 as i32 (Transmute);
+          StorageDead(_14);
+          _12 = opaque::(move _13) -> [return: bb2, unwind unreachable];
       }
   
       bb2: {
-          StorageDead(_11);
-          StorageDead(_10);
+          StorageDead(_13);
+          StorageDead(_12);
+-         StorageLive(_15);
++         nop;
+          StorageLive(_16);
+          _16 = copy _3;
+-         _15 = move _16 as ZeroOneTwo (Transmute);
++         _15 = copy _3 as ZeroOneTwo (Transmute);
+          StorageDead(_16);
+          StorageLive(_17);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _19 = move _15;
+-         _18 = move _19 as u8 (Transmute);
++         _19 = copy _15;
++         _18 = copy _3 as u8 (Transmute);
+          StorageDead(_19);
+          _17 = opaque::(move _18) -> [return: bb3, unwind unreachable];
+      }
+  
+      bb3: {
+          StorageDead(_18);
+          StorageDead(_17);
+-         StorageLive(_20);
++         nop;
+          StorageLive(_21);
+          _21 = copy _4;
+-         _20 = move _21 as ZeroOneTwo (Transmute);
++         _20 = copy _4 as ZeroOneTwo (Transmute);
+          StorageDead(_21);
+          StorageLive(_22);
+          StorageLive(_23);
+          StorageLive(_24);
+-         _24 = move _20;
+-         _23 = move _24 as bool (Transmute);
++         _24 = copy _20;
++         _23 = copy _4 as bool (Transmute);
+          StorageDead(_24);
+          _22 = opaque::(move _23) -> [return: bb4, unwind unreachable];
+      }
+  
+      bb4: {
+          StorageDead(_23);
+          StorageDead(_22);
           _0 = const ();
--         StorageDead(_8);
--         StorageDead(_3);
+-         StorageDead(_20);
+-         StorageDead(_15);
+-         StorageDead(_10);
+-         StorageDead(_5);
++         nop;
++         nop;
 +         nop;
 +         nop;
           return;
diff --git a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff
index e32397c1aed0..3b25dd362cd5 100644
--- a/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff
+++ b/tests/mir-opt/gvn.transmute_then_transmute_again.GVN.panic-unwind.diff
@@ -1,71 +1,135 @@
 - // MIR for `transmute_then_transmute_again` before GVN
 + // MIR for `transmute_then_transmute_again` after GVN
   
-  fn transmute_then_transmute_again(_1: u32, _2: char) -> () {
+  fn transmute_then_transmute_again(_1: u32, _2: char, _3: bool, _4: u8) -> () {
       debug a => _1;
       debug c => _2;
+      debug b => _3;
+      debug d => _4;
       let mut _0: ();
-      let _3: char;
-      let mut _4: u32;
-      let _5: ();
-      let mut _6: i32;
-      let mut _7: char;
+      let _5: char;
+      let mut _6: u32;
+      let _7: ();
+      let mut _8: i32;
       let mut _9: char;
-      let _10: ();
-      let mut _11: i32;
-      let mut _12: u32;
+      let mut _11: char;
+      let _12: ();
+      let mut _13: i32;
+      let mut _14: u32;
+      let mut _16: bool;
+      let _17: ();
+      let mut _18: u8;
+      let mut _19: ZeroOneTwo;
+      let mut _21: u8;
+      let _22: ();
+      let mut _23: bool;
+      let mut _24: ZeroOneTwo;
       scope 1 {
-          debug x => _3;
-          let _8: u32;
+          debug x => _5;
+          let _10: u32;
           scope 2 {
-              debug x => _8;
+              debug x => _10;
+              let _15: ZeroOneTwo;
+              scope 3 {
+                  debug x => _15;
+                  let _20: ZeroOneTwo;
+                  scope 4 {
+                      debug x => _20;
+                  }
+              }
           }
       }
   
       bb0: {
--         StorageLive(_3);
+-         StorageLive(_5);
 +         nop;
-          StorageLive(_4);
-          _4 = copy _1;
--         _3 = move _4 as char (Transmute);
-+         _3 = copy _1 as char (Transmute);
-          StorageDead(_4);
-          StorageLive(_5);
           StorageLive(_6);
+          _6 = copy _1;
+-         _5 = move _6 as char (Transmute);
++         _5 = copy _1 as char (Transmute);
+          StorageDead(_6);
           StorageLive(_7);
-          _7 = copy _3;
--         _6 = move _7 as i32 (Transmute);
-+         _6 = copy _3 as i32 (Transmute);
-          StorageDead(_7);
-          _5 = opaque::(move _6) -> [return: bb1, unwind continue];
+          StorageLive(_8);
+          StorageLive(_9);
+          _9 = copy _5;
+-         _8 = move _9 as i32 (Transmute);
++         _8 = copy _5 as i32 (Transmute);
+          StorageDead(_9);
+          _7 = opaque::(move _8) -> [return: bb1, unwind continue];
       }
   
       bb1: {
-          StorageDead(_6);
-          StorageDead(_5);
--         StorageLive(_8);
+          StorageDead(_8);
+          StorageDead(_7);
+-         StorageLive(_10);
 +         nop;
-          StorageLive(_9);
-          _9 = copy _2;
--         _8 = move _9 as u32 (Transmute);
-+         _8 = copy _2 as u32 (Transmute);
-          StorageDead(_9);
-          StorageLive(_10);
           StorageLive(_11);
+          _11 = copy _2;
+-         _10 = move _11 as u32 (Transmute);
++         _10 = copy _2 as u32 (Transmute);
+          StorageDead(_11);
           StorageLive(_12);
-          _12 = copy _8;
--         _11 = move _12 as i32 (Transmute);
-+         _11 = copy _2 as i32 (Transmute);
-          StorageDead(_12);
-          _10 = opaque::(move _11) -> [return: bb2, unwind continue];
+          StorageLive(_13);
+          StorageLive(_14);
+          _14 = copy _10;
+-         _13 = move _14 as i32 (Transmute);
++         _13 = copy _2 as i32 (Transmute);
+          StorageDead(_14);
+          _12 = opaque::(move _13) -> [return: bb2, unwind continue];
       }
   
       bb2: {
-          StorageDead(_11);
-          StorageDead(_10);
+          StorageDead(_13);
+          StorageDead(_12);
+-         StorageLive(_15);
++         nop;
+          StorageLive(_16);
+          _16 = copy _3;
+-         _15 = move _16 as ZeroOneTwo (Transmute);
++         _15 = copy _3 as ZeroOneTwo (Transmute);
+          StorageDead(_16);
+          StorageLive(_17);
+          StorageLive(_18);
+          StorageLive(_19);
+-         _19 = move _15;
+-         _18 = move _19 as u8 (Transmute);
++         _19 = copy _15;
++         _18 = copy _3 as u8 (Transmute);
+          StorageDead(_19);
+          _17 = opaque::(move _18) -> [return: bb3, unwind continue];
+      }
+  
+      bb3: {
+          StorageDead(_18);
+          StorageDead(_17);
+-         StorageLive(_20);
++         nop;
+          StorageLive(_21);
+          _21 = copy _4;
+-         _20 = move _21 as ZeroOneTwo (Transmute);
++         _20 = copy _4 as ZeroOneTwo (Transmute);
+          StorageDead(_21);
+          StorageLive(_22);
+          StorageLive(_23);
+          StorageLive(_24);
+-         _24 = move _20;
+-         _23 = move _24 as bool (Transmute);
++         _24 = copy _20;
++         _23 = copy _4 as bool (Transmute);
+          StorageDead(_24);
+          _22 = opaque::(move _23) -> [return: bb4, unwind continue];
+      }
+  
+      bb4: {
+          StorageDead(_23);
+          StorageDead(_22);
           _0 = const ();
--         StorageDead(_8);
--         StorageDead(_3);
+-         StorageDead(_20);
+-         StorageDead(_15);
+-         StorageDead(_10);
+-         StorageDead(_5);
++         nop;
++         nop;
 +         nop;
 +         nop;
           return;
diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
index f56af33ea603..74bbdeb8f744 100644
--- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
+++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
@@ -7,7 +7,7 @@
       let mut _2: *mut u8;
       scope 1 (inlined dangling_mut::) {
           scope 2 (inlined NonNull::::dangling) {
-              let mut _3: std::num::NonZero;
+              let _3: std::ptr::Alignment;
               scope 3 {
                   scope 5 (inlined std::ptr::Alignment::as_nonzero) {
                   }
@@ -40,9 +40,9 @@
           StorageLive(_1);
           StorageLive(_2);
           StorageLive(_3);
--         _3 = const std::ptr::Alignment::of::::{constant#0} as std::num::NonZero (Transmute);
+-         _3 = const std::ptr::Alignment::of::::{constant#0};
 -         _2 = copy _3 as *mut u8 (Transmute);
-+         _3 = const NonZero::(core::num::niche_types::NonZeroUsizeInner(1_usize));
++         _3 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
 +         _2 = const {0x1 as *mut u8};
           StorageDead(_3);
           StorageLive(_4);

From 929e2809734a1b3df4428912d9c8a36ac2546225 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 25 Jan 2026 17:05:09 -0800
Subject: [PATCH 129/319] Update `ptr::Alignment` to go through transmuting

---
 library/core/src/ptr/alignment.rs             |  5 +++-
 ...ace.PreCodegen.after.32bit.panic-abort.mir | 24 +++++++++----------
 ...ce.PreCodegen.after.32bit.panic-unwind.mir | 24 +++++++++----------
 ...ace.PreCodegen.after.64bit.panic-abort.mir | 24 +++++++++----------
 ...ce.PreCodegen.after.64bit.panic-unwind.mir | 24 +++++++++----------
 tests/mir-opt/pre-codegen/drop_boxed_slice.rs |  5 ++--
 6 files changed, 54 insertions(+), 52 deletions(-)

diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs
index 42c95e6c9cd2..c3187c9a5c2b 100644
--- a/library/core/src/ptr/alignment.rs
+++ b/library/core/src/ptr/alignment.rs
@@ -166,7 +166,10 @@ impl Alignment {
     #[unstable(feature = "ptr_alignment_type", issue = "102070")]
     #[inline]
     pub const fn as_usize(self) -> usize {
-        self.0 as usize
+        // Going through `as_nonzero` helps this be more clearly the inverse of
+        // `new_unchecked`, letting MIR optimizations fold it away.
+
+        self.as_nonzero().get()
     }
 
     /// Returns the alignment as a [NonZero]<[usize]>.
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-abort.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.32bit.panic-unwind.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-abort.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
index 6ffadd4c51db..0adc66951e78 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.generic_in_place.PreCodegen.after.64bit.panic-unwind.mir
@@ -8,9 +8,8 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
             let _2: std::ptr::NonNull<[T]>;
             let mut _3: *mut [T];
             let mut _4: *const [T];
-            let _11: ();
+            let _10: ();
             scope 3 {
-                let _8: std::ptr::alignment::AlignmentEnum;
                 scope 4 {
                     scope 17 (inlined Layout::size) {
                     }
@@ -27,17 +26,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
                     scope 23 (inlined ::deallocate) {
                         scope 24 (inlined std::alloc::Global::deallocate_impl) {
                             scope 25 (inlined std::alloc::Global::deallocate_impl_runtime) {
-                                let mut _9: *mut u8;
+                                let mut _8: *mut u8;
                                 scope 26 (inlined Layout::size) {
                                 }
                                 scope 27 (inlined NonNull::::as_ptr) {
                                 }
                                 scope 28 (inlined std::alloc::dealloc) {
-                                    let mut _10: usize;
+                                    let mut _9: usize;
                                     scope 29 (inlined Layout::size) {
                                     }
                                     scope 30 (inlined Layout::align) {
                                         scope 31 (inlined std::ptr::Alignment::as_usize) {
+                                            scope 32 (inlined std::ptr::Alignment::as_nonzero) {
+                                            }
+                                            scope 33 (inlined NonZero::::get) {
+                                            }
                                         }
                                     }
                                 }
@@ -82,7 +85,6 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         StorageLive(_4);
         _3 = copy _2 as *mut [T] (Transmute);
         _4 = copy _2 as *const [T] (Transmute);
-        StorageLive(_7);
         _5 = std::intrinsics::size_of_val::<[T]>(move _4) -> [return: bb1, unwind unreachable];
     }
 
@@ -91,23 +93,21 @@ fn generic_in_place(_1: *mut Box<[T]>) -> () {
         _6 = const ::ALIGN;
         _7 = copy _6 as std::ptr::Alignment (Transmute);
         StorageDead(_6);
-        _8 = copy (_7.0: std::ptr::alignment::AlignmentEnum);
-        StorageDead(_7);
         StorageDead(_4);
         switchInt(copy _5) -> [0: bb4, otherwise: bb2];
     }
 
     bb2: {
+        StorageLive(_8);
+        _8 = copy _3 as *mut u8 (PtrToPtr);
         StorageLive(_9);
-        _9 = copy _3 as *mut u8 (PtrToPtr);
-        StorageLive(_10);
-        _10 = discriminant(_8);
-        _11 = alloc::alloc::__rust_dealloc(move _9, move _5, move _10) -> [return: bb3, unwind unreachable];
+        _9 = copy _7 as usize (Transmute);
+        _10 = alloc::alloc::__rust_dealloc(move _8, move _5, move _9) -> [return: bb3, unwind unreachable];
     }
 
     bb3: {
-        StorageDead(_10);
         StorageDead(_9);
+        StorageDead(_8);
         goto -> bb4;
     }
 
diff --git a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
index dddcffdd4843..cad7251421eb 100644
--- a/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
+++ b/tests/mir-opt/pre-codegen/drop_boxed_slice.rs
@@ -11,8 +11,7 @@ pub unsafe fn generic_in_place(ptr: *mut Box<[T]>) {
     // CHECK: [[SIZE:_.+]] = std::intrinsics::size_of_val::<[T]>
     // CHECK: [[ALIGN:_.+]] = const ::ALIGN;
     // CHECK: [[B:_.+]] = copy [[ALIGN]] as std::ptr::Alignment (Transmute);
-    // CHECK: [[C:_.+]] = copy ([[B]].0: std::ptr::alignment::AlignmentEnum);
-    // CHECK: [[D:_.+]] = discriminant([[C]]);
-    // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[D]]) ->
+    // CHECK: [[C:_.+]] = copy [[B]] as usize (Transmute);
+    // CHECK: = alloc::alloc::__rust_dealloc({{.+}}, move [[SIZE]], move [[C]]) ->
     std::ptr::drop_in_place(ptr)
 }

From 9288c208a2ee5ef34dbaad65d3a4aca65d1d62d8 Mon Sep 17 00:00:00 2001
From: Scott McMurray 
Date: Sun, 25 Jan 2026 16:35:34 -0800
Subject: [PATCH 130/319] Adjust `Alignment` to emphasize that we don't look at
 its field

---
 library/core/src/ptr/alignment.rs                        | 9 +++++++--
 ...hout_updating_operand.test.GVN.32bit.panic-abort.diff | 8 ++++----
 ...hout_updating_operand.test.GVN.64bit.panic-abort.diff | 8 ++++----
 tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff     | 2 +-
 ...rint_invalid_constant.main.GVN.32bit.panic-abort.diff | 6 +++---
 ...int_invalid_constant.main.GVN.32bit.panic-unwind.diff | 6 +++---
 ...rint_invalid_constant.main.GVN.64bit.panic-abort.diff | 6 +++---
 ...int_invalid_constant.main.GVN.64bit.panic-unwind.diff | 6 +++---
 8 files changed, 28 insertions(+), 23 deletions(-)

diff --git a/library/core/src/ptr/alignment.rs b/library/core/src/ptr/alignment.rs
index c3187c9a5c2b..7c34b026e14b 100644
--- a/library/core/src/ptr/alignment.rs
+++ b/library/core/src/ptr/alignment.rs
@@ -13,7 +13,12 @@ use crate::{cmp, fmt, hash, mem, num};
 #[unstable(feature = "ptr_alignment_type", issue = "102070")]
 #[derive(Copy, Clone, PartialEq, Eq)]
 #[repr(transparent)]
-pub struct Alignment(AlignmentEnum);
+pub struct Alignment {
+    // This field is never used directly (nor is the enum),
+    // as it's just there to convey the validity invariant.
+    // (Hopefully it'll eventually be a pattern type instead.)
+    _inner_repr_trick: AlignmentEnum,
+}
 
 // Alignment is `repr(usize)`, but via extra steps.
 const _: () = assert!(size_of::() == size_of::());
@@ -37,7 +42,7 @@ impl Alignment {
     /// assert_eq!(Alignment::MIN.as_usize(), 1);
     /// ```
     #[unstable(feature = "ptr_alignment_type", issue = "102070")]
-    pub const MIN: Self = Self(AlignmentEnum::_Align1Shl0);
+    pub const MIN: Self = Self::new(1).unwrap();
 
     /// Returns the alignment for a type.
     ///
diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
index 2e428b778504..cde4d281f687 100644
--- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.32bit.panic-abort.diff
@@ -111,7 +111,7 @@
   
       bb3: {
 -         _22 = handle_alloc_error(move _18) -> unwind unreachable;
-+         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable;
++         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}) -> unwind unreachable;
       }
   
       bb4: {
@@ -190,12 +190,12 @@
           StorageLive(_24);
 -         _24 = copy _17 as std::ptr::Alignment (Transmute);
 -         _18 = Layout { size: copy _16, align: move _24 };
-+         _24 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
-+         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }};
++         _24 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }};
++         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }};
           StorageDead(_24);
           StorageLive(_19);
 -         _19 = std::alloc::Global::alloc_impl_runtime(copy _18, const false) -> [return: bb7, unwind unreachable];
-+         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable];
++         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}, const false) -> [return: bb7, unwind unreachable];
       }
   
       bb7: {
diff --git a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
index 4531720ee501..ea5622eb9a2f 100644
--- a/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/dont_reset_cast_kind_without_updating_operand.test.GVN.64bit.panic-abort.diff
@@ -111,7 +111,7 @@
   
       bb3: {
 -         _22 = handle_alloc_error(move _18) -> unwind unreachable;
-+         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}) -> unwind unreachable;
++         _22 = handle_alloc_error(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}) -> unwind unreachable;
       }
   
       bb4: {
@@ -190,12 +190,12 @@
           StorageLive(_24);
 -         _24 = copy _17 as std::ptr::Alignment (Transmute);
 -         _18 = Layout { size: copy _16, align: move _24 };
-+         _24 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
-+         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }};
++         _24 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }};
++         _18 = const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }};
           StorageDead(_24);
           StorageLive(_19);
 -         _19 = std::alloc::Global::alloc_impl_runtime(copy _18, const false) -> [return: bb7, unwind unreachable];
-+         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0) }}, const false) -> [return: bb7, unwind unreachable];
++         _19 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: 0_usize, align: std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }} }}, const false) -> [return: bb7, unwind unreachable];
       }
   
       bb7: {
diff --git a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
index 74bbdeb8f744..9e543699da70 100644
--- a/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
+++ b/tests/mir-opt/gvn_ptr_eq_with_constant.main.GVN.diff
@@ -42,7 +42,7 @@
           StorageLive(_3);
 -         _3 = const std::ptr::Alignment::of::::{constant#0};
 -         _2 = copy _3 as *mut u8 (Transmute);
-+         _3 = const std::ptr::Alignment(std::ptr::alignment::AlignmentEnum::_Align1Shl0);
++         _3 = const std::ptr::Alignment {{ _inner_repr_trick: std::ptr::alignment::AlignmentEnum::_Align1Shl0 }};
 +         _2 = const {0x1 as *mut u8};
           StorageDead(_3);
           StorageLive(_4);
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
index d0fda06c115c..b48e6fc56f43 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-abort.diff
@@ -63,7 +63,7 @@
   
       bb3: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -73,8 +73,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
index 485ff902a7b9..d0e59c06ff99 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.32bit.panic-unwind.diff
@@ -64,7 +64,7 @@
   
       bb4: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -74,8 +74,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb5, unwind continue];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x00000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(4 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x00000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb5, unwind continue];
       }
   
       bb5: {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
index b45a0f4a9bdd..18fc4ac0d87f 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-abort.diff
@@ -63,7 +63,7 @@
   
       bb3: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -73,8 +73,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb4, unwind unreachable];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb4, unwind unreachable];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb4, unwind unreachable];
       }
   
       bb4: {
diff --git a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
index beee899dafe6..c109fe735e89 100644
--- a/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
+++ b/tests/mir-opt/pre-codegen/issue_117368_print_invalid_constant.main.GVN.64bit.panic-unwind.diff
@@ -64,7 +64,7 @@
   
       bb4: {
 -         _1 = move ((_2 as Some).0: std::alloc::Layout);
-+         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
++         _1 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
           StorageDead(_8);
           StorageDead(_2);
           StorageLive(_3);
@@ -74,8 +74,8 @@
           StorageLive(_7);
 -         _7 = copy _1;
 -         _6 = std::alloc::Global::alloc_impl_runtime(move _7, const false) -> [return: bb5, unwind continue];
-+         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }};
-+         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment(Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum) }}, const false) -> [return: bb5, unwind continue];
++         _7 = const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }};
++         _6 = std::alloc::Global::alloc_impl_runtime(const Layout {{ size: Indirect { alloc_id: ALLOC0, offset: Size(8 bytes) }: usize, align: std::ptr::Alignment {{ _inner_repr_trick: Scalar(0x0000000000000000): std::ptr::alignment::AlignmentEnum }} }}, const false) -> [return: bb5, unwind continue];
       }
   
       bb5: {

From 9fd291ed7ee4d1313cfdaa5a5651c77b0a57fd33 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 19 Jan 2026 20:09:30 +0800
Subject: [PATCH 131/319] Fix not complete 'else' before tuple

Example
---
```rust
fn foo() -> (i32, i32) {
    if foo {} el$0
    (2, 3)
}
```

**Before this PR**

```rust
...
kw crate::
kw false
kw for
...
```

**After this PR**

```rust
...
kw crate::
kw else
kw else if
kw false
kw for
...
```
---
 .../ide-completion/src/context/analysis.rs    |  7 ++---
 .../ide-completion/src/tests/expression.rs    | 26 +++++++++++++++++++
 2 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 0db93b0837cd..8842d29c8d90 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -2030,9 +2030,10 @@ fn is_after_if_expr(node: SyntaxNode) -> bool {
         Some(stmt) => stmt.syntax().clone(),
         None => node,
     };
-    let prev_sibling =
-        non_trivia_sibling(node.into(), Direction::Prev).and_then(NodeOrToken::into_node);
-    iter::successors(prev_sibling, |it| it.last_child_or_token()?.into_node())
+    let Some(prev_token) = previous_non_trivia_token(node) else { return false };
+    prev_token
+        .parent_ancestors()
+        .take_while(|it| it.text_range().end() == prev_token.text_range().end())
         .find_map(ast::IfExpr::cast)
         .is_some()
 }
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
index ff005a29218b..df39591a3346 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/expression.rs
@@ -2182,6 +2182,32 @@ fn foo() { match () { () => if foo {} $0, _ => (), } }
             kw ref
         "#]],
     );
+    check(
+        r#"
+fn foo() -> (i32, i32) { if foo {} el$0 (2, 3) }
+"#,
+        expect![[r#"
+            fn foo fn() -> (i32, i32)
+            bt u32                u32
+            kw const
+            kw crate::
+            kw else
+            kw else if
+            kw false
+            kw for
+            kw if
+            kw if let
+            kw loop
+            kw match
+            kw return
+            kw self::
+            kw true
+            kw unsafe
+            kw while
+            kw while let
+            ex foo()
+        "#]],
+    );
     // FIXME: support else completion after ast::RecordExprField
 }
 

From 2f49df3140280f1cdcaeb653c3fb9774de080f19 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 26 Jan 2026 15:41:43 +0800
Subject: [PATCH 132/319] Improve filter predicate to length cond

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

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
index 2230c391cbad..f2363c6f7ba2 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/extract_function.rs
@@ -257,7 +257,7 @@ fn make_function_name(
 
     let mut name = body
         .suggest_name()
-        .filter(|name| name.contains('_'))
+        .filter(|name| name.len() > 2)
         .unwrap_or_else(|| default_name.to_owned());
     let mut counter = 0;
     while names_in_scope.contains(&name) {

From 38bb09eeff34272c36dfbaeeb31e62f9e0674698 Mon Sep 17 00:00:00 2001
From: Edwin Cheng 
Date: Mon, 26 Jan 2026 16:45:48 +0800
Subject: [PATCH 133/319] Fix rust-src installation command in FAQ

Correct the command to install rust-src in the FAQ.
---
 src/tools/rust-analyzer/docs/book/src/faq.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/docs/book/src/faq.md b/src/tools/rust-analyzer/docs/book/src/faq.md
index 8c143ab94935..9eeb2ae55539 100644
--- a/src/tools/rust-analyzer/docs/book/src/faq.md
+++ b/src/tools/rust-analyzer/docs/book/src/faq.md
@@ -4,7 +4,7 @@
 
 rust-analyzer fails to resolve `None`, and thinks you are binding to a variable
 named `None`. That's usually a sign of a corrupted sysroot. Try removing and re-installing
-it: `rustup component remove rust-src` then `rustup component install rust-src`.
+it: `rustup component remove rust-src` then `rustup component add rust-src`.
 
 ### Rust Analyzer and Cargo compete over the build lock
 

From b75c58d4b3495021b414778d8b91c12285d3d7ee Mon Sep 17 00:00:00 2001
From: kouhe <25522053+kouhe3@users.noreply.github.com>
Date: Mon, 5 Jan 2026 19:10:34 +0800
Subject: [PATCH 134/319] Implement default field values `..` syntax

- Added `RecordSpread` enum to distinguish between no spread, field defaults, and spread expressions
- Updated `FieldData` to include `default_value` field
- Modified record literal lowering to handle default field values
- Updated diagnostics to check for missing fields considering defaults
- Added methods to get matched fields for records for completions
- Enhanced hover support for struct rest patterns
---
 .../crates/hir-def/src/expr_store.rs          |  10 +-
 .../crates/hir-def/src/expr_store/lower.rs    |  14 ++-
 .../src/expr_store/lower/format_args.rs       |   5 +-
 .../crates/hir-def/src/expr_store/pretty.rs   |  20 ++-
 .../rust-analyzer/crates/hir-def/src/hir.rs   |   9 +-
 .../crates/hir-def/src/signatures.rs          |  12 +-
 .../crates/hir-ty/src/diagnostics/expr.rs     |  46 +++++--
 .../hir-ty/src/infer/closure/analysis.rs      |   4 +-
 .../crates/hir-ty/src/infer/expr.rs           |   6 +-
 .../crates/hir-ty/src/infer/mutability.rs     |  10 +-
 .../crates/hir-ty/src/mir/lower.rs            |  11 +-
 .../rust-analyzer/crates/hir/src/semantics.rs |  18 +++
 .../crates/hir/src/source_analyzer.rs         | 118 ++++++++++++++++--
 .../src/handlers/expand_rest_pattern.rs       |   6 +-
 .../ide-completion/src/completions/expr.rs    |   2 +-
 .../ide-completion/src/completions/record.rs  |  14 +--
 .../crates/ide-completion/src/tests/record.rs |  40 +++++-
 .../src/handlers/missing_fields.rs            |  77 ++++++++++++
 .../crates/ide/src/hover/render.rs            |   8 +-
 19 files changed, 355 insertions(+), 75 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
index 10cd460d1d36..edbfd42d1314 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs
@@ -32,7 +32,7 @@ use crate::{
     expr_store::path::Path,
     hir::{
         Array, AsmOperand, Binding, BindingId, Expr, ExprId, ExprOrPatId, Label, LabelId, Pat,
-        PatId, RecordFieldPat, Statement,
+        PatId, RecordFieldPat, RecordSpread, Statement,
     },
     nameres::{DefMap, block_def_map},
     type_ref::{LifetimeRef, LifetimeRefId, PathId, TypeRef, TypeRefId},
@@ -575,8 +575,8 @@ impl ExpressionStore {
                 for field in fields.iter() {
                     f(field.expr);
                 }
-                if let &Some(expr) = spread {
-                    f(expr);
+                if let RecordSpread::Expr(expr) = spread {
+                    f(*expr);
                 }
             }
             Expr::Closure { body, .. } => {
@@ -706,8 +706,8 @@ impl ExpressionStore {
                 for field in fields.iter() {
                     f(field.expr);
                 }
-                if let &Some(expr) = spread {
-                    f(expr);
+                if let RecordSpread::Expr(expr) = spread {
+                    f(*expr);
                 }
             }
             Expr::Closure { body, .. } => {
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 79222615929f..4fbf6d951779 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
@@ -47,7 +47,7 @@ use crate::{
     hir::{
         Array, Binding, BindingAnnotation, BindingId, BindingProblems, CaptureBy, ClosureKind,
         Expr, ExprId, Item, Label, LabelId, Literal, MatchArm, Movability, OffsetOf, Pat, PatId,
-        RecordFieldPat, RecordLitField, Statement, generics::GenericParams,
+        RecordFieldPat, RecordLitField, RecordSpread, Statement, generics::GenericParams,
     },
     item_scope::BuiltinShadowMode,
     item_tree::FieldsShape,
@@ -1266,10 +1266,16 @@ impl<'db> ExprCollector<'db> {
                             Some(RecordLitField { name, expr })
                         })
                         .collect();
-                    let spread = nfl.spread().map(|s| self.collect_expr(s));
+                    let spread_expr = nfl.spread().map(|s| self.collect_expr(s));
+                    let has_spread_syntax = nfl.dotdot_token().is_some();
+                    let spread = match (spread_expr, has_spread_syntax) {
+                        (None, false) => RecordSpread::None,
+                        (None, true) => RecordSpread::FieldDefaults,
+                        (Some(expr), _) => RecordSpread::Expr(expr),
+                    };
                     Expr::RecordLit { path, fields, spread }
                 } else {
-                    Expr::RecordLit { path, fields: Box::default(), spread: None }
+                    Expr::RecordLit { path, fields: Box::default(), spread: RecordSpread::None }
                 };
 
                 self.alloc_expr(record_lit, syntax_ptr)
@@ -1995,7 +2001,7 @@ impl<'db> ExprCollector<'db> {
         }
     }
 
-    fn collect_expr_opt(&mut self, expr: Option) -> ExprId {
+    pub fn collect_expr_opt(&mut self, expr: Option) -> ExprId {
         match expr {
             Some(expr) => self.collect_expr(expr),
             None => self.missing_expr(),
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs
index 7ef84f27f664..51616afb3892 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/lower/format_args.rs
@@ -10,7 +10,8 @@ use crate::{
     builtin_type::BuiltinUint,
     expr_store::{HygieneId, lower::ExprCollector, path::Path},
     hir::{
-        Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, Statement,
+        Array, BindingAnnotation, Expr, ExprId, Literal, Pat, RecordLitField, RecordSpread,
+        Statement,
         format_args::{
             self, FormatAlignment, FormatArgs, FormatArgsPiece, FormatArgument, FormatArgumentKind,
             FormatArgumentsCollector, FormatCount, FormatDebugHex, FormatOptions,
@@ -869,7 +870,7 @@ impl<'db> ExprCollector<'db> {
             self.alloc_expr_desugared(Expr::RecordLit {
                 path: self.lang_path(lang_items.FormatPlaceholder).map(Box::new),
                 fields: Box::new([position, flags, precision, width]),
-                spread: None,
+                spread: RecordSpread::None,
             })
         } else {
             let format_placeholder_new =
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
index f5ef8e1a3595..35f3cd114e36 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store/pretty.rs
@@ -16,7 +16,8 @@ use crate::{
     attrs::AttrFlags,
     expr_store::path::{GenericArg, GenericArgs},
     hir::{
-        Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, Statement,
+        Array, BindingAnnotation, CaptureBy, ClosureKind, Literal, Movability, RecordSpread,
+        Statement,
         generics::{GenericParams, WherePredicate},
     },
     lang_item::LangItemTarget,
@@ -139,7 +140,7 @@ pub fn print_variant_body_hir(db: &dyn DefDatabase, owner: VariantId, edition: E
     }
 
     for (_, data) in fields.fields().iter() {
-        let FieldData { name, type_ref, visibility, is_unsafe } = data;
+        let FieldData { name, type_ref, visibility, is_unsafe, default_value: _ } = data;
         match visibility {
             crate::item_tree::RawVisibility::Module(interned, _visibility_explicitness) => {
                 w!(p, "pub(in {})", interned.display(db, p.edition))
@@ -679,10 +680,17 @@ impl Printer<'_> {
                         p.print_expr(field.expr);
                         wln!(p, ",");
                     }
-                    if let Some(spread) = spread {
-                        w!(p, "..");
-                        p.print_expr(*spread);
-                        wln!(p);
+                    match spread {
+                        RecordSpread::None => {}
+                        RecordSpread::FieldDefaults => {
+                            w!(p, "..");
+                            wln!(p);
+                        }
+                        RecordSpread::Expr(spread_expr) => {
+                            w!(p, "..");
+                            p.print_expr(*spread_expr);
+                            wln!(p);
+                        }
                     }
                 });
                 w!(self, "}}");
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
index 53be0de7d9c3..7781a8fe54ee 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/hir.rs
@@ -187,6 +187,13 @@ impl From for Literal {
     }
 }
 
+#[derive(Debug, Clone, Eq, PartialEq, Copy)]
+pub enum RecordSpread {
+    None,
+    FieldDefaults,
+    Expr(ExprId),
+}
+
 #[derive(Debug, Clone, Eq, PartialEq)]
 pub enum Expr {
     /// This is produced if the syntax tree does not have a required expression piece.
@@ -259,7 +266,7 @@ pub enum Expr {
     RecordLit {
         path: Option>,
         fields: Box<[RecordLitField]>,
-        spread: Option,
+        spread: RecordSpread,
     },
     Field {
         expr: ExprId,
diff --git a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
index 0dd88edbfb08..37c8f762fe5d 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/signatures.rs
@@ -12,7 +12,7 @@ use intern::{Symbol, sym};
 use la_arena::{Arena, Idx};
 use rustc_abi::{IntegerType, ReprOptions};
 use syntax::{
-    NodeOrToken, SyntaxNodePtr, T,
+    AstNode, NodeOrToken, SyntaxNodePtr, T,
     ast::{self, HasGenericParams, HasName, HasVisibility, IsString},
 };
 use thin_vec::ThinVec;
@@ -754,6 +754,7 @@ pub struct FieldData {
     pub type_ref: TypeRefId,
     pub visibility: RawVisibility,
     pub is_unsafe: bool,
+    pub default_value: Option,
 }
 
 pub type LocalFieldId = Idx;
@@ -903,7 +904,14 @@ fn lower_fields(
                     .filter_map(NodeOrToken::into_token)
                     .any(|token| token.kind() == T![unsafe]);
                 let name = field_name(idx, &field);
-                arena.alloc(FieldData { name, type_ref, visibility, is_unsafe });
+
+                // Check if field has default value (only for record fields)
+                let default_value = ast::RecordField::cast(field.syntax().clone())
+                    .and_then(|rf| rf.eq_token().is_some().then_some(rf.expr()))
+                    .flatten()
+                    .map(|expr| col.collect_expr_opt(Some(expr)));
+
+                arena.alloc(FieldData { name, type_ref, visibility, is_unsafe, default_value });
                 idx += 1;
             }
             Err(cfg) => {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
index dd1fc3b36ef8..4e1bb6f4c533 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/expr.rs
@@ -41,7 +41,7 @@ use crate::{
 pub(crate) use hir_def::{
     LocalFieldId, VariantId,
     expr_store::Body,
-    hir::{Expr, ExprId, MatchArm, Pat, PatId, Statement},
+    hir::{Expr, ExprId, MatchArm, Pat, PatId, RecordSpread, Statement},
 };
 
 pub enum BodyValidationDiagnostic {
@@ -123,7 +123,7 @@ impl<'db> ExprValidator<'db> {
         }
 
         for (id, expr) in body.exprs() {
-            if let Some((variant, missed_fields, true)) =
+            if let Some((variant, missed_fields)) =
                 record_literal_missing_fields(db, self.infer, id, expr)
             {
                 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
@@ -154,7 +154,7 @@ impl<'db> ExprValidator<'db> {
         }
 
         for (id, pat) in body.pats() {
-            if let Some((variant, missed_fields, true)) =
+            if let Some((variant, missed_fields)) =
                 record_pattern_missing_fields(db, self.infer, id, pat)
             {
                 self.diagnostics.push(BodyValidationDiagnostic::RecordMissingFields {
@@ -557,9 +557,9 @@ pub fn record_literal_missing_fields(
     infer: &InferenceResult,
     id: ExprId,
     expr: &Expr,
-) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> {
-    let (fields, exhaustive) = match expr {
-        Expr::RecordLit { fields, spread, .. } => (fields, spread.is_none()),
+) -> Option<(VariantId, Vec)> {
+    let (fields, spread) = match expr {
+        Expr::RecordLit { fields, spread, .. } => (fields, spread),
         _ => return None,
     };
 
@@ -571,15 +571,28 @@ pub fn record_literal_missing_fields(
     let variant_data = variant_def.fields(db);
 
     let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // don't show missing fields if:
+    // - has ..expr
+    // - or has default value + ..
+    // - or already in code
     let missed_fields: Vec = variant_data
         .fields()
         .iter()
-        .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) })
+        .filter_map(|(f, d)| {
+            if specified_fields.contains(&d.name)
+                || matches!(spread, RecordSpread::Expr(_))
+                || (d.default_value.is_some() && matches!(spread, RecordSpread::FieldDefaults))
+            {
+                None
+            } else {
+                Some(f)
+            }
+        })
         .collect();
     if missed_fields.is_empty() {
         return None;
     }
-    Some((variant_def, missed_fields, exhaustive))
+    Some((variant_def, missed_fields))
 }
 
 pub fn record_pattern_missing_fields(
@@ -587,9 +600,9 @@ pub fn record_pattern_missing_fields(
     infer: &InferenceResult,
     id: PatId,
     pat: &Pat,
-) -> Option<(VariantId, Vec, /*exhaustive*/ bool)> {
-    let (fields, exhaustive) = match pat {
-        Pat::Record { path: _, args, ellipsis } => (args, !ellipsis),
+) -> Option<(VariantId, Vec)> {
+    let (fields, ellipsis) = match pat {
+        Pat::Record { path: _, args, ellipsis } => (args, *ellipsis),
         _ => return None,
     };
 
@@ -601,15 +614,22 @@ pub fn record_pattern_missing_fields(
     let variant_data = variant_def.fields(db);
 
     let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // don't show missing fields if:
+    // - in code
+    // - or has ..
     let missed_fields: Vec = variant_data
         .fields()
         .iter()
-        .filter_map(|(f, d)| if specified_fields.contains(&d.name) { None } else { Some(f) })
+        .filter_map(
+            |(f, d)| {
+                if specified_fields.contains(&d.name) || ellipsis { None } else { Some(f) }
+            },
+        )
         .collect();
     if missed_fields.is_empty() {
         return None;
     }
-    Some((variant_def, missed_fields, exhaustive))
+    Some((variant_def, missed_fields))
 }
 
 fn types_of_subpatterns_do_match(pat: PatId, body: &Body, infer: &InferenceResult) -> bool {
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs
index b25901cc3b99..5a3eba1a71ae 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/closure/analysis.rs
@@ -8,7 +8,7 @@ use hir_def::{
     expr_store::path::Path,
     hir::{
         Array, AsmOperand, BinaryOp, BindingId, CaptureBy, Expr, ExprId, ExprOrPatId, Pat, PatId,
-        Statement, UnaryOp,
+        RecordSpread, Statement, UnaryOp,
     },
     item_tree::FieldsShape,
     resolver::ValueNs,
@@ -627,7 +627,7 @@ impl<'db> InferenceContext<'_, 'db> {
                 self.consume_expr(expr);
             }
             Expr::RecordLit { fields, spread, .. } => {
-                if let &Some(expr) = spread {
+                if let RecordSpread::Expr(expr) = *spread {
                     self.consume_expr(expr);
                 }
                 self.consume_exprs(fields.iter().map(|it| it.expr));
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 c57d41cc5f73..9f2d9d25b957 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
@@ -8,7 +8,7 @@ use hir_def::{
     expr_store::path::{GenericArgs as HirGenericArgs, Path},
     hir::{
         Array, AsmOperand, AsmOptions, BinaryOp, BindingAnnotation, Expr, ExprId, ExprOrPatId,
-        InlineAsmKind, LabelId, Literal, Pat, PatId, Statement, UnaryOp,
+        InlineAsmKind, LabelId, Literal, Pat, PatId, RecordSpread, Statement, UnaryOp,
     },
     resolver::ValueNs,
 };
@@ -657,8 +657,8 @@ impl<'db> InferenceContext<'_, 'db> {
                         }
                     }
                 }
-                if let Some(expr) = spread {
-                    self.infer_expr(*expr, &Expectation::has_type(ty), ExprIsRead::Yes);
+                if let RecordSpread::Expr(expr) = *spread {
+                    self.infer_expr(expr, &Expectation::has_type(ty), ExprIsRead::Yes);
                 }
                 ty
             }
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
index 729ed214daea..45fa141b6d3d 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/infer/mutability.rs
@@ -2,7 +2,8 @@
 //! between `Deref` and `DerefMut` or `Index` and `IndexMut` or similar.
 
 use hir_def::hir::{
-    Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, Statement, UnaryOp,
+    Array, AsmOperand, BinaryOp, BindingAnnotation, Expr, ExprId, Pat, PatId, RecordSpread,
+    Statement, UnaryOp,
 };
 use rustc_ast_ir::Mutability;
 
@@ -132,8 +133,11 @@ impl<'db> InferenceContext<'_, 'db> {
             Expr::Become { expr } => {
                 self.infer_mut_expr(*expr, Mutability::Not);
             }
-            Expr::RecordLit { path: _, fields, spread } => {
-                self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr).chain(*spread))
+            Expr::RecordLit { path: _, fields, spread, .. } => {
+                self.infer_mut_not_expr_iter(fields.iter().map(|it| it.expr));
+                if let RecordSpread::Expr(expr) = *spread {
+                    self.infer_mut_expr(expr, Mutability::Not);
+                }
             }
             &Expr::Index { base, index } => {
                 if mutability == Mutability::Mut {
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 1579f00e9266..199db7a3e718 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
@@ -9,7 +9,7 @@ use hir_def::{
     expr_store::{Body, ExpressionStore, HygieneId, path::Path},
     hir::{
         ArithOp, Array, BinaryOp, BindingAnnotation, BindingId, ExprId, LabelId, Literal, MatchArm,
-        Pat, PatId, RecordFieldPat, RecordLitField,
+        Pat, PatId, RecordFieldPat, RecordLitField, RecordSpread,
     },
     item_tree::FieldsShape,
     lang_item::LangItems,
@@ -867,16 +867,17 @@ impl<'a, 'db> MirLowerCtx<'a, 'db> {
             }
             Expr::Become { .. } => not_supported!("tail-calls"),
             Expr::Yield { .. } => not_supported!("yield"),
-            Expr::RecordLit { fields, path, spread } => {
-                let spread_place = match spread {
-                    &Some(it) => {
+            Expr::RecordLit { fields, path, spread, .. } => {
+                let spread_place = match *spread {
+                    RecordSpread::Expr(it) => {
                         let Some((p, c)) = self.lower_expr_as_place(current, it, true)? else {
                             return Ok(None);
                         };
                         current = c;
                         Some(p)
                     }
-                    None => None,
+                    RecordSpread::None => None,
+                    RecordSpread::FieldDefaults => not_supported!("empty record spread"),
                 };
                 let variant_id =
                     self.infer.variant_resolution_for_expr(expr_id).ok_or_else(|| match path {
diff --git a/src/tools/rust-analyzer/crates/hir/src/semantics.rs b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
index 98f5739600f3..4bc757da4417 100644
--- a/src/tools/rust-analyzer/crates/hir/src/semantics.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/semantics.rs
@@ -2003,6 +2003,15 @@ impl<'db> SemanticsImpl<'db> {
             .unwrap_or_default()
     }
 
+    pub fn record_literal_matched_fields(
+        &self,
+        literal: &ast::RecordExpr,
+    ) -> Vec<(Field, Type<'db>)> {
+        self.analyze(literal.syntax())
+            .and_then(|it| it.record_literal_matched_fields(self.db, literal))
+            .unwrap_or_default()
+    }
+
     pub fn record_pattern_missing_fields(
         &self,
         pattern: &ast::RecordPat,
@@ -2012,6 +2021,15 @@ impl<'db> SemanticsImpl<'db> {
             .unwrap_or_default()
     }
 
+    pub fn record_pattern_matched_fields(
+        &self,
+        pattern: &ast::RecordPat,
+    ) -> Vec<(Field, Type<'db>)> {
+        self.analyze(pattern.syntax())
+            .and_then(|it| it.record_pattern_matched_fields(self.db, pattern))
+            .unwrap_or_default()
+    }
+
     fn with_ctx) -> T, T>(&self, f: F) -> T {
         let mut ctx = SourceToDefCtx { db: self.db, cache: &mut self.s2d_cache.borrow_mut() };
         f(&mut ctx)
diff --git a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
index 6ba7a42c1946..4e85e299a97f 100644
--- a/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/source_analyzer.rs
@@ -17,7 +17,7 @@ use hir_def::{
         path::Path,
         scope::{ExprScopes, ScopeId},
     },
-    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat},
+    hir::{BindingId, Expr, ExprId, ExprOrPatId, Pat, PatId},
     lang_item::LangItems,
     nameres::MacroSubNs,
     resolver::{HasResolver, Resolver, TypeNs, ValueNs, resolver_for_scope},
@@ -44,6 +44,7 @@ use hir_ty::{
 };
 use intern::sym;
 use itertools::Itertools;
+use rustc_hash::FxHashSet;
 use rustc_type_ir::{
     AliasTyKind,
     inherent::{AdtDef, IntoKind, Ty as _},
@@ -1241,21 +1242,31 @@ impl<'db> SourceAnalyzer<'db> {
         let body = self.store()?;
         let infer = self.infer()?;
 
-        let expr_id = self.expr_id(literal.clone().into())?;
-        let substs = infer.expr_or_pat_ty(expr_id).as_adt()?.1;
-
-        let (variant, missing_fields, _exhaustive) = match expr_id {
-            ExprOrPatId::ExprId(expr_id) => {
-                record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?
-            }
-            ExprOrPatId::PatId(pat_id) => {
-                record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?
-            }
-        };
+        let expr_id = self.expr_id(literal.clone().into())?.as_expr()?;
+        let substs = infer.expr_ty(expr_id).as_adt()?.1;
+        let (variant, missing_fields) =
+            record_literal_missing_fields(db, infer, expr_id, &body[expr_id])?;
         let res = self.missing_fields(db, substs, variant, missing_fields);
         Some(res)
     }
 
+    pub(crate) fn record_literal_matched_fields(
+        &self,
+        db: &'db dyn HirDatabase,
+        literal: &ast::RecordExpr,
+    ) -> Option)>> {
+        let body = self.store()?;
+        let infer = self.infer()?;
+
+        let expr_id = self.expr_id(literal.clone().into())?.as_expr()?;
+        let substs = infer.expr_ty(expr_id).as_adt()?.1;
+        let (variant, matched_fields) =
+            record_literal_matched_fields(db, infer, expr_id, &body[expr_id])?;
+
+        let res = self.missing_fields(db, substs, variant, matched_fields);
+        Some(res)
+    }
+
     pub(crate) fn record_pattern_missing_fields(
         &self,
         db: &'db dyn HirDatabase,
@@ -1267,12 +1278,29 @@ impl<'db> SourceAnalyzer<'db> {
         let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?;
         let substs = infer.pat_ty(pat_id).as_adt()?.1;
 
-        let (variant, missing_fields, _exhaustive) =
+        let (variant, missing_fields) =
             record_pattern_missing_fields(db, infer, pat_id, &body[pat_id])?;
         let res = self.missing_fields(db, substs, variant, missing_fields);
         Some(res)
     }
 
+    pub(crate) fn record_pattern_matched_fields(
+        &self,
+        db: &'db dyn HirDatabase,
+        pattern: &ast::RecordPat,
+    ) -> Option)>> {
+        let body = self.store()?;
+        let infer = self.infer()?;
+
+        let pat_id = self.pat_id(&pattern.clone().into())?.as_pat()?;
+        let substs = infer.pat_ty(pat_id).as_adt()?.1;
+
+        let (variant, matched_fields) =
+            record_pattern_matched_fields(db, infer, pat_id, &body[pat_id])?;
+        let res = self.missing_fields(db, substs, variant, matched_fields);
+        Some(res)
+    }
+
     fn missing_fields(
         &self,
         db: &'db dyn HirDatabase,
@@ -1810,3 +1838,67 @@ pub(crate) fn name_hygiene(db: &dyn HirDatabase, name: InFile<&SyntaxNode>) -> H
     let ctx = span_map.span_at(name.value.text_range().start()).ctx;
     HygieneId::new(ctx.opaque_and_semiopaque(db))
 }
+
+fn record_literal_matched_fields(
+    db: &dyn HirDatabase,
+    infer: &InferenceResult,
+    id: ExprId,
+    expr: &Expr,
+) -> Option<(VariantId, Vec)> {
+    let (fields, _spread) = match expr {
+        Expr::RecordLit { fields, spread, .. } => (fields, spread),
+        _ => return None,
+    };
+
+    let variant_def = infer.variant_resolution_for_expr(id)?;
+    if let VariantId::UnionId(_) = variant_def {
+        return None;
+    }
+
+    let variant_data = variant_def.fields(db);
+
+    let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // suggest fields if:
+    // - not in code
+    let matched_fields: Vec = variant_data
+        .fields()
+        .iter()
+        .filter_map(|(f, d)| (!specified_fields.contains(&d.name)).then_some(f))
+        .collect();
+    if matched_fields.is_empty() {
+        return None;
+    }
+    Some((variant_def, matched_fields))
+}
+
+fn record_pattern_matched_fields(
+    db: &dyn HirDatabase,
+    infer: &InferenceResult,
+    id: PatId,
+    pat: &Pat,
+) -> Option<(VariantId, Vec)> {
+    let (fields, _ellipsis) = match pat {
+        Pat::Record { path: _, args, ellipsis } => (args, *ellipsis),
+        _ => return None,
+    };
+
+    let variant_def = infer.variant_resolution_for_pat(id)?;
+    if let VariantId::UnionId(_) = variant_def {
+        return None;
+    }
+
+    let variant_data = variant_def.fields(db);
+
+    let specified_fields: FxHashSet<_> = fields.iter().map(|f| &f.name).collect();
+    // suggest fields if:
+    // - not in code
+    let matched_fields: Vec = variant_data
+        .fields()
+        .iter()
+        .filter_map(|(f, d)| if !specified_fields.contains(&d.name) { Some(f) } else { None })
+        .collect();
+    if matched_fields.is_empty() {
+        return None;
+    }
+    Some((variant_def, matched_fields))
+}
diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
index b746099e7279..867ac4851864 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/expand_rest_pattern.rs
@@ -33,8 +33,8 @@ fn expand_record_rest_pattern(
     record_pat: ast::RecordPat,
     rest_pat: ast::RestPat,
 ) -> Option<()> {
-    let missing_fields = ctx.sema.record_pattern_missing_fields(&record_pat);
-    if missing_fields.is_empty() {
+    let matched_fields = ctx.sema.record_pattern_matched_fields(&record_pat);
+    if matched_fields.is_empty() {
         cov_mark::hit!(no_missing_fields);
         return None;
     }
@@ -53,7 +53,7 @@ fn expand_record_rest_pattern(
         |builder| {
             let make = SyntaxFactory::with_mappings();
             let mut editor = builder.make_editor(rest_pat.syntax());
-            let new_fields = old_field_list.fields().chain(missing_fields.iter().map(|(f, _)| {
+            let new_fields = old_field_list.fields().chain(matched_fields.iter().map(|(f, _)| {
                 make.record_pat_field_shorthand(
                     make.ident_pat(
                         false,
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
index 77734c5d6f98..8c532e0f4d04 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/expr.rs
@@ -340,7 +340,7 @@ pub(crate) fn complete_expr_path(
                             let missing_fields =
                                 ctx.sema.record_literal_missing_fields(record_expr);
                             if !missing_fields.is_empty() {
-                                add_default_update(acc, ctx, ty);
+                                add_default_update(acc, ctx, ty.as_ref());
                             }
                         }
                     };
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
index c5bfdcb8b734..12c564af5cba 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/completions/record.rs
@@ -36,7 +36,7 @@ pub(crate) fn complete_record_pattern_fields(
                     true => return,
                 }
             }
-            _ => ctx.sema.record_pattern_missing_fields(record_pat),
+            _ => ctx.sema.record_pattern_matched_fields(record_pat),
         };
         complete_fields(acc, ctx, missing_fields);
     }
@@ -69,14 +69,14 @@ pub(crate) fn complete_record_expr_fields(
             }
         }
         _ => {
-            let missing_fields = ctx.sema.record_literal_missing_fields(record_expr);
+            let suggest_fields = ctx.sema.record_literal_matched_fields(record_expr);
             let update_exists = record_expr
                 .record_expr_field_list()
                 .is_some_and(|list| list.dotdot_token().is_some());
 
-            if !missing_fields.is_empty() && !update_exists {
+            if !suggest_fields.is_empty() && !update_exists {
                 cov_mark::hit!(functional_update_field);
-                add_default_update(acc, ctx, ty);
+                add_default_update(acc, ctx, ty.as_ref());
             }
             if dot_prefix {
                 cov_mark::hit!(functional_update_one_dot);
@@ -90,7 +90,7 @@ pub(crate) fn complete_record_expr_fields(
                 item.add_to(acc, ctx.db);
                 return;
             }
-            missing_fields
+            suggest_fields
         }
     };
     complete_fields(acc, ctx, missing_fields);
@@ -99,11 +99,11 @@ pub(crate) fn complete_record_expr_fields(
 pub(crate) fn add_default_update(
     acc: &mut Completions,
     ctx: &CompletionContext<'_>,
-    ty: Option>,
+    ty: Option<&hir::TypeInfo<'_>>,
 ) {
     let default_trait = ctx.famous_defs().core_default_Default();
     let impls_default_trait = default_trait
-        .zip(ty.as_ref())
+        .zip(ty)
         .is_some_and(|(default_trait, ty)| ty.original.impls_trait(ctx.db, default_trait, &[]));
     if impls_default_trait {
         // FIXME: This should make use of scope_def like completions so we get all the other goodies
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
index d9be6556fa5b..045b2d03b051 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/record.rs
@@ -286,6 +286,24 @@ fn main() {
     );
 }
 
+#[test]
+fn functional_update_fields_completion() {
+    // Complete fields before functional update `..`
+    check(
+        r#"
+struct Point { x: i32 = 0, y: i32 = 0 }
+
+fn main() {
+    let p = Point { $0, .. };
+}
+"#,
+        expect![[r#"
+            fd x i32
+            fd y i32
+        "#]],
+    );
+}
+
 #[test]
 fn empty_union_literal() {
     check(
@@ -302,7 +320,27 @@ fn foo() {
             fd bar f32
             fd foo u32
         "#]],
-    )
+    );
+}
+
+#[test]
+fn record_pattern_field_with_rest_pat() {
+    // When .. is present, complete all unspecified fields (even those with default values)
+    check(
+        r#"
+struct UserInfo { id: i32, age: f32, email: u64 }
+
+fn foo(u1: UserInfo) {
+    let UserInfo { id, $0, .. } = u1;
+}
+"#,
+        expect![[r#"
+            fd age   f32
+            fd email u64
+            kw mut
+            kw ref
+        "#]],
+    );
 }
 
 #[test]
diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
index 2a251382d465..d5f25dfaf208 100644
--- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
+++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/missing_fields.rs
@@ -857,4 +857,81 @@ pub struct Claims {
         "#,
         );
     }
+
+    #[test]
+    fn test_default_field_values_basic() {
+        // This should work without errors - only field 'b' is required
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct Struct {
+    a: usize = 0,
+    b: usize,
+}
+
+fn main() {
+    Struct { b: 1, .. };
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_default_field_values_missing_field_error() {
+        // This should report a missing field error because email is required
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct UserInfo {
+    id: i32,
+    age: f32 = 1.0,
+    email: String,
+}
+
+fn main() {
+    UserInfo { id: 20, .. };
+//  ^^^^^^^^💡 error: missing structure fields:
+//         |- email
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_default_field_values_requires_spread_syntax() {
+        // without `..` should report missing fields
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct Point {
+    x: i32 = 0,
+    y: i32 = 0,
+}
+
+fn main() {
+    Point { x: 0 };
+//  ^^^^^💡 error: missing structure fields:
+//      |- y
+}
+"#,
+        );
+    }
+
+    #[test]
+    fn test_default_field_values_pattern_matching() {
+        check_diagnostics(
+            r#"
+#![feature(default_field_values)]
+struct Point {
+    x: i32 = 0,
+    y: i32 = 0,
+    z: i32,
+}
+
+fn main() {
+    let Point { x, .. } = Point { z: 5, .. };
+}
+"#,
+        );
+    }
 }
diff --git a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
index feac5fff84a7..15ea92d1c6ec 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/render.rs
@@ -272,9 +272,9 @@ pub(super) fn struct_rest_pat(
     edition: Edition,
     display_target: DisplayTarget,
 ) -> HoverResult {
-    let missing_fields = sema.record_pattern_missing_fields(pattern);
+    let matched_fields = sema.record_pattern_matched_fields(pattern);
 
-    // if there are no missing fields, the end result is a hover that shows ".."
+    // if there are no matched fields, the end result is a hover that shows ".."
     // should be left in to indicate that there are no more fields in the pattern
     // example, S {a: 1, b: 2, ..} when struct S {a: u32, b: u32}
 
@@ -285,13 +285,13 @@ pub(super) fn struct_rest_pat(
             targets.push(item);
         }
     };
-    for (_, t) in &missing_fields {
+    for (_, t) in &matched_fields {
         walk_and_push_ty(sema.db, t, &mut push_new_def);
     }
 
     res.markup = {
         let mut s = String::from(".., ");
-        for (f, _) in &missing_fields {
+        for (f, _) in &matched_fields {
             s += f.display(sema.db, display_target).to_string().as_ref();
             s += ", ";
         }

From 76b6623267dc944fd61e06741327f74ee4820a21 Mon Sep 17 00:00:00 2001
From: David Wood 
Date: Fri, 23 Jan 2026 11:06:57 +0000
Subject: [PATCH 135/319] target: fix destabilising target-spec-json

---
 compiler/rustc_target/src/spec/mod.rs                 | 3 +++
 tests/run-make/rust-lld-custom-target/rmake.rs        | 6 +++++-
 tests/run-make/rustdoc-target-spec-json-path/rmake.rs | 8 +++++++-
 tests/run-make/target-specs/rmake.rs                  | 9 ++++++++-
 tests/ui/check-cfg/values-target-json.rs              | 3 ++-
 5 files changed, 25 insertions(+), 4 deletions(-)

diff --git a/compiler/rustc_target/src/spec/mod.rs b/compiler/rustc_target/src/spec/mod.rs
index ef475630d494..164db15d24e0 100644
--- a/compiler/rustc_target/src/spec/mod.rs
+++ b/compiler/rustc_target/src/spec/mod.rs
@@ -3352,6 +3352,9 @@ impl Target {
 
                 Err(format!("could not find specification for target {target_tuple:?}"))
             }
+            TargetTuple::TargetJson { ref contents, .. } if !unstable_options => {
+                Err("custom targets are unstable and require `-Zunstable-options`".to_string())
+            }
             TargetTuple::TargetJson { ref contents, .. } => Target::from_json(contents),
         }
     }
diff --git a/tests/run-make/rust-lld-custom-target/rmake.rs b/tests/run-make/rust-lld-custom-target/rmake.rs
index 90ba424ffe94..d281d820f47b 100644
--- a/tests/run-make/rust-lld-custom-target/rmake.rs
+++ b/tests/run-make/rust-lld-custom-target/rmake.rs
@@ -15,7 +15,11 @@ fn main() {
     // Compile to a custom target spec with rust-lld enabled by default. We'll check that by asking
     // the linker to display its version number with a link-arg.
     assert_rustc_uses_lld(
-        rustc().crate_type("cdylib").target("custom-target.json").input("lib.rs"),
+        rustc()
+            .crate_type("cdylib")
+            .target("custom-target.json")
+            .arg("-Zunstable-options")
+            .input("lib.rs"),
     );
 
     // But it can also be disabled via linker features.
diff --git a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
index d43aa9b90ac7..8660556564f1 100644
--- a/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
+++ b/tests/run-make/rustdoc-target-spec-json-path/rmake.rs
@@ -5,8 +5,14 @@ use run_make_support::{cwd, rustc, rustdoc};
 
 fn main() {
     let out_dir = "rustdoc-target-spec-json-path";
-    rustc().crate_type("lib").input("dummy_core.rs").target("target.json").run();
+    rustc()
+        .arg("-Zunstable-options")
+        .crate_type("lib")
+        .input("dummy_core.rs")
+        .target("target.json")
+        .run();
     rustdoc()
+        .arg("-Zunstable-options")
         .input("my_crate.rs")
         .out_dir(out_dir)
         .library_search_path(cwd())
diff --git a/tests/run-make/target-specs/rmake.rs b/tests/run-make/target-specs/rmake.rs
index 69292af5fd69..6c88f3164e9e 100644
--- a/tests/run-make/target-specs/rmake.rs
+++ b/tests/run-make/target-specs/rmake.rs
@@ -20,13 +20,20 @@ fn main() {
         .target("my-incomplete-platform.json")
         .run_fail()
         .assert_stderr_contains("missing field `llvm-target`");
-    let test_platform = rustc()
+    let _ = rustc()
         .input("foo.rs")
         .target("my-x86_64-unknown-linux-gnu-platform")
         .crate_type("lib")
         .emit("asm")
         .run_fail()
         .assert_stderr_contains("custom targets are unstable and require `-Zunstable-options`");
+    let _ = rustc()
+        .input("foo.rs")
+        .target("my-awesome-platform.json")
+        .crate_type("lib")
+        .emit("asm")
+        .run_fail()
+        .assert_stderr_contains("custom targets are unstable and require `-Zunstable-options`");
     rustc()
         .arg("-Zunstable-options")
         .env("RUST_TARGET_PATH", ".")
diff --git a/tests/ui/check-cfg/values-target-json.rs b/tests/ui/check-cfg/values-target-json.rs
index defb286ed19b..21b6af9b5560 100644
--- a/tests/ui/check-cfg/values-target-json.rs
+++ b/tests/ui/check-cfg/values-target-json.rs
@@ -4,7 +4,8 @@
 //@ check-pass
 //@ no-auto-check-cfg
 //@ needs-llvm-components: x86
-//@ compile-flags: --crate-type=lib --check-cfg=cfg() --target={{src-base}}/check-cfg/my-awesome-platform.json
+//@ compile-flags: --crate-type=lib --check-cfg=cfg()
+//@ compile-flags: -Zunstable-options --target={{src-base}}/check-cfg/my-awesome-platform.json
 //@ ignore-backends: gcc
 
 #![feature(lang_items, no_core, auto_traits, rustc_attrs)]

From 2c2602bf0d1edc47cb886dbe4be24a247eb64ab5 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 26 Jan 2026 16:13:06 +0200
Subject: [PATCH 136/319] Fix macro matching of `meta` then `=>` or `==`

The parser declared it was invalid meta because it consumed the lone `=`, which is incorrect.
---
 .../src/macro_expansion_tests/mbe/matching.rs | 20 +++++++++++++++++++
 .../crates/parser/src/grammar/attributes.rs   |  2 +-
 2 files changed, 21 insertions(+), 1 deletion(-)

diff --git a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
index e33a366769b0..bbadcf8794bf 100644
--- a/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
+++ b/src/tools/rust-analyzer/crates/hir-def/src/macro_expansion_tests/mbe/matching.rs
@@ -237,3 +237,23 @@ fn test() {
 "#]],
     );
 }
+
+#[test]
+fn meta_fat_arrow() {
+    check(
+        r#"
+macro_rules! m {
+    ( $m:meta => ) => {};
+}
+
+m! { foo => }
+    "#,
+        expect![[r#"
+macro_rules! m {
+    ( $m:meta => ) => {};
+}
+
+
+    "#]],
+    );
+}
diff --git a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
index 54b5c8a275a8..c0cf43a87bf7 100644
--- a/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/grammar/attributes.rs
@@ -70,7 +70,7 @@ pub(super) fn meta(p: &mut Parser<'_>) {
     paths::attr_path(p);
 
     match p.current() {
-        T![=] => {
+        T![=] if !p.at(T![=>]) && !p.at(T![==]) => {
             p.bump(T![=]);
             if expressions::expr(p).is_none() {
                 p.error("expected expression");

From cd8fe54a1a743c0bf88f8a9c28ae8554345f3d58 Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Mon, 26 Jan 2026 01:46:13 +0800
Subject: [PATCH 137/319] Fix semicolon for toggle_macro_delimiter

Example
---
```rust
macro_rules! sth {
    () => {};
}

sth!$0{ }
```

(old test `sth!{};` is a syntax error in item place)

**Before this PR**

```rust
macro_rules! sth {
    () => {};
}

sth![ ]
```

**After this PR**

```rust
macro_rules! sth {
    () => {};
}

sth![ ];
```
---
 .../src/handlers/toggle_macro_delimiter.rs    | 149 +++++++++++++++++-
 1 file changed, 145 insertions(+), 4 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
index bf1546986ed2..60b0797f028a 100644
--- a/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
+++ b/src/tools/rust-analyzer/crates/ide-assists/src/handlers/toggle_macro_delimiter.rs
@@ -86,7 +86,14 @@ pub(crate) fn toggle_macro_delimiter(acc: &mut Assists, ctx: &AssistContext<'_>)
                 }
                 MacroDelims::LCur | MacroDelims::RCur => {
                     editor.replace(ltoken, make.token(T!['[']));
-                    editor.replace(rtoken, make.token(T![']']));
+                    if semicolon.is_some() || !needs_semicolon(token_tree) {
+                        editor.replace(rtoken, make.token(T![']']));
+                    } else {
+                        editor.replace_with_many(
+                            rtoken,
+                            vec![make.token(T![']']).into(), make.token(T![;]).into()],
+                        );
+                    }
                 }
             }
             editor.add_mappings(make.finish_with_mappings());
@@ -103,6 +110,30 @@ fn macro_semicolon(makro: &ast::MacroCall) -> Option {
     })
 }
 
+fn needs_semicolon(tt: ast::TokenTree) -> bool {
+    (|| {
+        let call = ast::MacroCall::cast(tt.syntax().parent()?)?;
+        let container = call.syntax().parent()?;
+        let kind = container.kind();
+
+        if call.semicolon_token().is_some() {
+            return Some(false);
+        }
+
+        Some(
+            ast::ItemList::can_cast(kind)
+                || ast::SourceFile::can_cast(kind)
+                || ast::AssocItemList::can_cast(kind)
+                || ast::ExternItemList::can_cast(kind)
+                || ast::MacroItems::can_cast(kind)
+                || ast::MacroExpr::can_cast(kind)
+                    && ast::ExprStmt::cast(container.parent()?)
+                        .is_some_and(|it| it.semicolon_token().is_none()),
+        )
+    })()
+    .unwrap_or(false)
+}
+
 #[cfg(test)]
 mod tests {
     use crate::tests::{check_assist, check_assist_not_applicable};
@@ -161,7 +192,7 @@ macro_rules! sth {
     () => {};
 }
 
-sth!$0{ };
+sth!$0{ }
             "#,
             r#"
 macro_rules! sth {
@@ -170,7 +201,117 @@ macro_rules! sth {
 
 sth![ ];
             "#,
-        )
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+fn foo() -> i32 {
+    sth!$0{ }
+    2
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+
+fn foo() -> i32 {
+    sth![ ];
+    2
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() {
+    sth!$0{ };
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() {
+    sth![ ];
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    sth!$0{ }
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    sth![ ]
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+impl () {
+    sth!$0{}
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {};
+}
+impl () {
+    sth![];
+}
+            "#,
+        );
+
+        check_assist(
+            toggle_macro_delimiter,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    bar(sth!$0{ })
+}
+            "#,
+            r#"
+macro_rules! sth {
+    () => {2};
+}
+
+fn foo() -> i32 {
+    bar(sth![ ])
+}
+            "#,
+        );
     }
 
     #[test]
@@ -204,7 +345,7 @@ mod abc {
         () => {};
     }
 
-    sth!$0{ };
+    sth!$0{ }
 }
             "#,
             r#"

From 996d72bce8845a4c4dcb0d41823cf0af4c11c59a Mon Sep 17 00:00:00 2001
From: Martin Nordholts 
Date: Sat, 17 Jan 2026 17:53:10 +0100
Subject: [PATCH 138/319] compiletest: Support `--extern` modifiers with
 `proc-macro` directive

So that `pub-priv1.rs` test does not have to (ab)use the `aux-crate`
directive for this purpose.

This is very edge-casey so I don't think we should document this in
rustc-dev-guide. If someone needs to do this they will look at the code
and easily find the functionality.

This is a bit hacky since `--extern priv:pm.rs` is not valid, but we can
make our directives work however we want. And I think this is a fine
pragmatic approach. Doing it "the right way" would be a lot of work for
not much gain. Plus, that work can be done incrementally in small steps
in the future if wanted.
---
 .../compiletest/src/directives/auxiliary.rs   | 20 +++++++++++++++++--
 .../src/directives/auxiliary/tests.rs         | 16 +++++++++++++++
 src/tools/compiletest/src/runtest.rs          |  2 +-
 tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs |  5 -----
 tests/ui/privacy/pub-priv-dep/pub-priv1.rs    |  2 +-
 5 files changed, 36 insertions(+), 9 deletions(-)

diff --git a/src/tools/compiletest/src/directives/auxiliary.rs b/src/tools/compiletest/src/directives/auxiliary.rs
index 14cbab640eb6..0e7e370adbd4 100644
--- a/src/tools/compiletest/src/directives/auxiliary.rs
+++ b/src/tools/compiletest/src/directives/auxiliary.rs
@@ -26,8 +26,12 @@ pub struct AuxCrate {
 }
 
 /// The value of a `proc-macro` directive.
-#[derive(Clone, Debug, Default)]
+#[derive(Clone, Debug, Default, PartialEq, Eq)]
 pub(crate) struct ProcMacro {
+    /// Contains `--extern` modifiers, if any. See the tracking issue for more
+    /// info: 
+    /// With `proc-macro: noprelude:bar.rs` this will be `noprelude`.
+    pub extern_modifiers: Option,
     /// With `proc-macro: bar.rs` this will be `bar.rs`.
     pub path: String,
 }
@@ -108,5 +112,17 @@ fn parse_aux_crate(r: String) -> AuxCrate {
 }
 
 fn parse_proc_macro(r: String) -> ProcMacro {
-    ProcMacro { path: r.trim().to_string() }
+    let r = r.trim();
+
+    // Matches:
+    //   path
+    //   modifiers:path
+    let caps = static_regex!(r"^(?:(?[^=]*?):)?(?.*)$")
+        .captures(r)
+        .expect("can never fail");
+
+    let modifiers = caps.name("modifiers").map(|m| m.as_str().to_string());
+    let path = caps["path"].to_string();
+
+    ProcMacro { extern_modifiers: modifiers, path }
 }
diff --git a/src/tools/compiletest/src/directives/auxiliary/tests.rs b/src/tools/compiletest/src/directives/auxiliary/tests.rs
index ad205eaabfda..74fff630692e 100644
--- a/src/tools/compiletest/src/directives/auxiliary/tests.rs
+++ b/src/tools/compiletest/src/directives/auxiliary/tests.rs
@@ -25,3 +25,19 @@ fn test_aux_crate_value_with_modifiers() {
 fn test_aux_crate_value_invalid() {
     parse_aux_crate("foo.rs".to_string());
 }
+
+#[test]
+fn test_proc_macro_value_no_modifiers() {
+    assert_eq!(
+        ProcMacro { extern_modifiers: None, path: "foo.rs".to_string() },
+        parse_proc_macro("foo.rs".to_string())
+    );
+}
+
+#[test]
+fn test_proc_macro_value_with_modifiers() {
+    assert_eq!(
+        ProcMacro { extern_modifiers: Some("noprelude".to_string()), path: "foo.rs".to_string() },
+        parse_proc_macro("noprelude:foo.rs".to_string())
+    );
+}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 2bfb73f05d16..502db382e269 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1302,7 +1302,7 @@ impl<'test> TestCx<'test> {
             let crate_name = path_to_crate_name(&proc_macro.path);
             add_extern(
                 rustc,
-                None, // `extern_modifiers`
+                proc_macro.extern_modifiers.as_deref(),
                 &crate_name,
                 &proc_macro.path,
                 AuxType::ProcMacro,
diff --git a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
index 9e2aa898afe8..d45f2639d182 100644
--- a/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
+++ b/tests/ui/privacy/pub-priv-dep/auxiliary/pm.rs
@@ -1,8 +1,3 @@
-//@ force-host
-//@ no-prefer-dynamic
-
-#![crate_type = "proc-macro"]
-
 extern crate proc_macro;
 use proc_macro::TokenStream;
 
diff --git a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
index eae0f9756a10..09ad59582d84 100644
--- a/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
+++ b/tests/ui/privacy/pub-priv-dep/pub-priv1.rs
@@ -1,6 +1,6 @@
 //@ aux-crate:priv:priv_dep=priv_dep.rs
 //@ aux-build:pub_dep.rs
-//@ aux-crate:priv:pm=pm.rs
+//@ proc-macro:priv:pm.rs
 //@ compile-flags: -Zunstable-options
 
 // Basic behavior check of exported_private_dependencies from either a public

From d02e33db2d5f2b9f5fd87eebd9dd883bfdf8a09b Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Mon, 26 Jan 2026 18:40:51 +0200
Subject: [PATCH 139/319] Handle `Self::EnumVariant` and `Self` on traits in
 doclinks

---
 .../rust-analyzer/crates/hir/src/attrs.rs     | 66 ++++++++++++-----
 .../crates/ide/src/hover/tests.rs             | 72 +++++++++++++++++++
 2 files changed, 120 insertions(+), 18 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir/src/attrs.rs b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
index cba1b39e5254..cfb95e07c362 100644
--- a/src/tools/rust-analyzer/crates/hir/src/attrs.rs
+++ b/src/tools/rust-analyzer/crates/hir/src/attrs.rs
@@ -3,7 +3,8 @@
 use cfg::CfgExpr;
 use either::Either;
 use hir_def::{
-    AssocItemId, AttrDefId, FieldId, LifetimeParamId, ModuleDefId, TypeOrConstParamId,
+    AssocItemId, AttrDefId, FieldId, GenericDefId, ItemContainerId, LifetimeParamId, ModuleDefId,
+    TraitId, TypeOrConstParamId,
     attrs::{AttrFlags, Docs, IsInnerDoc},
     expr_store::path::Path,
     item_scope::ItemInNs,
@@ -22,6 +23,7 @@ use hir_ty::{
     next_solver::{DbInterner, TypingMode, infer::DbInternerInferExt},
 };
 use intern::Symbol;
+use stdx::never;
 
 use crate::{
     Adt, AsAssocItem, AssocItem, BuiltinType, Const, ConstParam, DocLinkDef, Enum, ExternCrateDecl,
@@ -357,13 +359,46 @@ fn resolve_assoc_or_field(
     ns: Option,
 ) -> Option {
     let path = Path::from_known_path_with_no_generic(path);
-    // FIXME: This does not handle `Self` on trait definitions, which we should resolve to the
-    // trait itself.
     let base_def = resolver.resolve_path_in_type_ns_fully(db, &path)?;
 
+    let handle_trait = |id: TraitId| {
+        // Doc paths in this context may only resolve to an item of this trait
+        // (i.e. no items of its supertraits), so we need to handle them here
+        // independently of others.
+        id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
+            let def = match *assoc_id {
+                AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
+                AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
+                AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
+            };
+            DocLinkDef::ModuleDef(def)
+        })
+    };
     let ty = match base_def {
         TypeNs::SelfType(id) => Impl::from(id).self_ty(db),
-        TypeNs::GenericParam(_) => {
+        TypeNs::GenericParam(param) => {
+            let generic_params = db.generic_params(param.parent());
+            if generic_params[param.local_id()].is_trait_self() {
+                // `Self::assoc` in traits should refer to the trait itself.
+                let parent_trait = |container| match container {
+                    ItemContainerId::TraitId(trait_) => handle_trait(trait_),
+                    _ => {
+                        never!("container {container:?} should be a trait");
+                        None
+                    }
+                };
+                return match param.parent() {
+                    GenericDefId::TraitId(trait_) => handle_trait(trait_),
+                    GenericDefId::ConstId(it) => parent_trait(it.loc(db).container),
+                    GenericDefId::FunctionId(it) => parent_trait(it.loc(db).container),
+                    GenericDefId::TypeAliasId(it) => parent_trait(it.loc(db).container),
+                    _ => {
+                        never!("type param {param:?} should belong to a trait");
+                        None
+                    }
+                };
+            }
+
             // Even if this generic parameter has some trait bounds, rustdoc doesn't
             // resolve `name` to trait items.
             return None;
@@ -384,19 +419,7 @@ fn resolve_assoc_or_field(
             alias.ty(db)
         }
         TypeNs::BuiltinType(id) => BuiltinType::from(id).ty(db),
-        TypeNs::TraitId(id) => {
-            // Doc paths in this context may only resolve to an item of this trait
-            // (i.e. no items of its supertraits), so we need to handle them here
-            // independently of others.
-            return id.trait_items(db).items.iter().find(|it| it.0 == name).map(|(_, assoc_id)| {
-                let def = match *assoc_id {
-                    AssocItemId::FunctionId(it) => ModuleDef::Function(it.into()),
-                    AssocItemId::ConstId(it) => ModuleDef::Const(it.into()),
-                    AssocItemId::TypeAliasId(it) => ModuleDef::TypeAlias(it.into()),
-                };
-                DocLinkDef::ModuleDef(def)
-            });
-        }
+        TypeNs::TraitId(id) => return handle_trait(id),
         TypeNs::ModuleId(_) => {
             return None;
         }
@@ -414,7 +437,14 @@ fn resolve_assoc_or_field(
     let variant_def = match ty.as_adt()? {
         Adt::Struct(it) => it.into(),
         Adt::Union(it) => it.into(),
-        Adt::Enum(_) => return None,
+        Adt::Enum(enum_) => {
+            // Can happen on `Self::Variant` (otherwise would be fully resolved by the resolver).
+            return enum_
+                .id
+                .enum_variants(db)
+                .variant(&name)
+                .map(|variant| DocLinkDef::ModuleDef(ModuleDef::Variant(variant.into())));
+        }
     };
     resolve_field(db, variant_def, name, ns)
 }
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 0b518021e39e..7900a0dc9991 100644
--- a/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
+++ b/src/tools/rust-analyzer/crates/ide/src/hover/tests.rs
@@ -11239,3 +11239,75 @@ impl Foo for T {
         "#]],
     );
 }
+
+#[test]
+fn doc_link_enum_self_variant() {
+    check(
+        r#"
+/// - [`VariantOne$0`](Self::One)
+pub enum MyEnum {
+    One,
+    Two,
+}
+    "#,
+        expect![[r#"
+            *[`VariantOne`](Self::One)*
+
+            ```rust
+            ra_test_fixture::MyEnum
+            ```
+
+            ```rust
+            One = 0
+            ```
+        "#]],
+    );
+}
+
+#[test]
+fn doc_link_trait_self() {
+    check(
+        r#"
+/// - [`do_something$0`](Self::do_something)
+pub trait MyTrait {
+    fn do_something(&self);
+}
+    "#,
+        expect![[r#"
+            *[`do_something`](Self::do_something)*
+
+            ```rust
+            ra_test_fixture::MyTrait
+            ```
+
+            ```rust
+            pub trait MyTrait
+            pub fn do_something(&self)
+            ```
+        "#]],
+    );
+    check(
+        r#"
+pub trait MyTrait {
+    /// - [`do_something$0`](Self::do_something)
+    fn do_something(&self);
+}
+    "#,
+        expect![[r#"
+            *[`do_something`](Self::do_something)*
+
+            ```rust
+            ra_test_fixture::MyTrait
+            ```
+
+            ```rust
+            pub trait MyTrait
+            pub fn do_something(&self)
+            ```
+
+            ---
+
+            * [`do_something`](https://docs.rs/ra_test_fixture/*/ra_test_fixture/trait.MyTrait.html#tymethod.do_something)
+        "#]],
+    );
+}

From cd18a8d287d1a42f358d007ab72ec534b4cedd5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= 
Date: Wed, 12 Mar 2025 12:09:19 +0100
Subject: [PATCH 140/319] Tweak `VecCache` to improve performance

---
 .../rustc_data_structures/src/vec_cache.rs    | 72 +++++++++++++------
 .../src/vec_cache/tests.rs                    | 15 +++-
 2 files changed, 63 insertions(+), 24 deletions(-)

diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs
index 599970663db8..70524bae624a 100644
--- a/compiler/rustc_data_structures/src/vec_cache.rs
+++ b/compiler/rustc_data_structures/src/vec_cache.rs
@@ -29,8 +29,6 @@ struct Slot {
 struct SlotIndex {
     // the index of the bucket in VecCache (0 to 20)
     bucket_idx: usize,
-    // number of entries in that bucket
-    entries: usize,
     // the index of the slot within the bucket
     index_in_bucket: usize,
 }
@@ -39,12 +37,12 @@ struct SlotIndex {
 // compile-time. Visiting all powers of two is enough to hit all the buckets.
 //
 // We confirm counts are accurate in the slot_index_exhaustive test.
-const ENTRIES_BY_BUCKET: [usize; 21] = {
-    let mut entries = [0; 21];
+const ENTRIES_BY_BUCKET: [usize; BUCKETS] = {
+    let mut entries = [0; BUCKETS];
     let mut key = 0;
     loop {
         let si = SlotIndex::from_index(key);
-        entries[si.bucket_idx] = si.entries;
+        entries[si.bucket_idx] = si.entries();
         if key == 0 {
             key = 1;
         } else if key == (1 << 31) {
@@ -56,7 +54,14 @@ const ENTRIES_BY_BUCKET: [usize; 21] = {
     entries
 };
 
+const BUCKETS: usize = 21;
+
 impl SlotIndex {
+    /// The total possible number of entries in the bucket
+    const fn entries(&self) -> usize {
+        if self.bucket_idx == 0 { 1 << 12 } else { 1 << (self.bucket_idx + 11) }
+    }
+
     // This unpacks a flat u32 index into identifying which bucket it belongs to and the offset
     // within that bucket. As noted in the VecCache docs, buckets double in size with each index.
     // Typically that would mean 31 buckets (2^0 + 2^1 ... + 2^31 = u32::MAX - 1), but to reduce
@@ -70,18 +75,13 @@ impl SlotIndex {
     const fn from_index(idx: u32) -> Self {
         const FIRST_BUCKET_SHIFT: usize = 12;
         if idx < (1 << FIRST_BUCKET_SHIFT) {
-            return SlotIndex {
-                bucket_idx: 0,
-                entries: 1 << FIRST_BUCKET_SHIFT,
-                index_in_bucket: idx as usize,
-            };
+            return SlotIndex { bucket_idx: 0, index_in_bucket: idx as usize };
         }
         // We already ruled out idx 0, so this `ilog2` never panics (and the check optimizes away)
         let bucket = idx.ilog2() as usize;
         let entries = 1 << bucket;
         SlotIndex {
             bucket_idx: bucket - FIRST_BUCKET_SHIFT + 1,
-            entries,
             index_in_bucket: idx as usize - entries,
         }
     }
@@ -98,7 +98,7 @@ impl SlotIndex {
         if ptr.is_null() {
             return None;
         }
-        assert!(self.index_in_bucket < self.entries);
+        debug_assert!(self.index_in_bucket < self.entries());
         // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this
         // must be inbounds.
         let slot = unsafe { ptr.add(self.index_in_bucket) };
@@ -126,11 +126,12 @@ impl SlotIndex {
 
     fn bucket_ptr(&self, bucket: &AtomicPtr>) -> *mut Slot {
         let ptr = bucket.load(Ordering::Acquire);
-        if ptr.is_null() { self.initialize_bucket(bucket) } else { ptr }
+        if ptr.is_null() { Self::initialize_bucket(bucket, self.bucket_idx) } else { ptr }
     }
 
     #[cold]
-    fn initialize_bucket(&self, bucket: &AtomicPtr>) -> *mut Slot {
+    #[inline(never)]
+    fn initialize_bucket(bucket: &AtomicPtr>, bucket_idx: usize) -> *mut Slot {
         static LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
 
         // If we are initializing the bucket, then acquire a global lock.
@@ -144,8 +145,8 @@ impl SlotIndex {
         // OK, now under the allocator lock, if we're still null then it's definitely us that will
         // initialize this bucket.
         if ptr.is_null() {
-            let bucket_layout =
-                std::alloc::Layout::array::>(self.entries as usize).unwrap();
+            let bucket_len = SlotIndex { bucket_idx, index_in_bucket: 0 }.entries();
+            let bucket_layout = std::alloc::Layout::array::>(bucket_len).unwrap();
             // This is more of a sanity check -- this code is very cold, so it's safe to pay a
             // little extra cost here.
             assert!(bucket_layout.size() > 0);
@@ -171,7 +172,7 @@ impl SlotIndex {
         let bucket = unsafe { buckets.get_unchecked(self.bucket_idx) };
         let ptr = self.bucket_ptr(bucket);
 
-        assert!(self.index_in_bucket < self.entries);
+        debug_assert!(self.index_in_bucket < self.entries());
         // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this
         // must be inbounds.
         let slot = unsafe { ptr.add(self.index_in_bucket) };
@@ -204,6 +205,31 @@ impl SlotIndex {
             Err(_) => false,
         }
     }
+
+    /// Inserts into the map, given that the slot is unique, so it won't race with other threads.
+    #[inline]
+    unsafe fn put_unique(&self, buckets: &[AtomicPtr>; 21], value: V, extra: u32) {
+        // SAFETY: `bucket_idx` is ilog2(u32).saturating_sub(11), which is at most 21, i.e.,
+        // in-bounds of buckets.
+        let bucket = unsafe { buckets.get_unchecked(self.bucket_idx) };
+        let ptr = self.bucket_ptr(bucket);
+
+        debug_assert!(self.index_in_bucket < self.entries());
+        // SAFETY: `bucket` was allocated (so <= isize in total bytes) to hold `entries`, so this
+        // must be inbounds.
+        let slot = unsafe { ptr.add(self.index_in_bucket) };
+
+        // SAFETY: We known our slot is unique as a precondition of this function, so this can't race.
+        unsafe {
+            (&raw mut (*slot).value).write(value);
+        }
+
+        // SAFETY: initialized bucket has zeroed all memory within the bucket, so we are valid for
+        // AtomicU32 access.
+        let index_and_lock = unsafe { &(*slot).index_and_lock };
+
+        index_and_lock.store(extra.checked_add(2).unwrap(), Ordering::Release);
+    }
 }
 
 /// In-memory cache for queries whose keys are densely-numbered IDs
@@ -229,11 +255,11 @@ pub struct VecCache {
     // Bucket 19: 1073741824
     // Bucket 20: 2147483648
     // The total number of entries if all buckets are initialized is u32::MAX-1.
-    buckets: [AtomicPtr>; 21],
+    buckets: [AtomicPtr>; BUCKETS],
 
     // In the compiler's current usage these are only *read* during incremental and self-profiling.
     // They are an optimization over iterating the full buckets array.
-    present: [AtomicPtr>; 21],
+    present: [AtomicPtr>; BUCKETS],
     len: AtomicUsize,
 
     key: PhantomData<(K, I)>,
@@ -307,9 +333,11 @@ where
         let slot_idx = SlotIndex::from_index(key);
         if slot_idx.put(&self.buckets, value, index.index() as u32) {
             let present_idx = self.len.fetch_add(1, Ordering::Relaxed);
-            let slot = SlotIndex::from_index(present_idx as u32);
-            // We should always be uniquely putting due to `len` fetch_add returning unique values.
-            assert!(slot.put(&self.present, (), key));
+            let slot = SlotIndex::from_index(u32::try_from(present_idx).unwrap());
+            // SAFETY: We should always be uniquely putting due to `len` fetch_add returning unique values.
+            // We can't get here if `len` overflows because `put` will not succeed u32::MAX + 1 times
+            // as it will run out of slots.
+            unsafe { slot.put_unique(&self.present, (), key) };
         }
     }
 
diff --git a/compiler/rustc_data_structures/src/vec_cache/tests.rs b/compiler/rustc_data_structures/src/vec_cache/tests.rs
index 9b60913ec922..f588442eee62 100644
--- a/compiler/rustc_data_structures/src/vec_cache/tests.rs
+++ b/compiler/rustc_data_structures/src/vec_cache/tests.rs
@@ -68,6 +68,13 @@ fn slot_entries_table() {
     );
 }
 
+#[test]
+fn bucket_entries_matches() {
+    for i in 0..BUCKETS {
+        assert_eq!(SlotIndex { bucket_idx: i, index_in_bucket: 0 }.entries(), ENTRIES_BY_BUCKET[i]);
+    }
+}
+
 #[test]
 #[cfg(not(miri))]
 fn slot_index_exhaustive() {
@@ -81,14 +88,18 @@ fn slot_index_exhaustive() {
     let mut prev = slot_idx;
     for idx in 1..=u32::MAX {
         let slot_idx = SlotIndex::from_index(idx);
+
+        // SAFETY: Ensure indices don't go out of bounds of buckets.
+        assert!(slot_idx.index_in_bucket < slot_idx.entries());
+
         if prev.bucket_idx == slot_idx.bucket_idx {
             assert_eq!(prev.index_in_bucket + 1, slot_idx.index_in_bucket);
         } else {
             assert_eq!(slot_idx.index_in_bucket, 0);
         }
 
-        assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.entries as u32);
-        assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.entries, "{}", idx);
+        assert_eq!(buckets[slot_idx.bucket_idx], slot_idx.entries() as u32);
+        assert_eq!(ENTRIES_BY_BUCKET[slot_idx.bucket_idx], slot_idx.entries(), "{}", idx);
 
         prev = slot_idx;
     }

From bcb76f4ea6ee380972b1187423def5091c4ef25c Mon Sep 17 00:00:00 2001
From: zakie 
Date: Tue, 27 Jan 2026 14:57:42 +0900
Subject: [PATCH 141/319] docs: fix outdated Debian Ports ISO reference

---
 src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
index 1efea86df92b..5334a176027f 100644
--- a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
+++ b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
@@ -52,7 +52,7 @@ Atari systems or emulated environments such as QEMU version 4.2 or newer or ARAn
 ISO images for installation are provided by the Debian Ports team and can be obtained
 from the Debian CD image server available at:
 
-[https://cdimage.debian.org/cdimage/ports/current](https://cdimage.debian.org/cdimage/ports/current/)
+[https://cdimage.debian.org/cdimage/ports/](https://cdimage.debian.org/cdimage/ports/)
 
 Documentation for Debian/m68k is available on the Debian Wiki at:
 

From 6664fc61946f5bc3c2f8e372310ce83ac41e2ab0 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 27 Jan 2026 08:57:25 +0100
Subject: [PATCH 142/319] minor: Downgrade noisy log

---
 src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 62a3b3a17bdf..71c4b2accce9 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -309,10 +309,10 @@ impl GlobalState {
 
         let event_dbg_msg = format!("{event:?}");
         tracing::debug!(?loop_start, ?event, "handle_event");
-        if tracing::enabled!(tracing::Level::INFO) {
+        if tracing::enabled!(tracing::Level::TRACE) {
             let task_queue_len = self.task_pool.handle.len();
             if task_queue_len > 0 {
-                tracing::info!("task queue len: {}", task_queue_len);
+                tracing::trace!("task queue len: {}", task_queue_len);
             }
         }
 

From 3d6f46c6f1ab182e50bb0c90186367f81b7f9379 Mon Sep 17 00:00:00 2001
From: zakie 
Date: Tue, 27 Jan 2026 18:55:15 +0900
Subject: [PATCH 143/319] docs: point m68k install link to final Debian 12.0
 release

---
 src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
index 5334a176027f..c15a57c1ceac 100644
--- a/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
+++ b/src/doc/rustc/src/platform-support/m68k-unknown-linux-gnu.md
@@ -52,7 +52,7 @@ Atari systems or emulated environments such as QEMU version 4.2 or newer or ARAn
 ISO images for installation are provided by the Debian Ports team and can be obtained
 from the Debian CD image server available at:
 
-[https://cdimage.debian.org/cdimage/ports/](https://cdimage.debian.org/cdimage/ports/)
+[https://cdimage.debian.org/cdimage/ports/12.0/m68k/](https://cdimage.debian.org/cdimage/ports/12.0/m68k/)
 
 Documentation for Debian/m68k is available on the Debian Wiki at:
 

From c48186e9389c1b930adb9416a4e5b4c2c16fc670 Mon Sep 17 00:00:00 2001
From: Chayim Refael Friedman 
Date: Tue, 27 Jan 2026 14:48:40 +0200
Subject: [PATCH 144/319] Fix a panic where an opaque was constrained to an
 impossible type in method autoderef

---
 .../hir-ty/src/method_resolution/probe.rs     | 23 +++++++++---
 .../infer/canonical/instantiate.rs            |  4 ++-
 .../crates/hir-ty/src/tests/opaque_types.rs   | 36 +++++++++++++++++++
 3 files changed, 57 insertions(+), 6 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
index fdd501723fb5..fc2bd87ee429 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/method_resolution/probe.rs
@@ -285,11 +285,15 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
                 let infcx = self.infcx;
                 let (self_ty, var_values) = infcx.instantiate_canonical(&query_input);
                 debug!(?self_ty, ?query_input, "probe_op: Mode::Path");
+                let prev_opaque_entries =
+                    self.infcx.inner.borrow_mut().opaque_types().num_entries();
                 MethodAutoderefStepsResult {
                     steps: smallvec![CandidateStep {
-                        self_ty: self
-                            .infcx
-                            .make_query_response_ignoring_pending_obligations(var_values, self_ty),
+                        self_ty: self.infcx.make_query_response_ignoring_pending_obligations(
+                            var_values,
+                            self_ty,
+                            prev_opaque_entries
+                        ),
                         self_ty_is_opaque: false,
                         autoderefs: 0,
                         from_unsafe_deref: false,
@@ -376,6 +380,8 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
             // infer var is not an opaque.
             let infcx = self.infcx;
             let (self_ty, inference_vars) = infcx.instantiate_canonical(self_ty);
+            let prev_opaque_entries = infcx.inner.borrow_mut().opaque_types().num_entries();
+
             let self_ty_is_opaque = |ty: Ty<'_>| {
                 if let TyKind::Infer(InferTy::TyVar(vid)) = ty.kind() {
                     infcx.has_opaques_with_sub_unified_hidden_type(vid)
@@ -414,6 +420,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
                             self_ty: infcx.make_query_response_ignoring_pending_obligations(
                                 inference_vars,
                                 ty,
+                                prev_opaque_entries,
                             ),
                             self_ty_is_opaque: self_ty_is_opaque(ty),
                             autoderefs: d,
@@ -437,6 +444,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
                             self_ty: infcx.make_query_response_ignoring_pending_obligations(
                                 inference_vars,
                                 ty,
+                                prev_opaque_entries,
                             ),
                             self_ty_is_opaque: self_ty_is_opaque(ty),
                             autoderefs: d,
@@ -461,13 +469,17 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
                         ty: infcx.make_query_response_ignoring_pending_obligations(
                             inference_vars,
                             final_ty,
+                            prev_opaque_entries,
                         ),
                     })
                 }
                 TyKind::Error(_) => Some(MethodAutoderefBadTy {
                     reached_raw_pointer,
-                    ty: infcx
-                        .make_query_response_ignoring_pending_obligations(inference_vars, final_ty),
+                    ty: infcx.make_query_response_ignoring_pending_obligations(
+                        inference_vars,
+                        final_ty,
+                        prev_opaque_entries,
+                    ),
                 }),
                 TyKind::Array(elem_ty, _) => {
                     let autoderefs = steps.iter().filter(|s| s.reachable_via_deref).count() - 1;
@@ -475,6 +487,7 @@ impl<'a, 'db> MethodResolutionContext<'a, 'db> {
                         self_ty: infcx.make_query_response_ignoring_pending_obligations(
                             inference_vars,
                             Ty::new_slice(infcx.interner, elem_ty),
+                            prev_opaque_entries,
                         ),
                         self_ty_is_opaque: false,
                         autoderefs,
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
index b758042e85b0..61d1e9774622 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/next_solver/infer/canonical/instantiate.rs
@@ -15,6 +15,7 @@ use crate::next_solver::{
     infer::{
         InferCtxt, InferOk, InferResult,
         canonical::{QueryRegionConstraints, QueryResponse, canonicalizer::OriginalQueryValues},
+        opaque_types::table::OpaqueTypeStorageEntries,
         traits::{ObligationCause, PredicateObligations},
     },
 };
@@ -194,6 +195,7 @@ impl<'db> InferCtxt<'db> {
         &self,
         inference_vars: CanonicalVarValues<'db>,
         answer: T,
+        prev_entries: OpaqueTypeStorageEntries,
     ) -> Canonical<'db, QueryResponse<'db, T>>
     where
         T: TypeFoldable>,
@@ -209,7 +211,7 @@ impl<'db> InferCtxt<'db> {
             .inner
             .borrow_mut()
             .opaque_type_storage
-            .iter_opaque_types()
+            .opaque_types_added_since(prev_entries)
             .map(|(k, v)| (k, v.ty))
             .collect();
 
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs
index ca986336ff30..21d830ed51e3 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/opaque_types.rs
@@ -1,5 +1,7 @@
 use expect_test::expect;
 
+use crate::tests::check_infer;
+
 use super::{check_infer_with_mismatches, check_no_mismatches, check_types};
 
 #[test]
@@ -176,3 +178,37 @@ fn main() {
         "#,
     );
 }
+
+#[test]
+fn regression_21455() {
+    check_infer(
+        r#"
+//- minicore: copy
+
+struct Vec(T);
+impl Vec {
+    pub fn new() -> Self { loop {} }
+}
+
+pub struct Miku {}
+
+impl Miku {
+    pub fn all_paths_to(&self) -> impl Copy {
+        Miku {
+            full_paths: Vec::new(),
+        }
+    }
+}
+    "#,
+        expect![[r#"
+            61..72 '{ loop {} }': Vec
+            63..70 'loop {}': !
+            68..70 '{}': ()
+            133..137 'self': &'? Miku
+            152..220 '{     ...     }': Miku
+            162..214 'Miku {...     }': Miku
+            193..201 'Vec::new': fn new<{unknown}>() -> Vec<{unknown}>
+            193..203 'Vec::new()': Vec<{unknown}>
+        "#]],
+    );
+}

From c420ed5632d90b793a5ab75f47129af7cf312923 Mon Sep 17 00:00:00 2001
From: Lukas Wirth 
Date: Tue, 27 Jan 2026 14:03:16 +0100
Subject: [PATCH 145/319] fix: Do not panic if rust-analyzer fails to spawn the
 discover command

---
 .../crates/rust-analyzer/src/command.rs       |  8 +++--
 .../crates/rust-analyzer/src/discover.rs      |  4 +--
 .../crates/rust-analyzer/src/main_loop.rs     | 34 ++++++++-----------
 .../crates/rust-analyzer/src/test_runner.rs   |  2 +-
 4 files changed, 24 insertions(+), 24 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs
index 2f052618cdfa..49ce6db4ea9a 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/command.rs
@@ -10,6 +10,7 @@ use std::{
     process::{ChildStderr, ChildStdout, Command, Stdio},
 };
 
+use anyhow::Context;
 use crossbeam_channel::Sender;
 use paths::Utf8PathBuf;
 use process_wrap::std::{StdChildWrapper, StdCommandWrap};
@@ -156,7 +157,7 @@ impl CommandHandle {
         parser: impl JsonLinesParser,
         sender: Sender,
         out_file: Option,
-    ) -> std::io::Result {
+    ) -> anyhow::Result {
         command.stdout(Stdio::piped()).stderr(Stdio::piped()).stdin(Stdio::null());
 
         let program = command.get_program().into();
@@ -168,7 +169,10 @@ impl CommandHandle {
         child.wrap(process_wrap::std::ProcessSession);
         #[cfg(windows)]
         child.wrap(process_wrap::std::JobObject);
-        let mut child = child.spawn().map(JodGroupChild)?;
+        let mut child = child
+            .spawn()
+            .map(JodGroupChild)
+            .with_context(|| "Failed to spawn command: {child:?}")?;
 
         let stdout = child.0.stdout().take().unwrap();
         let stderr = child.0.stderr().take().unwrap();
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
index f129f156a030..098b6a4d986d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/discover.rs
@@ -1,6 +1,6 @@
 //! Infrastructure for lazy project discovery. Currently only support rust-project.json discovery
 //! via a custom discover command.
-use std::{io, path::Path};
+use std::path::Path;
 
 use crossbeam_channel::Sender;
 use ide_db::FxHashMap;
@@ -47,7 +47,7 @@ impl DiscoverCommand {
         &self,
         discover_arg: DiscoverArgument,
         current_dir: &Path,
-    ) -> io::Result {
+    ) -> anyhow::Result {
         let command = &self.command[0];
         let args = &self.command[1..];
 
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
index 71c4b2accce9..f5cead5d8f9d 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs
@@ -825,33 +825,29 @@ impl GlobalState {
             }
             Task::DiscoverLinkedProjects(arg) => {
                 if let Some(cfg) = self.config.discover_workspace_config() {
-                    // the clone is unfortunately necessary to avoid a borrowck error when
-                    // `self.report_progress` is called later
-                    let title = &cfg.progress_label.clone();
                     let command = cfg.command.clone();
                     let discover = DiscoverCommand::new(self.discover_sender.clone(), command);
 
-                    if self.discover_jobs_active == 0 {
-                        self.report_progress(title, Progress::Begin, None, None, None);
-                    }
-                    self.discover_jobs_active += 1;
-
                     let arg = match arg {
                         DiscoverProjectParam::Buildfile(it) => DiscoverArgument::Buildfile(it),
                         DiscoverProjectParam::Path(it) => DiscoverArgument::Path(it),
                     };
 
-                    let handle = discover
-                        .spawn(
-                            arg,
-                            &std::env::current_dir()
-                                .expect("Failed to get cwd during project discovery"),
-                        )
-                        .unwrap_or_else(|e| {
-                            panic!("Failed to spawn project discovery command: {e}")
-                        });
-
-                    self.discover_handles.push(handle);
+                    match discover.spawn(arg, self.config.root_path().as_ref()) {
+                        Ok(handle) => {
+                            if self.discover_jobs_active == 0 {
+                                let title = &cfg.progress_label.clone();
+                                self.report_progress(title, Progress::Begin, None, None, None);
+                            }
+                            self.discover_jobs_active += 1;
+                            self.discover_handles.push(handle)
+                        }
+                        Err(e) => self.show_message(
+                            lsp_types::MessageType::ERROR,
+                            format!("Failed to spawn project discovery command: {e:#}"),
+                            false,
+                        ),
+                    }
                 }
             }
             Task::FetchBuildData(progress) => {
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
index f0020f9088e3..0d9c8310d858 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/test_runner.rs
@@ -101,7 +101,7 @@ impl CargoTestHandle {
         ws_target_dir: Option<&Utf8Path>,
         test_target: TestTarget,
         sender: Sender,
-    ) -> std::io::Result {
+    ) -> anyhow::Result {
         let mut cmd = toolchain::command(Tool::Cargo.path(), root, &options.extra_env);
         cmd.env("RUSTC_BOOTSTRAP", "1");
         cmd.arg("--color=always");

From bf12b8bca5e4e6c313f043de0301e68fdb7cec7d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Tue, 27 Jan 2026 15:19:00 +0200
Subject: [PATCH 146/319] Fix linking of proc-macro-srv-cli

---
 src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs | 5 +++++
 .../crates/proc-macro-srv-cli/tests/legacy_json.rs           | 4 ++++
 2 files changed, 9 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs
index 9e6f03bf4604..8475c05ae8a1 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/lib.rs
@@ -2,5 +2,10 @@
 //!
 //! This module exposes the server main loop and protocol format for integration testing.
 
+#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
+
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_driver as _;
+
 #[cfg(feature = "sysroot-abi")]
 pub mod main_loop;
diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
index c0dbfd1679f7..562cf0c2516f 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/legacy_json.rs
@@ -4,6 +4,10 @@
 //! channels without needing to spawn the actual server and client processes.
 
 #![cfg(feature = "sysroot-abi")]
+#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))]
+
+#[cfg(feature = "in-rust-tree")]
+extern crate rustc_driver as _;
 
 mod common {
     pub(crate) mod utils;

From 3491099f88c40da8dcd7d19fde0e45ccde6b2d4e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Tue, 27 Jan 2026 15:56:27 +0200
Subject: [PATCH 147/319] Prepare for merging from rust-lang/rust

This updates the rust-version file to 94a0cd15f5976fa35e5e6784e621c04e9f958e57.
---
 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 1fe86330b4a8..17b678eed936 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-004d710faff53f8764a1cf69d87a5a5963850b60
+94a0cd15f5976fa35e5e6784e621c04e9f958e57

From 52525bd27d663011e2e8e7e56448c44b90ad6a8a Mon Sep 17 00:00:00 2001
From: Jonathan Pallant 
Date: Mon, 19 Jan 2026 18:02:45 +0000
Subject: [PATCH 148/319] Codegen tests for Arm Cortex-R82

This PR adds checks to the `aarch64v8r-unknown-none` target to verify that if the Cortex-R82 CPU is enabled (with `-Ctarget-cpu=cortex-r82`), that the appropriate additional AArch64 features are enabled.

This is important because Cortex-R82 is (currently) the only processor implementing Armv8-R AArch64 and it implements a number of Armv8 features over and above the baseline for the architecture. Many of these features are of interest to safety-critical firmware development (for example `FEAT_RASv1p1`, which adds support for the *RAS Common Fault Injection Model Extension*) and so we anticipate them being enabled when building such firmware.

We are offering these tests upstream in-lieu of a full Cortex-R82 specific target because we understand the Project has a preference for architecture-baseline targets over CPU-specific targets.

This PR builds on and requires https://github.com/rust-lang/rust/pull/150863, but we've pulled them out as a separate PR.

This PR was developed by Ferrous Systems on behalf of Arm. Arm is the owner of these changes.
---
 tests/ui/asm/aarch64v8r.rs |   6 +-
 tests/ui/asm/cortex-r82.rs | 176 +++++++++++++++++++++++++++++++++++++
 2 files changed, 181 insertions(+), 1 deletion(-)
 create mode 100644 tests/ui/asm/cortex-r82.rs

diff --git a/tests/ui/asm/aarch64v8r.rs b/tests/ui/asm/aarch64v8r.rs
index 6b582bb730f0..abc254ad5f8e 100644
--- a/tests/ui/asm/aarch64v8r.rs
+++ b/tests/ui/asm/aarch64v8r.rs
@@ -1,11 +1,15 @@
 // Codegen test of mandatory Armv8-R AArch64 extensions
 
+// The Cortex-R82 CPU is an implementation of the Arm v8-R AArch64 ISA so
+// it also implements the ISA-level mandatory extensions. We check that with a revision
 //@ add-minicore
-//@ revisions: hf sf
+//@ revisions: hf sf r82
 //@ [hf] compile-flags: --target aarch64v8r-unknown-none
 //@ [hf] needs-llvm-components: aarch64
 //@ [sf] compile-flags: --target aarch64v8r-unknown-none-softfloat
 //@ [sf] needs-llvm-components: aarch64
+//@ [r82] compile-flags: --target aarch64v8r-unknown-none -C target-cpu=cortex-r82
+//@ [r82] needs-llvm-components: aarch64
 //@ build-pass
 //@ ignore-backends: gcc
 
diff --git a/tests/ui/asm/cortex-r82.rs b/tests/ui/asm/cortex-r82.rs
new file mode 100644
index 000000000000..a64349b37af7
--- /dev/null
+++ b/tests/ui/asm/cortex-r82.rs
@@ -0,0 +1,176 @@
+// Codegen test of mandatory Cortex-R82 extensions
+
+//@ add-minicore
+//@ compile-flags: --target aarch64v8r-unknown-none -C target-cpu=cortex-r82
+//@ needs-llvm-components: aarch64
+//@ build-pass
+//@ ignore-backends: gcc
+
+#![deny(dead_code)]
+#![feature(no_core)]
+#![no_core]
+#![no_main]
+#![crate_type = "rlib"]
+
+extern crate minicore;
+use minicore::*;
+
+/* # Mandatory extensions
+ *
+ * A `//` comment indicates that the extension has no associated assembly instruction and cannot
+ * be codegen tested
+ * A `/*  */` comment indicates that the extension is being tested in the ISA level codegen test
+ * (`tests/ui/asm/aarch64v8r.rs`)
+ *
+ * ## References:
+ *
+ * - Arm Cortex-R82 Processor Technical Reference Manual Revision r3p1 (102670_0301_06_en Issue 6)
+ *   section 3.2.1 has the list of mandatory extensions
+ * - Arm Architecture Reference Manual for A-profile architecture (ARM DDI 0487) -- has the
+ *   mapping from features to instructions
+ * - Feature names in A-profile architecture (109697_0100_02_en Version 1.0) -- overview of what
+ *   each extension mean
+ * */
+pub fn mandatory_extensions() {
+    // FEAT_GICv3
+    // FEAT_GICv3p1
+    // FEAT_GICv3_TDIR
+    feat_pmuv3();
+    // FEAT_ETMv4
+    // FEAT_ETMv4p1
+    // FEAT_ETMv4p2
+    // FEAT_ETMv4p3
+    // FEAT_ETMv4p4
+    // FEAT_ETMv4p5
+    /* FEAT_RAS */
+    // FEAT_PCSRv8
+    feat_ssbs();
+    feat_ssbs2();
+    // FEAT_CSV2
+    // FEAT_CSV2_1p1
+    // FEAT_CSV3
+    feat_sb();
+    feat_specres();
+    feat_dgh();
+    // FEAT_nTLBPA
+    /* FEAT_CRC32 */
+    /* FEAT_LSE */
+    feat_rdm();
+    /* FEAT_HPDS */
+    /* FEAT_PAN */
+    // FEAT_HAFDBS
+    // FEAT_PMUv3p1
+    // FEAT_TTCNP
+    // FEAT_XNX
+    /* FEAT_UAO */
+    feat_pan2();
+    feat_dpb();
+    /* FEAT_Debugv8p2 */
+    /* FEAT_ASMv8p2 */
+    // FEAT_IESB
+    feat_fp16();
+    // FEAT_PCSRv8p2
+    feat_dotprod();
+    feat_fhm();
+    feat_dpb2();
+    /* FEAT_PAuth */
+    // FEAT_PACQARMA3
+    // FEAT_PAuth2
+    // FEAT_FPAC
+    // FEAT_FPACCOMBINE
+    // FEAT_CONSTPACFIELD
+    feat_jscvt();
+    /* FEAT_LRCPC */
+    feat_fcma();
+    // FEAT_DoPD
+    // FEAT_SEL2
+    /* FEAT_S2FWB */
+    /* FEAT_DIT */
+    /* FEAT_IDST */
+    /* FEAT_FlagM */
+    /* FEAT_LSE2 */
+    /* FEAT_LRCPC2 */
+    /* FEAT_TLBIOS */
+    /* FEAT_TLBIRANGE */
+    /* FEAT_TTL */
+    // FEAT_BBM
+    // FEAT_CNTSC
+    feat_rasv1p1();
+    // FEAT_Debugv8p4
+    feat_pmuv3p4();
+    feat_trf();
+    // FEAT_TTST
+    // FEAT_E0PD
+}
+
+fn feat_pmuv3() {
+    unsafe { asm!("mrs x0, PMCCFILTR_EL0") }
+}
+
+fn feat_ssbs() {
+    unsafe { asm!("msr SSBS, 1") }
+}
+
+fn feat_ssbs2() {
+    unsafe { asm!("mrs x0, SSBS") }
+}
+
+fn feat_sb() {
+    unsafe { asm!("sb") }
+}
+
+fn feat_specres() {
+    unsafe { asm!("cfp rctx, x0") }
+}
+
+fn feat_dgh() {
+    unsafe { asm!("dgh") }
+}
+
+fn feat_rdm() {
+    unsafe { asm!("sqrdmlah v0.4h, v1.4h, v2.4h") }
+}
+
+fn feat_pan2() {
+    unsafe { asm!("AT S1E1RP, x0") }
+}
+
+fn feat_dpb() {
+    unsafe { asm!("DC CVAP, x0") }
+}
+
+fn feat_fp16() {
+    unsafe { asm!("fmulx h0, h1, h2") }
+}
+
+fn feat_dotprod() {
+    unsafe { asm!("sdot V0.4S, V1.16B, V2.16B") }
+}
+
+fn feat_fhm() {
+    unsafe { asm!("fmlal v0.2s, v1.2h, v2.2h") }
+}
+
+fn feat_dpb2() {
+    unsafe { asm!("DC CVADP, x0") }
+}
+
+fn feat_jscvt() {
+    unsafe { asm!("fjcvtzs w0, d1") }
+}
+
+fn feat_fcma() {
+    unsafe { asm!("fcadd v0.4h, v1.4h, v2.4h, #90") }
+}
+
+fn feat_rasv1p1() {
+    unsafe { asm!("mrs x0, ERXMISC2_EL1") }
+}
+
+fn feat_pmuv3p4() {
+    unsafe { asm!("mrs x0, PMMIR_EL1") }
+}
+
+fn feat_trf() {
+    unsafe { asm!("tsb csync") }
+}

From 1f72a25b54ed6bfeab105e531447c3384d38bc64 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= 
Date: Tue, 27 Jan 2026 16:40:49 +0200
Subject: [PATCH 149/319] Fix sysroot-abi build

---
 src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
index 77c3809e5d14..e04f744ae2b0 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs
@@ -22,8 +22,11 @@
 )]
 #![deny(deprecated_safe, clippy::undocumented_unsafe_blocks)]
 
+#[cfg(not(feature = "in-rust-tree"))]
+extern crate proc_macro as rustc_proc_macro;
 #[cfg(feature = "in-rust-tree")]
 extern crate rustc_driver as _;
+#[cfg(feature = "in-rust-tree")]
 extern crate rustc_proc_macro;
 
 #[cfg(not(feature = "in-rust-tree"))]

From 90f0f4b75f0a79d39abc712f51e3ad674ccb4e8c Mon Sep 17 00:00:00 2001
From: Ayush Singh 
Date: Mon, 12 Jan 2026 12:59:47 +0530
Subject: [PATCH 150/319] std: sys: uefi: os: Implement join_paths

- PATHS_SEP is defined as global const since I will implement
  split_paths in the future.
- Tested using OVMF using QEMU.

Signed-off-by: Ayush Singh 
---
 library/std/src/env.rs             |  4 ++--
 library/std/src/sys/pal/uefi/os.rs | 26 +++++++++++++++++++++++---
 2 files changed, 25 insertions(+), 5 deletions(-)

diff --git a/library/std/src/env.rs b/library/std/src/env.rs
index 5c068ad2471a..6669fada60d2 100644
--- a/library/std/src/env.rs
+++ b/library/std/src/env.rs
@@ -518,8 +518,8 @@ pub struct JoinPathsError {
 ///
 /// Returns an [`Err`] (containing an error message) if one of the input
 /// [`Path`]s contains an invalid character for constructing the `PATH`
-/// variable (a double quote on Windows or a colon on Unix), or if the system
-/// does not have a `PATH`-like variable (e.g. UEFI or WASI).
+/// variable (a double quote on Windows or a colon on Unix or semicolon on
+/// UEFI), or if the system does not have a `PATH`-like variable (e.g. WASI).
 ///
 /// # Examples
 ///
diff --git a/library/std/src/sys/pal/uefi/os.rs b/library/std/src/sys/pal/uefi/os.rs
index 178f7f506341..5b9785c8371e 100644
--- a/library/std/src/sys/pal/uefi/os.rs
+++ b/library/std/src/sys/pal/uefi/os.rs
@@ -5,10 +5,13 @@ use super::{helpers, unsupported_err};
 use crate::ffi::{OsStr, OsString};
 use crate::marker::PhantomData;
 use crate::os::uefi;
+use crate::os::uefi::ffi::{OsStrExt, OsStringExt};
 use crate::path::{self, PathBuf};
 use crate::ptr::NonNull;
 use crate::{fmt, io};
 
+const PATHS_SEP: u16 = b';' as u16;
+
 pub fn getcwd() -> io::Result {
     match helpers::open_shell() {
         Some(shell) => {
@@ -54,17 +57,34 @@ impl<'a> Iterator for SplitPaths<'a> {
 #[derive(Debug)]
 pub struct JoinPathsError;
 
-pub fn join_paths(_paths: I) -> Result
+// UEFI Shell Path variable is defined in Section 3.6.1
+// [UEFI Shell Specification](https://uefi.org/sites/default/files/resources/UEFI_Shell_2_2.pdf).
+pub fn join_paths(paths: I) -> Result
 where
     I: Iterator,
     T: AsRef,
 {
-    Err(JoinPathsError)
+    let mut joined = Vec::new();
+
+    for (i, path) in paths.enumerate() {
+        if i > 0 {
+            joined.push(PATHS_SEP)
+        }
+
+        let v = path.as_ref().encode_wide().collect::>();
+        if v.contains(&PATHS_SEP) {
+            return Err(JoinPathsError);
+        }
+
+        joined.extend_from_slice(&v);
+    }
+
+    Ok(OsString::from_wide(&joined))
 }
 
 impl fmt::Display for JoinPathsError {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        "not supported on this platform yet".fmt(f)
+        "path segment contains `;`".fmt(f)
     }
 }
 

From 829c314f2820a330d09b58c2e8c9a7d1b6991341 Mon Sep 17 00:00:00 2001
From: mu001999 
Date: Wed, 28 Jan 2026 01:18:37 +0800
Subject: [PATCH 151/319] Check proj's parent when checking dyn compatibility

---
 .../src/traits/dyn_compatibility.rs                 |  9 ++++++---
 .../const-generics/mgca/type-const-used-in-trait.rs | 13 +++++++++++++
 2 files changed, 19 insertions(+), 3 deletions(-)
 create mode 100644 tests/ui/const-generics/mgca/type-const-used-in-trait.rs

diff --git a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
index be70612653ce..9371b55b6363 100644
--- a/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
+++ b/compiler/rustc_trait_selection/src/traits/dyn_compatibility.rs
@@ -8,6 +8,7 @@ use std::ops::ControlFlow;
 
 use rustc_errors::FatalError;
 use rustc_hir::attrs::AttributeKind;
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, LangItem, find_attr};
 use rustc_middle::query::Providers;
@@ -833,8 +834,10 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> {
         match ct.kind() {
             ty::ConstKind::Unevaluated(proj) if self.tcx.features().min_generic_const_args() => {
                 match self.allow_self_projections {
-                    AllowSelfProjections::Yes => {
-                        let trait_def_id = self.tcx.parent(proj.def);
+                    AllowSelfProjections::Yes
+                        if let trait_def_id = self.tcx.parent(proj.def)
+                            && self.tcx.def_kind(trait_def_id) == DefKind::Trait =>
+                    {
                         let trait_ref = ty::TraitRef::from_assoc(self.tcx, trait_def_id, proj.args);
 
                         // Only walk contained consts if the parent trait is not a supertrait.
@@ -844,7 +847,7 @@ impl<'tcx> TypeVisitor> for IllegalSelfTypeVisitor<'tcx> {
                             ct.super_visit_with(self)
                         }
                     }
-                    AllowSelfProjections::No => ct.super_visit_with(self),
+                    _ => ct.super_visit_with(self),
                 }
             }
             _ => ct.super_visit_with(self),
diff --git a/tests/ui/const-generics/mgca/type-const-used-in-trait.rs b/tests/ui/const-generics/mgca/type-const-used-in-trait.rs
new file mode 100644
index 000000000000..c98c14775a0f
--- /dev/null
+++ b/tests/ui/const-generics/mgca/type-const-used-in-trait.rs
@@ -0,0 +1,13 @@
+//@ check-pass
+
+#![feature(min_generic_const_args)]
+#![expect(incomplete_features)]
+
+#[type_const]
+const N: usize = 2;
+
+trait CollectArray {
+    fn inner_array(&mut self) -> [A; N];
+}
+
+fn main() {}

From ce03e7b33ab1e26bb9a553dd35f0af06b372b7a9 Mon Sep 17 00:00:00 2001
From: Lukas Bergdoll 
Date: Tue, 27 Jan 2026 18:29:25 +0100
Subject: [PATCH 152/319] Avoid miri error in `slice::sort` under Stacked
 Borrows

See comment in code.

Fixes: https://github.com/rust-lang/rust/pull/131065
---
 library/alloctests/tests/sort/tests.rs          | 8 ++++++++
 library/core/src/slice/sort/stable/quicksort.rs | 9 ++++++---
 2 files changed, 14 insertions(+), 3 deletions(-)

diff --git a/library/alloctests/tests/sort/tests.rs b/library/alloctests/tests/sort/tests.rs
index d321f8df5189..09b76773d6b2 100644
--- a/library/alloctests/tests/sort/tests.rs
+++ b/library/alloctests/tests/sort/tests.rs
@@ -362,6 +362,13 @@ fn sort_vs_sort_by_impl() {
     assert_eq!(input_sort_by, expected);
 }
 
+pub fn box_value_impl() {
+    for len in [3, 9, 35, 56, 132] {
+        test_is_sorted::, S>(len, Box::new, patterns::random);
+        test_is_sorted::, S>(len, Box::new, |len| patterns::random_sorted(len, 80.0));
+    }
+}
+
 gen_sort_test_fns_with_default_patterns!(
     correct_i32,
     |len, pattern_fn| test_is_sorted::(len, |val| val, pattern_fn),
@@ -967,6 +974,7 @@ define_instantiate_sort_tests!(
     [miri_yes, fixed_seed_rand_vec_prefix],
     [miri_yes, int_edge],
     [miri_yes, sort_vs_sort_by],
+    [miri_yes, box_value],
     [miri_yes, correct_i32_random],
     [miri_yes, correct_i32_random_z1],
     [miri_yes, correct_i32_random_d2],
diff --git a/library/core/src/slice/sort/stable/quicksort.rs b/library/core/src/slice/sort/stable/quicksort.rs
index 734a495ce225..acc8a5e838e1 100644
--- a/library/core/src/slice/sort/stable/quicksort.rs
+++ b/library/core/src/slice/sort/stable/quicksort.rs
@@ -1,6 +1,6 @@
 //! This module contains a stable quicksort and partition implementation.
 
-use crate::mem::{ManuallyDrop, MaybeUninit};
+use crate::mem::MaybeUninit;
 use crate::slice::sort::shared::FreezeMarker;
 use crate::slice::sort::shared::pivot::choose_pivot;
 use crate::slice::sort::shared::smallsort::StableSmallSortTypeImpl;
@@ -41,8 +41,11 @@ pub fn quicksort bool>(
         // SAFETY: We only access the temporary copy for Freeze types, otherwise
         // self-modifications via `is_less` would not be observed and this would
         // be unsound. Our temporary copy does not escape this scope.
-        let pivot_copy = unsafe { ManuallyDrop::new(ptr::read(&v[pivot_pos])) };
-        let pivot_ref = (!has_direct_interior_mutability::()).then_some(&*pivot_copy);
+        // We use `MaybeUninit` to avoid re-tag issues. FIXME: use `MaybeDangling`.
+        let pivot_copy = unsafe { ptr::read((&raw const v[pivot_pos]).cast::>()) };
+        let pivot_ref =
+            // SAFETY: We created the value in an init state.
+            (!has_direct_interior_mutability::()).then_some(unsafe { &*pivot_copy.as_ptr() });
 
         // We choose a pivot, and check if this pivot is equal to our left
         // ancestor. If true, we do a partition putting equal elements on the

From b801df5641f4ea73abaac3aec90a26efe267cb95 Mon Sep 17 00:00:00 2001
From: The rustc-josh-sync Cronjob Bot 
Date: Thu, 29 Jan 2026 04:39:36 +0000
Subject: [PATCH 153/319] Prepare for merging from rust-lang/rust

This updates the rust-version file to ba284f468cd2cda48420251efc991758ec13d450.
---
 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 17b678eed936..a1011c4a0acf 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-94a0cd15f5976fa35e5e6784e621c04e9f958e57
+ba284f468cd2cda48420251efc991758ec13d450

From 3e6b27514360856d4b4b616983766809a885258d Mon Sep 17 00:00:00 2001
From: A4-Tacks 
Date: Thu, 29 Jan 2026 13:48:07 +0800
Subject: [PATCH 154/319] fix: complete inferred type in static

Example
---
```rust
struct Foo(T);
static FOO: $0 = Foo(2);
```

**Before this PR**

```text
...
bt u32                      u32
...
```

**After this PR**

```text
...
bt u32                      u32
it Foo
...
```
---
 .../ide-completion/src/context/analysis.rs    |  5 ++++
 .../ide-completion/src/tests/type_pos.rs      | 29 +++++++++++++++++++
 2 files changed, 34 insertions(+)

diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
index 8842d29c8d90..1c8bc656ca25 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/context/analysis.rs
@@ -1250,6 +1250,11 @@ fn classify_name_ref<'db>(
                     let original = ast::Const::cast(name.syntax().parent()?)?;
                     TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
                 },
+                ast::Static(it) => {
+                    let name = find_opt_node_in_file(original_file, it.name())?;
+                    let original = ast::Static::cast(name.syntax().parent()?)?;
+                    TypeLocation::TypeAscription(TypeAscriptionTarget::Const(original.body()))
+                },
                 ast::RetType(it) => {
                     it.thin_arrow_token()?;
                     let parent = match ast::Fn::cast(parent.parent()?) {
diff --git a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
index 3bbba18c2b9f..7c6b7370aafd 100644
--- a/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
+++ b/src/tools/rust-analyzer/crates/ide-completion/src/tests/type_pos.rs
@@ -183,6 +183,35 @@ const FOO: $0 = Foo(2);
     );
 }
 
+#[test]
+fn inferred_type_static() {
+    check_with_base_items(
+        r#"
+struct Foo(T);
+static FOO: $0 = Foo(2);
+"#,
+        expect![[r#"
+            en Enum                    Enum
+            ma makro!(…) macro_rules! makro
+            md module
+            st Foo<…>        Foo<{unknown}>
+            st Record                Record
+            st Tuple                  Tuple
+            st Unit                    Unit
+            tt Trait
+            un Union                  Union
+            bt u32                      u32
+            it Foo
+            kw crate::
+            kw dyn
+            kw fn
+            kw for
+            kw impl
+            kw self::
+        "#]],
+    );
+}
+
 #[test]
 fn inferred_type_closure_param() {
     check_with_base_items(

From 2b0ce429ff81543e1a312a5a966302faf539f6b1 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 30 Jan 2026 00:09:31 +0530
Subject: [PATCH 155/319] remove codec and framing

---
 .../crates/proc-macro-api/src/transport/codec.rs  | 15 ---------------
 .../proc-macro-api/src/transport/framing.rs       | 14 --------------
 2 files changed, 29 deletions(-)
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs
deleted file mode 100644
index c9afad260a56..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-//! Protocol codec
-
-use std::io;
-
-use serde::de::DeserializeOwned;
-
-use crate::transport::framing::Framing;
-
-pub mod json;
-pub mod postcard;
-
-pub trait Codec: Framing {
-    fn encode(msg: &T) -> io::Result;
-    fn decode(buf: &mut Self::Buf) -> io::Result;
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs
deleted file mode 100644
index 56c3b68e8cd2..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/framing.rs
+++ /dev/null
@@ -1,14 +0,0 @@
-//! Protocol framing
-
-use std::io::{self, BufRead, Write};
-
-pub trait Framing {
-    type Buf: Default + Send + Sync;
-
-    fn read<'a, R: BufRead + ?Sized>(
-        inp: &mut R,
-        buf: &'a mut Self::Buf,
-    ) -> io::Result>;
-
-    fn write(out: &mut W, buf: &Self::Buf) -> io::Result<()>;
-}

From 06c0fde840a2d369eb917b6410111a9c32d046e9 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 30 Jan 2026 00:10:09 +0530
Subject: [PATCH 156/319] move json and postcard as top level api's

---
 .../src/transport/codec/json.rs               | 58 -------------------
 .../src/transport/codec/postcard.rs           | 40 -------------
 .../proc-macro-api/src/transport/json.rs      | 48 +++++++++++++++
 .../proc-macro-api/src/transport/postcard.rs  | 30 ++++++++++
 4 files changed, 78 insertions(+), 98 deletions(-)
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs
 delete mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs
 create mode 100644 src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs
deleted file mode 100644
index 96db802e0bfd..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/json.rs
+++ /dev/null
@@ -1,58 +0,0 @@
-//! Protocol functions for json.
-use std::io::{self, BufRead, Write};
-
-use serde::{Serialize, de::DeserializeOwned};
-
-use crate::{Codec, transport::framing::Framing};
-
-pub struct JsonProtocol;
-
-impl Framing for JsonProtocol {
-    type Buf = String;
-
-    fn read<'a, R: BufRead + ?Sized>(
-        inp: &mut R,
-        buf: &'a mut String,
-    ) -> io::Result> {
-        loop {
-            buf.clear();
-
-            inp.read_line(buf)?;
-            buf.pop(); // Remove trailing '\n'
-
-            if buf.is_empty() {
-                return Ok(None);
-            }
-
-            // Some ill behaved macro try to use stdout for debugging
-            // We ignore it here
-            if !buf.starts_with('{') {
-                tracing::error!("proc-macro tried to print : {}", buf);
-                continue;
-            }
-
-            return Ok(Some(buf));
-        }
-    }
-
-    fn write(out: &mut W, buf: &String) -> io::Result<()> {
-        tracing::debug!("> {}", buf);
-        out.write_all(buf.as_bytes())?;
-        out.write_all(b"\n")?;
-        out.flush()
-    }
-}
-
-impl Codec for JsonProtocol {
-    fn encode(msg: &T) -> io::Result {
-        Ok(serde_json::to_string(msg)?)
-    }
-
-    fn decode(buf: &mut String) -> io::Result {
-        let mut deserializer = serde_json::Deserializer::from_str(buf);
-        // Note that some proc-macro generate very deep syntax tree
-        // We have to disable the current limit of serde here
-        deserializer.disable_recursion_limit();
-        Ok(T::deserialize(&mut deserializer)?)
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs
deleted file mode 100644
index 6f5319e75b37..000000000000
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/codec/postcard.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-//! Postcard encode and decode implementations.
-
-use std::io::{self, BufRead, Write};
-
-use serde::{Serialize, de::DeserializeOwned};
-
-use crate::{Codec, transport::framing::Framing};
-
-pub struct PostcardProtocol;
-
-impl Framing for PostcardProtocol {
-    type Buf = Vec;
-
-    fn read<'a, R: BufRead + ?Sized>(
-        inp: &mut R,
-        buf: &'a mut Vec,
-    ) -> io::Result>> {
-        buf.clear();
-        let n = inp.read_until(0, buf)?;
-        if n == 0 {
-            return Ok(None);
-        }
-        Ok(Some(buf))
-    }
-
-    fn write(out: &mut W, buf: &Vec) -> io::Result<()> {
-        out.write_all(buf)?;
-        out.flush()
-    }
-}
-
-impl Codec for PostcardProtocol {
-    fn encode(msg: &T) -> io::Result> {
-        postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
-    }
-
-    fn decode(buf: &mut Self::Buf) -> io::Result {
-        postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
-    }
-}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs
new file mode 100644
index 000000000000..da79dc5309b5
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs
@@ -0,0 +1,48 @@
+//! Protocol functions for json.
+use std::io::{self, BufRead, Write};
+
+use serde::{Serialize, de::DeserializeOwned};
+
+pub fn read<'a, R: BufRead + ?Sized>(
+    inp: &mut R,
+    buf: &'a mut String,
+) -> io::Result> {
+    loop {
+        buf.clear();
+
+        inp.read_line(buf)?;
+        buf.pop(); // Remove trailing '\n'
+
+        if buf.is_empty() {
+            return Ok(None);
+        }
+
+        // Some ill behaved macro try to use stdout for debugging
+        // We ignore it here
+        if !buf.starts_with('{') {
+            tracing::error!("proc-macro tried to print : {}", buf);
+            continue;
+        }
+
+        return Ok(Some(buf));
+    }
+}
+
+pub fn write(out: &mut W, buf: &String) -> io::Result<()> {
+    tracing::debug!("> {}", buf);
+    out.write_all(buf.as_bytes())?;
+    out.write_all(b"\n")?;
+    out.flush()
+}
+
+pub fn encode(msg: &T) -> io::Result {
+    Ok(serde_json::to_string(msg)?)
+}
+
+pub fn decode(buf: &mut str) -> io::Result {
+    let mut deserializer = serde_json::Deserializer::from_str(buf);
+    // Note that some proc-macro generate very deep syntax tree
+    // We have to disable the current limit of serde here
+    deserializer.disable_recursion_limit();
+    Ok(T::deserialize(&mut deserializer)?)
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs
new file mode 100644
index 000000000000..ddd5f405d5dc
--- /dev/null
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs
@@ -0,0 +1,30 @@
+//! Postcard encode and decode implementations.
+
+use std::io::{self, BufRead, Write};
+
+use serde::{Serialize, de::DeserializeOwned};
+
+pub fn read<'a, R: BufRead + ?Sized>(
+    inp: &mut R,
+    buf: &'a mut Vec,
+) -> io::Result>> {
+    buf.clear();
+    let n = inp.read_until(0, buf)?;
+    if n == 0 {
+        return Ok(None);
+    }
+    Ok(Some(buf))
+}
+
+pub fn write(out: &mut W, buf: &[u8]) -> io::Result<()> {
+    out.write_all(buf)?;
+    out.flush()
+}
+
+pub fn encode(msg: &T) -> io::Result> {
+    postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
+}
+
+pub fn decode(buf: &mut [u8]) -> io::Result {
+    postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))
+}

From dfb22b94069a58ac2c98fc1af0d03b7cb64473e2 Mon Sep 17 00:00:00 2001
From: bit-aloo 
Date: Fri, 30 Jan 2026 00:11:52 +0530
Subject: [PATCH 157/319] adapt proc-macro-api to remove codec abstraction

---
 .../src/bidirectional_protocol.rs             | 23 +++++------
 .../src/bidirectional_protocol/msg.rs         | 21 +++++++++-
 .../proc-macro-api/src/legacy_protocol.rs     | 12 +++---
 .../proc-macro-api/src/legacy_protocol/msg.rs | 38 ++++++++++++++-----
 .../crates/proc-macro-api/src/lib.rs          |  1 -
 .../crates/proc-macro-api/src/process.rs      | 21 +++++-----
 .../crates/proc-macro-api/src/transport.rs    |  4 +-
 7 files changed, 77 insertions(+), 43 deletions(-)

diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
index a13bff7d7d02..8311df23d718 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs
@@ -9,7 +9,7 @@ use paths::AbsPath;
 use span::Span;
 
 use crate::{
-    Codec, ProcMacro, ProcMacroKind, ServerError,
+    ProcMacro, ProcMacroKind, ServerError,
     bidirectional_protocol::msg::{
         BidirectionalMessage, ExpandMacro, ExpandMacroData, ExpnGlobals, Request, Response,
         SubRequest, SubResponse,
@@ -22,25 +22,25 @@ use crate::{
         },
     },
     process::ProcMacroServerProcess,
-    transport::codec::postcard::PostcardProtocol,
+    transport::postcard,
 };
 
 pub mod msg;
 
 pub type SubCallback<'a> = &'a dyn Fn(SubRequest) -> Result;
 
-pub fn run_conversation(
+pub fn run_conversation(
     writer: &mut dyn Write,
     reader: &mut dyn BufRead,
-    buf: &mut C::Buf,
+    buf: &mut Vec,
     msg: BidirectionalMessage,
     callback: SubCallback<'_>,
 ) -> Result {
-    let encoded = C::encode(&msg).map_err(wrap_encode)?;
-    C::write(writer, &encoded).map_err(wrap_io("failed to write initial request"))?;
+    let encoded = postcard::encode(&msg).map_err(wrap_encode)?;
+    postcard::write(writer, &encoded).map_err(wrap_io("failed to write initial request"))?;
 
     loop {
-        let maybe_buf = C::read(reader, buf).map_err(wrap_io("failed to read message"))?;
+        let maybe_buf = postcard::read(reader, buf).map_err(wrap_io("failed to read message"))?;
         let Some(b) = maybe_buf else {
             return Err(ServerError {
                 message: "proc-macro server closed the stream".into(),
@@ -48,7 +48,7 @@ pub fn run_conversation(
             });
         };
 
-        let msg: BidirectionalMessage = C::decode(b).map_err(wrap_decode)?;
+        let msg: BidirectionalMessage = postcard::decode(b).map_err(wrap_decode)?;
 
         match msg {
             BidirectionalMessage::Response(response) => {
@@ -57,8 +57,9 @@ pub fn run_conversation(
             BidirectionalMessage::SubRequest(sr) => {
                 let resp = callback(sr)?;
                 let reply = BidirectionalMessage::SubResponse(resp);
-                let encoded = C::encode(&reply).map_err(wrap_encode)?;
-                C::write(writer, &encoded).map_err(wrap_io("failed to write sub-response"))?;
+                let encoded = postcard::encode(&reply).map_err(wrap_encode)?;
+                postcard::write(writer, &encoded)
+                    .map_err(wrap_io("failed to write sub-response"))?;
             }
             _ => {
                 return Err(ServerError {
@@ -207,7 +208,7 @@ fn run_request(
     if let Some(err) = srv.exited() {
         return Err(err.clone());
     }
-    srv.run_bidirectional::(msg, callback)
+    srv.run_bidirectional(msg, callback)
 }
 
 pub fn reject_subrequests(req: SubRequest) -> Result {
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
index d030498e59c4..1df0c68379a5 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs
@@ -1,6 +1,9 @@
 //! Bidirectional protocol messages
 
-use std::ops::Range;
+use std::{
+    io::{self, BufRead, Write},
+    ops::Range,
+};
 
 use paths::Utf8PathBuf;
 use serde::{Deserialize, Serialize};
@@ -8,6 +11,7 @@ use serde::{Deserialize, Serialize};
 use crate::{
     ProcMacroKind,
     legacy_protocol::msg::{FlatTree, Message, PanicMessage, ServerConfig},
+    transport::postcard,
 };
 
 #[derive(Debug, Serialize, Deserialize)]
@@ -97,4 +101,17 @@ pub struct ExpnGlobals {
     pub mixed_site: usize,
 }
 
-impl Message for BidirectionalMessage {}
+impl Message for BidirectionalMessage {
+    type Buf = Vec;
+
+    fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> {
+        Ok(match postcard::read(inp, buf)? {
+            None => None,
+            Some(buf) => Some(postcard::decode(buf)?),
+        })
+    }
+    fn write(self, out: &mut dyn Write) -> io::Result<()> {
+        let value = postcard::encode(&self)?;
+        postcard::write(out, &value)
+    }
+}
diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
index eedf66d46086..ee1795d39c2e 100644
--- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
+++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol.rs
@@ -18,8 +18,6 @@ use crate::{
         flat::serialize_span_data_index_map,
     },
     process::ProcMacroServerProcess,
-    transport::codec::Codec,
-    transport::codec::json::JsonProtocol,
     version,
 };
 
@@ -149,21 +147,21 @@ fn send_task(srv: &ProcMacroServerProcess, req: Request) -> Result(send_request::, req)
+    srv.send_task_legacy::<_, _>(send_request, req)
 }
 
 /// Sends a request to the server and reads the response.
-fn send_request(
+fn send_request(
     mut writer: &mut dyn Write,
     mut reader: &mut dyn BufRead,
     req: Request,
-    buf: &mut P::Buf,
+    buf: &mut String,
 ) -> Result, ServerError> {
-    req.write::

(&mut writer).map_err(|err| ServerError { + req.write(&mut writer).map_err(|err| ServerError { message: "failed to write request".into(), io: Some(Arc::new(err)), })?; - let res = Response::read::

(&mut reader, buf).map_err(|err| ServerError { + let res = Response::read(&mut reader, buf).map_err(|err| ServerError { message: "failed to read response".into(), io: Some(Arc::new(err)), })?; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs index 1b6590693354..bb0dde472860 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/legacy_protocol/msg.rs @@ -8,7 +8,7 @@ use paths::Utf8PathBuf; use serde::de::DeserializeOwned; use serde_derive::{Deserialize, Serialize}; -use crate::{Codec, ProcMacroKind}; +use crate::{ProcMacroKind, transport::json}; /// Represents requests sent from the client to the proc-macro-srv. #[derive(Debug, Serialize, Deserialize)] @@ -155,20 +155,40 @@ impl ExpnGlobals { } pub trait Message: serde::Serialize + DeserializeOwned { - fn read(inp: &mut dyn BufRead, buf: &mut C::Buf) -> io::Result> { - Ok(match C::read(inp, buf)? { + type Buf; + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result>; + fn write(self, out: &mut dyn Write) -> io::Result<()>; +} + +impl Message for Request { + type Buf = String; + + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> { + Ok(match json::read(inp, buf)? { None => None, - Some(buf) => Some(C::decode(buf)?), + Some(buf) => Some(json::decode(buf)?), }) } - fn write(self, out: &mut dyn Write) -> io::Result<()> { - let value = C::encode(&self)?; - C::write(out, &value) + fn write(self, out: &mut dyn Write) -> io::Result<()> { + let value = json::encode(&self)?; + json::write(out, &value) } } -impl Message for Request {} -impl Message for Response {} +impl Message for Response { + type Buf = String; + + fn read(inp: &mut dyn BufRead, buf: &mut Self::Buf) -> io::Result> { + Ok(match json::read(inp, buf)? { + None => None, + Some(buf) => Some(json::decode(buf)?), + }) + } + fn write(self, out: &mut dyn Write) -> io::Result<()> { + let value = json::encode(&self)?; + json::write(out, &value) + } +} #[cfg(test)] mod tests { 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 3acd0b292a31..e4b121b033d3 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 @@ -27,7 +27,6 @@ use semver::Version; use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span}; use std::{fmt, io, sync::Arc, time::SystemTime}; -pub use crate::transport::codec::Codec; use crate::{ bidirectional_protocol::SubCallback, pool::ProcMacroServerPool, process::ProcMacroServerProcess, }; 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 2f5bef69abd5..9f80880965b8 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 @@ -17,7 +17,7 @@ use span::Span; use stdx::JodChild; use crate::{ - Codec, ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, + ProcMacro, ProcMacroKind, ProtocolFormat, ServerError, bidirectional_protocol::{self, SubCallback, msg::BidirectionalMessage, reject_subrequests}, legacy_protocol::{self, SpanMode}, version, @@ -305,17 +305,17 @@ impl ProcMacroServerProcess { result } - pub(crate) fn send_task( + pub(crate) fn send_task_legacy( &self, send: impl FnOnce( &mut dyn Write, &mut dyn BufRead, Request, - &mut C::Buf, + &mut String, ) -> Result, ServerError>, req: Request, ) -> Result { - self.with_locked_io::(|writer, reader, buf| { + self.with_locked_io(String::new(), |writer, reader, buf| { send(writer, reader, req, buf).and_then(|res| { res.ok_or_else(|| { let message = "proc-macro server did not respond with data".to_owned(); @@ -331,13 +331,12 @@ impl ProcMacroServerProcess { }) } - pub(crate) fn with_locked_io( + fn with_locked_io( &self, - f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut C::Buf) -> Result, + mut buf: B, + f: impl FnOnce(&mut dyn Write, &mut dyn BufRead, &mut B) -> Result, ) -> Result { let state = &mut *self.state.lock().unwrap(); - let mut buf = C::Buf::default(); - f(&mut state.stdin, &mut state.stdout, &mut buf).map_err(|e| { if e.io.as_ref().map(|it| it.kind()) == Some(io::ErrorKind::BrokenPipe) { match state.process.exit_err() { @@ -352,13 +351,13 @@ impl ProcMacroServerProcess { }) } - pub(crate) fn run_bidirectional( + pub(crate) fn run_bidirectional( &self, initial: BidirectionalMessage, callback: SubCallback<'_>, ) -> Result { - self.with_locked_io::(|writer, reader, buf| { - bidirectional_protocol::run_conversation::(writer, reader, buf, initial, callback) + self.with_locked_io(Vec::new(), |writer, reader, buf| { + bidirectional_protocol::run_conversation(writer, reader, buf, initial, callback) }) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs index b7a1d8f7322c..e31d6a589d94 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs @@ -1,3 +1,3 @@ //! Contains construct for transport of messages. -pub mod codec; -pub mod framing; +pub mod json; +pub mod postcard; From 0bab96e98f7799b97c16f4738b2b68d6e5affb11 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 30 Jan 2026 00:12:14 +0530 Subject: [PATCH 158/319] adapt proc-macro-srv-cli and test --- .../proc-macro-srv-cli/src/main_loop.rs | 72 +++++++++---------- .../proc-macro-srv-cli/tests/common/utils.rs | 11 ++- 2 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 70e1e091c197..758629fd1fd6 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -1,9 +1,6 @@ //! The main loop of the proc-macro server. use proc_macro_api::{ - Codec, ProtocolFormat, - bidirectional_protocol::msg as bidirectional, - legacy_protocol::msg as legacy, - transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, + ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, }; use std::{ @@ -40,14 +37,12 @@ pub fn run( format: ProtocolFormat, ) -> io::Result<()> { match format { - ProtocolFormat::JsonLegacy => run_old::(stdin, stdout), - ProtocolFormat::BidirectionalPostcardPrototype => { - run_new::(stdin, stdout) - } + ProtocolFormat::JsonLegacy => run_old(stdin, stdout), + ProtocolFormat::BidirectionalPostcardPrototype => run_new(stdin, stdout), } } -fn run_new( +fn run_new( stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), ) -> io::Result<()> { @@ -61,7 +56,7 @@ fn run_new( } } - let mut buf = C::Buf::default(); + let mut buf = Vec::default(); let env_snapshot = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env_snapshot); @@ -69,7 +64,7 @@ fn run_new( let mut span_mode = legacy::SpanMode::Id; 'outer: loop { - let req_opt = bidirectional::BidirectionalMessage::read::(stdin, &mut buf)?; + let req_opt = bidirectional::BidirectionalMessage::read(stdin, &mut buf)?; let Some(req) = req_opt else { break 'outer; }; @@ -84,11 +79,11 @@ fn run_new( .collect() }); - send_response::(stdout, bidirectional::Response::ListMacros(res))?; + send_response(stdout, bidirectional::Response::ListMacros(res))?; } bidirectional::Request::ApiVersionCheck {} => { - send_response::( + send_response( stdout, bidirectional::Response::ApiVersionCheck(CURRENT_API_VERSION), )?; @@ -96,10 +91,10 @@ fn run_new( bidirectional::Request::SetConfig(config) => { span_mode = config.span_mode; - send_response::(stdout, bidirectional::Response::SetConfig(config))?; + send_response(stdout, bidirectional::Response::SetConfig(config))?; } bidirectional::Request::ExpandMacro(task) => { - handle_expand::(&srv, stdin, stdout, &mut buf, span_mode, *task)?; + handle_expand(&srv, stdin, stdout, &mut buf, span_mode, *task)?; } }, _ => continue, @@ -109,21 +104,21 @@ fn run_new( Ok(()) } -fn handle_expand( +fn handle_expand( srv: &proc_macro_srv::ProcMacroSrv<'_>, stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), - buf: &mut C::Buf, + buf: &mut Vec, span_mode: legacy::SpanMode, task: bidirectional::ExpandMacro, ) -> io::Result<()> { match span_mode { - legacy::SpanMode::Id => handle_expand_id::(srv, stdout, task), - legacy::SpanMode::RustAnalyzer => handle_expand_ra::(srv, stdin, stdout, buf, task), + legacy::SpanMode::Id => handle_expand_id(srv, stdout, task), + legacy::SpanMode::RustAnalyzer => handle_expand_ra(srv, stdin, stdout, buf, task), } } -fn handle_expand_id( +fn handle_expand_id( srv: &proc_macro_srv::ProcMacroSrv<'_>, stdout: &mut dyn Write, task: bidirectional::ExpandMacro, @@ -164,34 +159,34 @@ fn handle_expand_id( }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(stdout, bidirectional::Response::ExpandMacro(res)) + send_response(stdout, bidirectional::Response::ExpandMacro(res)) } -struct ProcMacroClientHandle<'a, C: Codec> { +struct ProcMacroClientHandle<'a> { stdin: &'a mut (dyn BufRead + Send + Sync), stdout: &'a mut (dyn Write + Send + Sync), - buf: &'a mut C::Buf, + buf: &'a mut Vec, } -impl<'a, C: Codec> ProcMacroClientHandle<'a, C> { +impl<'a> ProcMacroClientHandle<'a> { fn roundtrip( &mut self, req: bidirectional::SubRequest, ) -> Option { let msg = bidirectional::BidirectionalMessage::SubRequest(req); - if msg.write::(&mut *self.stdout).is_err() { + if msg.write(&mut *self.stdout).is_err() { return None; } - match bidirectional::BidirectionalMessage::read::(&mut *self.stdin, self.buf) { + match bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) { Ok(Some(msg)) => Some(msg), _ => None, } } } -impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Some(bidirectional::BidirectionalMessage::SubResponse( @@ -260,11 +255,11 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl } } -fn handle_expand_ra( +fn handle_expand_ra( srv: &proc_macro_srv::ProcMacroSrv<'_>, stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), - buf: &mut C::Buf, + buf: &mut Vec, task: bidirectional::ExpandMacro, ) -> io::Result<()> { let bidirectional::ExpandMacro { @@ -309,7 +304,7 @@ fn handle_expand_ra( def_site, call_site, mixed_site, - Some(&mut ProcMacroClientHandle:: { stdin, stdout, buf }), + Some(&mut ProcMacroClientHandle { stdin, stdout, buf }), ) .map(|it| { ( @@ -325,10 +320,10 @@ fn handle_expand_ra( .map(|(tree, span_data_table)| bidirectional::ExpandMacroExtended { tree, span_data_table }) .map_err(|e| legacy::PanicMessage(e.into_string().unwrap_or_default())); - send_response::(stdout, bidirectional::Response::ExpandMacroExtended(res)) + send_response(stdout, bidirectional::Response::ExpandMacroExtended(res)) } -fn run_old( +fn run_old( stdin: &mut (dyn BufRead + Send + Sync), stdout: &mut (dyn Write + Send + Sync), ) -> io::Result<()> { @@ -342,9 +337,9 @@ fn run_old( } } - let mut buf = C::Buf::default(); - let mut read_request = || legacy::Request::read::(stdin, &mut buf); - let mut write_response = |msg: legacy::Response| msg.write::(stdout); + let mut buf = String::default(); + let mut read_request = || legacy::Request::read(stdin, &mut buf); + let mut write_response = |msg: legacy::Response| msg.write(stdout); let env = EnvSnapshot::default(); let srv = proc_macro_srv::ProcMacroSrv::new(&env); @@ -473,10 +468,7 @@ fn run_old( Ok(()) } -fn send_response( - stdout: &mut dyn Write, - resp: bidirectional::Response, -) -> io::Result<()> { +fn send_response(stdout: &mut dyn Write, resp: bidirectional::Response) -> io::Result<()> { let resp = bidirectional::BidirectionalMessage::Response(resp); - resp.write::(stdout) + resp.write(stdout) } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs index 85c394734b33..3049e9800405 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/common/utils.rs @@ -12,7 +12,6 @@ use proc_macro_api::{ BidirectionalMessage, Request as BiRequest, Response as BiResponse, SubRequest, SubResponse, }, legacy_protocol::msg::{FlatTree, Message, Request, Response, SpanDataIndexMap}, - transport::codec::{json::JsonProtocol, postcard::PostcardProtocol}, }; use span::{Edition, EditionedFileId, FileId, Span, SpanAnchor, SyntaxContext, TextRange}; use tt::{Delimiter, DelimiterKind, TopSubtreeBuilder}; @@ -210,12 +209,12 @@ impl TestProtocol for JsonLegacy { type Response = Response; fn request(&self, writer: &mut dyn Write, req: Request) { - req.write::(writer).expect("failed to write request"); + req.write(writer).expect("failed to write request"); } fn receive(&self, reader: &mut dyn BufRead, _writer: &mut dyn Write) -> Response { let mut buf = String::new(); - Response::read::(reader, &mut buf) + Response::read(reader, &mut buf) .expect("failed to read response") .expect("no response received") } @@ -238,14 +237,14 @@ where fn request(&self, writer: &mut dyn Write, req: BiRequest) { let msg = BidirectionalMessage::Request(req); - msg.write::(writer).expect("failed to write request"); + msg.write(writer).expect("failed to write request"); } fn receive(&self, reader: &mut dyn BufRead, writer: &mut dyn Write) -> BiResponse { let mut buf = Vec::new(); loop { - let msg = BidirectionalMessage::read::(reader, &mut buf) + let msg = BidirectionalMessage::read(reader, &mut buf) .expect("failed to read message") .expect("no message received"); @@ -254,7 +253,7 @@ where BidirectionalMessage::SubRequest(sr) => { let reply = (self.callback)(sr).expect("subrequest callback failed"); let msg = BidirectionalMessage::SubResponse(reply); - msg.write::(writer).expect("failed to write subresponse"); + msg.write(writer).expect("failed to write subresponse"); } other => panic!("unexpected message: {other:?}"), } From be833ca74c1ed8dc1504dc5d3ae23896189341b0 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 30 Jan 2026 07:30:41 +0530 Subject: [PATCH 159/319] remove postcard dep from proc-macro-srv-cli --- src/tools/rust-analyzer/Cargo.lock | 1 - src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml | 1 - 2 files changed, 2 deletions(-) diff --git a/src/tools/rust-analyzer/Cargo.lock b/src/tools/rust-analyzer/Cargo.lock index a2a18cf8eeea..2cf3e37a43bc 100644 --- a/src/tools/rust-analyzer/Cargo.lock +++ b/src/tools/rust-analyzer/Cargo.lock @@ -1882,7 +1882,6 @@ dependencies = [ "expect-test", "intern", "paths", - "postcard", "proc-macro-api", "proc-macro-srv", "proc-macro-test", diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml index a25e3b64ad42..f586fe7644d7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/Cargo.toml @@ -16,7 +16,6 @@ doctest = false [dependencies] proc-macro-srv.workspace = true proc-macro-api.workspace = true -postcard.workspace = true clap = {version = "4.5.42", default-features = false, features = ["std"]} [dev-dependencies] From 617ac8c0d444bf6aa7f4574b7018b6ea2d5480a1 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Fri, 30 Jan 2026 07:47:40 +0530 Subject: [PATCH 160/319] correct visibility of transport layer --- .../rust-analyzer/crates/proc-macro-api/src/transport.rs | 4 ++-- .../crates/proc-macro-api/src/transport/json.rs | 8 ++++---- .../crates/proc-macro-api/src/transport/postcard.rs | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs index e31d6a589d94..f383edb0cbbb 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport.rs @@ -1,3 +1,3 @@ //! Contains construct for transport of messages. -pub mod json; -pub mod postcard; +pub(crate) mod json; +pub(crate) mod postcard; diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs index da79dc5309b5..f433bb7de033 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/json.rs @@ -3,7 +3,7 @@ use std::io::{self, BufRead, Write}; use serde::{Serialize, de::DeserializeOwned}; -pub fn read<'a, R: BufRead + ?Sized>( +pub(crate) fn read<'a, R: BufRead + ?Sized>( inp: &mut R, buf: &'a mut String, ) -> io::Result> { @@ -28,18 +28,18 @@ pub fn read<'a, R: BufRead + ?Sized>( } } -pub fn write(out: &mut W, buf: &String) -> io::Result<()> { +pub(crate) fn write(out: &mut W, buf: &String) -> io::Result<()> { tracing::debug!("> {}", buf); out.write_all(buf.as_bytes())?; out.write_all(b"\n")?; out.flush() } -pub fn encode(msg: &T) -> io::Result { +pub(crate) fn encode(msg: &T) -> io::Result { Ok(serde_json::to_string(msg)?) } -pub fn decode(buf: &mut str) -> io::Result { +pub(crate) fn decode(buf: &mut str) -> io::Result { let mut deserializer = serde_json::Deserializer::from_str(buf); // Note that some proc-macro generate very deep syntax tree // We have to disable the current limit of serde here diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs index ddd5f405d5dc..75aa90e4c480 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/transport/postcard.rs @@ -4,7 +4,7 @@ use std::io::{self, BufRead, Write}; use serde::{Serialize, de::DeserializeOwned}; -pub fn read<'a, R: BufRead + ?Sized>( +pub(crate) fn read<'a, R: BufRead + ?Sized>( inp: &mut R, buf: &'a mut Vec, ) -> io::Result>> { @@ -16,15 +16,15 @@ pub fn read<'a, R: BufRead + ?Sized>( Ok(Some(buf)) } -pub fn write(out: &mut W, buf: &[u8]) -> io::Result<()> { +pub(crate) fn write(out: &mut W, buf: &[u8]) -> io::Result<()> { out.write_all(buf)?; out.flush() } -pub fn encode(msg: &T) -> io::Result> { +pub(crate) fn encode(msg: &T) -> io::Result> { postcard::to_allocvec_cobs(msg).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } -pub fn decode(buf: &mut [u8]) -> io::Result { +pub(crate) fn decode(buf: &mut [u8]) -> io::Result { postcard::from_bytes_cobs(buf).map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } From 91feb76d941592145238eda59e73be25fd191af3 Mon Sep 17 00:00:00 2001 From: yukang Date: Fri, 30 Jan 2026 08:56:56 +0000 Subject: [PATCH 161/319] Skip unused_allocation lint when method takes &Box --- compiler/rustc_lint/src/unused.rs | 8 ++- .../unused-allocation-box-ref-issue-151846.rs | 54 +++++++++++++++++++ 2 files changed, 61 insertions(+), 1 deletion(-) create mode 100644 tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 5516298c5e85..8b630d0d7385 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1782,7 +1782,7 @@ declare_lint! { declare_lint_pass!(UnusedAllocation => [UNUSED_ALLOCATION]); impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { - fn check_expr(&mut self, cx: &LateContext<'_>, e: &hir::Expr<'_>) { + fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &hir::Expr<'_>) { match e.kind { hir::ExprKind::Call(path_expr, [_]) if let hir::ExprKind::Path(qpath) = &path_expr.kind @@ -1793,6 +1793,12 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAllocation { for adj in cx.typeck_results().expr_adjustments(e) { if let adjustment::Adjust::Borrow(adjustment::AutoBorrow::Ref(m)) = adj.kind { + if let ty::Ref(_, inner_ty, _) = adj.target.kind() + && inner_ty.is_box() + { + // If the target type is `&Box` or `&mut Box`, the allocation is necessary + continue; + } match m { adjustment::AutoBorrowMutability::Not => { cx.emit_span_lint(UNUSED_ALLOCATION, e.span, UnusedAllocationDiag); diff --git a/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs b/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs new file mode 100644 index 000000000000..57eefc3891fa --- /dev/null +++ b/tests/ui/lint/unused/unused-allocation-box-ref-issue-151846.rs @@ -0,0 +1,54 @@ +//@ check-pass +// Test for issue #151846: unused_allocation warning should ignore +// allocations to pass Box to things taking self: &Box + +#![deny(unused_allocation)] + +struct MyStruct; + +trait TraitTakesBoxRef { + fn trait_takes_box_ref(&self); +} + +impl TraitTakesBoxRef for Box { + fn trait_takes_box_ref(&self) {} +} + +impl MyStruct { + fn inherent_takes_box_ref(self: &Box) {} +} + +fn takes_box_ref(_: &Box) {} + +trait TraitTakesBoxVal { + fn trait_takes_box_val(self); +} + +impl TraitTakesBoxVal for Box { + fn trait_takes_box_val(self) {} +} + +impl MyStruct { + fn inherent_takes_box_val(self: Box) {} +} + +fn takes_box_val(_: Box) {} + +pub fn foo() { + // These should NOT warn - the allocation is necessary because + // the method takes &Box + Box::new(MyStruct).trait_takes_box_ref(); + Box::new(MyStruct).inherent_takes_box_ref(); + takes_box_ref(&Box::new(MyStruct)); + + // These already don't warn - the allocation is necessary + Box::new(MyStruct).trait_takes_box_val(); + Box::new(MyStruct).inherent_takes_box_val(); + takes_box_val(Box::new(MyStruct)); + + // Fully-qualified syntax also does not warn: + as TraitTakesBoxRef>::trait_takes_box_ref(&Box::new(MyStruct)); + MyStruct::inherent_takes_box_ref(&Box::new(MyStruct)); +} + +fn main() {} From 09117c9b706f5b8a061fe52d5d6029b50de398a1 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 30 Jan 2026 17:17:46 +1100 Subject: [PATCH 162/319] Use the query vtable in `query_feed` plumbing --- compiler/rustc_middle/src/query/inner.rs | 50 +++++++++++-------- compiler/rustc_middle/src/query/mod.rs | 1 - compiler/rustc_middle/src/query/plumbing.rs | 22 ++------ .../rustc_query_system/src/dep_graph/graph.rs | 5 +- 4 files changed, 37 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_middle/src/query/inner.rs b/compiler/rustc_middle/src/query/inner.rs index e41d23f82f12..b25ae06479b9 100644 --- a/compiler/rustc_middle/src/query/inner.rs +++ b/compiler/rustc_middle/src/query/inner.rs @@ -1,17 +1,14 @@ //! Helper functions that serve as the immediate implementation of //! `tcx.$query(..)` and its variations. -use std::fmt::Debug; - -use rustc_data_structures::fingerprint::Fingerprint; use rustc_query_system::dep_graph::{DepKind, DepNodeParams}; -use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryCache, QueryMode, try_get_cached}; use rustc_span::{DUMMY_SP, ErrorGuaranteed, Span}; use crate::dep_graph; use crate::query::IntoQueryParam; use crate::query::erase::{self, Erasable, Erased}; +use crate::query::plumbing::QueryVTable; use crate::ty::TyCtxt; /// Shared implementation of `tcx.$query(..)` and `tcx.at(span).$query(..)` @@ -84,35 +81,38 @@ where } /// Common implementation of query feeding, used by `define_feedable!`. -pub(crate) fn query_feed<'tcx, Cache, Value>( +pub(crate) fn query_feed<'tcx, Cache>( tcx: TyCtxt<'tcx>, dep_kind: DepKind, - hasher: Option, &Value) -> Fingerprint>, + query_vtable: &QueryVTable<'tcx, Cache>, cache: &Cache, key: Cache::Key, - erased: Erased, + value: Cache::Value, ) where - Cache: QueryCache>, + Cache: QueryCache, Cache::Key: DepNodeParams>, - Value: Erasable + Debug, { - let value = erase::restore_val::(erased); + let format_value = query_vtable.format_value; + // Check whether the in-memory cache already has a value for this key. match try_get_cached(tcx, cache, &key) { Some(old) => { - let old = erase::restore_val::(old); - if let Some(hasher) = hasher { - let (value_hash, old_hash): (Fingerprint, Fingerprint) = tcx - .with_stable_hashing_context(|mut hcx| { - (hasher(&mut hcx, &value), hasher(&mut hcx, &old)) - }); + // The query already has a cached value for this key. + // That's OK if both values are the same, i.e. they have the same hash, + // so now we check their hashes. + if let Some(hasher_fn) = query_vtable.hash_result { + let (old_hash, value_hash) = tcx.with_stable_hashing_context(|ref mut hcx| { + (hasher_fn(hcx, &old), hasher_fn(hcx, &value)) + }); if old_hash != value_hash { // We have an inconsistency. This can happen if one of the two // results is tainted by errors. In this case, delay a bug to // ensure compilation is doomed, and keep the `old` value. tcx.dcx().delayed_bug(format!( "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", + old value: {old}\nnew value: {value}", + old = format_value(&old), + value = format_value(&value), )); } } else { @@ -121,14 +121,24 @@ pub(crate) fn query_feed<'tcx, Cache, Value>( // the query should not be marked `no_hash`. bug!( "Trying to feed an already recorded value for query {dep_kind:?} key={key:?}:\n\ - old value: {old:?}\nnew value: {value:?}", + old value: {old}\nnew value: {value}", + old = format_value(&old), + value = format_value(&value), ) } } None => { + // There is no cached value for this key, so feed the query by + // adding the provided value to the cache. let dep_node = dep_graph::DepNode::construct(tcx, dep_kind, &key); - let dep_node_index = tcx.dep_graph.with_feed_task(dep_node, tcx, &value, hasher); - cache.complete(key, erased, dep_node_index); + let dep_node_index = tcx.dep_graph.with_feed_task( + dep_node, + tcx, + &value, + query_vtable.hash_result, + query_vtable.format_value, + ); + cache.complete(key, value, dep_node_index); } } } diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index cea50f95df4b..c9291d89be8a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -87,7 +87,6 @@ use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, Trait use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; -use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryMode, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 2bda014a19fe..0f08fa77cbc7 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -492,18 +492,6 @@ macro_rules! define_callbacks { }; } -macro_rules! hash_result { - ([]) => {{ - Some(dep_graph::hash_result) - }}; - ([(no_hash) $($rest:tt)*]) => {{ - None - }}; - ([$other:tt $($modifiers:tt)*]) => { - hash_result!([$($modifiers)*]) - }; -} - macro_rules! define_feedable { ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { @@ -513,19 +501,17 @@ macro_rules! define_feedable { let key = self.key().into_query_param(); let tcx = self.tcx; - let erased = queries::$name::provided_to_erased(tcx, value); - let cache = &tcx.query_system.caches.$name; + let erased_value = queries::$name::provided_to_erased(tcx, value); let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; - let hasher: Option, &_) -> _> = hash_result!([$($modifiers)*]); $crate::query::inner::query_feed( tcx, dep_kind, - hasher, - cache, + &tcx.query_system.query_vtables.$name, + &tcx.query_system.caches.$name, key, - erased, + erased_value, ); } })* diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 6d46d144d0f1..9b9b73d73202 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -561,12 +561,13 @@ impl DepGraph { /// FIXME: If the code is changed enough for this node to be marked before requiring the /// caller's node, we suppose that those changes will be enough to mark this node red and /// force a recomputation using the "normal" way. - pub fn with_feed_task, R: Debug>( + pub fn with_feed_task, R>( &self, node: DepNode, cx: Ctxt, result: &R, hash_result: Option, &R) -> Fingerprint>, + format_value_fn: fn(&R) -> String, ) -> DepNodeIndex { if let Some(data) = self.data.as_ref() { // The caller query has more dependencies than the node we are creating. We may @@ -584,7 +585,7 @@ impl DepGraph { result, prev_index, hash_result, - |value| format!("{value:?}"), + format_value_fn, ); #[cfg(debug_assertions)] From e12acb9251eb9d5a967aeafbe7ff3b3e97ae401f Mon Sep 17 00:00:00 2001 From: Frank King Date: Mon, 24 Nov 2025 15:18:59 +0800 Subject: [PATCH 163/319] Forbid manual `Unpin` impls for structurally pinned types --- compiler/rustc_hir_analysis/messages.ftl | 4 + .../src/coherence/builtin.rs | 36 +++++++++ compiler/rustc_hir_analysis/src/errors.rs | 11 +++ tests/ui/pin-ergonomics/impl-unpin.adt.stderr | 14 ++++ .../ui/pin-ergonomics/impl-unpin.assoc.stderr | 15 ++++ tests/ui/pin-ergonomics/impl-unpin.rs | 74 +++++++++++++++++++ .../ui/pin-ergonomics/impl-unpin.tait.stderr | 27 +++++++ .../pin-ergonomics/impl-unpin.ty_alias.stderr | 14 ++++ 8 files changed, 195 insertions(+) create mode 100644 tests/ui/pin-ergonomics/impl-unpin.adt.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.assoc.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.rs create mode 100644 tests/ui/pin-ergonomics/impl-unpin.tait.stderr create mode 100644 tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index d9f8eba65c4a..33b8d3c82965 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -224,6 +224,10 @@ hir_analysis_impl_not_marked_default = `{$ident}` specializes an item from a par hir_analysis_impl_not_marked_default_err = `{$ident}` specializes an item from a parent `impl`, but that item is not marked `default` .note = parent implementation is in crate `{$cname}` +hir_analysis_impl_unpin_for_pin_projected_type = explicit impls for the `Unpin` trait are not permitted for structurally pinned types + .label = impl of `Unpin` not allowed + .help = `{$adt_name}` is structurally pinned because it is marked as `#[pin_v2]` + hir_analysis_inherent_dyn = cannot define inherent `impl` for a dyn auto trait .label = impl requires at least one non-auto trait .note = define and implement a new trait or type instead diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index 61562cc1e4f3..f81913a9c86f 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -38,6 +38,7 @@ pub(super) fn check_trait<'tcx>( checker.check(lang_items.drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.async_drop_trait(), visit_implementation_of_drop)?; checker.check(lang_items.copy_trait(), visit_implementation_of_copy)?; + checker.check(lang_items.unpin_trait(), visit_implementation_of_unpin)?; checker.check(lang_items.const_param_ty_trait(), |checker| { visit_implementation_of_const_param_ty(checker) })?; @@ -134,6 +135,41 @@ fn visit_implementation_of_copy(checker: &Checker<'_>) -> Result<(), ErrorGuaran } } +fn visit_implementation_of_unpin(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { + let tcx = checker.tcx; + let impl_header = checker.impl_header; + let impl_did = checker.impl_def_id; + debug!("visit_implementation_of_unpin: impl_did={:?}", impl_did); + + let self_type = impl_header.trait_ref.instantiate_identity().self_ty(); + debug!("visit_implementation_of_unpin: self_type={:?}", self_type); + + let span = tcx.def_span(impl_did); + + if tcx.features().pin_ergonomics() { + match self_type.kind() { + // Soundness concerns: a type `T` annotated with `#[pin_v2]` is allowed to project + // `Pin<&mut T>` to its field `Pin<&mut U>` safely (even if `U: !Unpin`). + // If `T` is allowed to impl `Unpin` manually (note that `Unpin` is a safe trait, + // which cannot carry safety properties), then `&mut U` could be obtained from + // `&mut T` that dereferenced by `Pin<&mut T>`, which breaks the safety contract of + // `Pin<&mut U>` for `U: !Unpin`. + ty::Adt(adt, _) if adt.is_pin_project() => { + return Err(tcx.dcx().emit_err(crate::errors::ImplUnpinForPinProjectedType { + span, + adt_span: tcx.def_span(adt.did()), + adt_name: tcx.item_name(adt.did()), + })); + } + ty::Adt(_, _) => {} + _ => { + return Err(tcx.dcx().span_delayed_bug(span, "impl of `Unpin` for a non-adt type")); + } + }; + } + Ok(()) +} + fn visit_implementation_of_const_param_ty(checker: &Checker<'_>) -> Result<(), ErrorGuaranteed> { let tcx = checker.tcx; let header = checker.impl_header; diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 2a77d0b997e2..f0c9023c522c 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1690,3 +1690,14 @@ pub(crate) struct EiiWithGenerics { pub eii_name: Symbol, pub impl_name: Symbol, } + +#[derive(Diagnostic)] +#[diag(hir_analysis_impl_unpin_for_pin_projected_type)] +pub(crate) struct ImplUnpinForPinProjectedType { + #[primary_span] + #[label] + pub span: Span, + #[help] + pub adt_span: Span, + pub adt_name: Symbol, +} diff --git a/tests/ui/pin-ergonomics/impl-unpin.adt.stderr b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr new file mode 100644 index 000000000000..56fac42d4e32 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.adt.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:14:5 + | +LL | impl Unpin for Foo {} + | ^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr new file mode 100644 index 000000000000..7f0ee1ddd898 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.assoc.stderr @@ -0,0 +1,15 @@ +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:68:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error[E0321]: cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + --> $DIR/impl-unpin.rs:70:5 + | +LL | impl Unpin for ::Assoc {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0321`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.rs b/tests/ui/pin-ergonomics/impl-unpin.rs new file mode 100644 index 000000000000..ded8b4774dd9 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.rs @@ -0,0 +1,74 @@ +//@ revisions: adt tait ty_alias assoc +#![feature(pin_ergonomics)] +#![cfg_attr(tait, feature(type_alias_impl_trait))] +#![allow(incomplete_features)] + +#[pin_v2] +struct Foo; +struct Bar; + +#[cfg(adt)] +mod adt { + use super::*; + + impl Unpin for Foo {} + //[adt]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Bar {} // ok +} + +#[cfg(ty_alias)] +mod ty_alias { + use super::*; + + type Identity = T; + + impl Unpin for Identity {} + //[ty_alias]~^ ERROR explicit impls for the `Unpin` trait are not permitted for structurally pinned types + impl Unpin for Identity {} // ok +} + +#[cfg(tait)] +mod tait { + use super::*; + + trait Identity {} + + impl Identity for T {} + + type FooAlias = impl Identity; + type BarAlias = impl Identity; + + #[define_opaque(FooAlias)] + fn foo_alias() -> FooAlias { + Foo + } + #[define_opaque(BarAlias)] + fn bar_alias() -> BarAlias { + Bar + } + + impl Unpin for FooAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types + impl Unpin for BarAlias {} + //[tait]~^ ERROR only traits defined in the current crate can be implemented for arbitrary types +} + +#[cfg(assoc)] +mod assoc { + use super::*; + + trait Identity { + type Assoc; + } + + impl Identity for T { + type Assoc = T; + } + + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` + impl Unpin for ::Assoc {} + //[assoc]~^ ERROR cross-crate traits with a default impl, like `Unpin`, can only be implemented for a struct/enum type, not `::Assoc` +} + +fn main() {} diff --git a/tests/ui/pin-ergonomics/impl-unpin.tait.stderr b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr new file mode 100644 index 000000000000..5d9392745df3 --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.tait.stderr @@ -0,0 +1,27 @@ +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:50:5 + | +LL | impl Unpin for FooAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error[E0117]: only traits defined in the current crate can be implemented for arbitrary types + --> $DIR/impl-unpin.rs:52:5 + | +LL | impl Unpin for BarAlias {} + | ^^^^^^^^^^^^^^^-------- + | | + | type alias impl trait is treated as if it were foreign, because its hidden type could be from a foreign crate + | + = note: impl doesn't have any local type before any uncovered type parameters + = note: for more information see https://doc.rust-lang.org/reference/items/implementations.html#orphan-rules + = note: define and implement a trait or new type instead + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0117`. diff --git a/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr new file mode 100644 index 000000000000..82c1a7d29ddf --- /dev/null +++ b/tests/ui/pin-ergonomics/impl-unpin.ty_alias.stderr @@ -0,0 +1,14 @@ +error: explicit impls for the `Unpin` trait are not permitted for structurally pinned types + --> $DIR/impl-unpin.rs:25:5 + | +LL | impl Unpin for Identity {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ impl of `Unpin` not allowed + | +help: `Foo` is structurally pinned because it is marked as `#[pin_v2]` + --> $DIR/impl-unpin.rs:7:1 + | +LL | struct Foo; + | ^^^^^^^^^^ + +error: aborting due to 1 previous error + From 2a9de6ba853ff78b9b34b655b87313f0737417c5 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Tue, 27 Jan 2026 21:35:59 +0100 Subject: [PATCH 164/319] CI: rfl: move to temporary commit to pass `-Zunstable-options` for custom target specs This also takes the chance to move forward the job to Linux v6.19-rc7, thus the previous workaround is not needed anymore. Link: https://github.com/rust-lang/rust/pull/151534 Signed-off-by: Miguel Ojeda --- src/ci/docker/scripts/rfl-build.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/scripts/rfl-build.sh b/src/ci/docker/scripts/rfl-build.sh index e7f86e10f55a..535250557740 100755 --- a/src/ci/docker/scripts/rfl-build.sh +++ b/src/ci/docker/scripts/rfl-build.sh @@ -2,8 +2,8 @@ set -euo pipefail -# https://github.com/rust-lang/rust/pull/145974 -LINUX_VERSION=842cfd8e5aff3157cb25481b2900b49c188d628a +# https://github.com/rust-lang/rust/pull/151534 +LINUX_VERSION=eb268c7972f65fa0b11b051c5ef2b92747bb2b62 # Build rustc, rustdoc, cargo, clippy-driver and rustfmt ../x.py build --stage 2 library rustdoc clippy rustfmt From 85f2923dc53811317f460fdac5c20d602dcb23d8 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Fri, 30 Jan 2026 16:00:31 +0100 Subject: [PATCH 165/319] fix: Fix diagnostics being leaked when diagnostics panic --- .../crates/rust-analyzer/src/diagnostics.rs | 58 ++++++++++--------- .../crates/rust-analyzer/src/main_loop.rs | 18 +++--- 2 files changed, 42 insertions(+), 34 deletions(-) diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs index 712960f13d7e..8d0f52433e02 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/diagnostics.rs @@ -289,34 +289,40 @@ pub(crate) fn fetch_native_diagnostics( let mut diagnostics = subscriptions[slice] .iter() .copied() - .filter_map(|file_id| { - let line_index = snapshot.file_line_index(file_id).ok()?; - let source_root = snapshot.analysis.source_root_id(file_id).ok()?; + .map(|file_id| { + let diagnostics = (|| { + let line_index = snapshot.file_line_index(file_id).ok()?; + let source_root = snapshot.analysis.source_root_id(file_id).ok()?; - let config = &snapshot.config.diagnostics(Some(source_root)); - let diagnostics = match kind { - NativeDiagnosticsFetchKind::Syntax => { - snapshot.analysis.syntax_diagnostics(config, file_id).ok()? - } - - NativeDiagnosticsFetchKind::Semantic if config.enabled => snapshot - .analysis - .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id) - .ok()?, - NativeDiagnosticsFetchKind::Semantic => return None, - }; - let diagnostics = diagnostics - .into_iter() - .filter_map(|d| { - if d.range.file_id == file_id { - Some(convert_diagnostic(&line_index, d)) - } else { - odd_ones.push(d); - None + let config = &snapshot.config.diagnostics(Some(source_root)); + let diagnostics = match kind { + NativeDiagnosticsFetchKind::Syntax => { + snapshot.analysis.syntax_diagnostics(config, file_id).ok()? } - }) - .collect::>(); - Some((file_id, diagnostics)) + + NativeDiagnosticsFetchKind::Semantic if config.enabled => snapshot + .analysis + .semantic_diagnostics(config, ide::AssistResolveStrategy::None, file_id) + .ok()?, + NativeDiagnosticsFetchKind::Semantic => return None, + }; + Some( + diagnostics + .into_iter() + .filter_map(|d| { + if d.range.file_id == file_id { + Some(convert_diagnostic(&line_index, d)) + } else { + odd_ones.push(d); + None + } + }) + .collect::>(), + ) + })() + .unwrap_or_default(); + + (file_id, diagnostics) }) .collect::>(); diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs index f5cead5d8f9d..64decc9e0db6 100644 --- a/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/main_loop.rs @@ -666,31 +666,33 @@ impl GlobalState { move |sender| { // We aren't observing the semantics token cache here let snapshot = AssertUnwindSafe(&snapshot); - let Ok(diags) = std::panic::catch_unwind(|| { + let diags = std::panic::catch_unwind(|| { fetch_native_diagnostics( &snapshot, subscriptions.clone(), slice.clone(), NativeDiagnosticsFetchKind::Syntax, ) - }) else { - return; - }; + }) + .unwrap_or_else(|_| { + subscriptions.iter().map(|&id| (id, Vec::new())).collect::>() + }); sender .send(Task::Diagnostics(DiagnosticsTaskKind::Syntax(generation, diags))) .unwrap(); if fetch_semantic { - let Ok(diags) = std::panic::catch_unwind(|| { + let diags = std::panic::catch_unwind(|| { fetch_native_diagnostics( &snapshot, subscriptions.clone(), slice.clone(), NativeDiagnosticsFetchKind::Semantic, ) - }) else { - return; - }; + }) + .unwrap_or_else(|_| { + subscriptions.iter().map(|&id| (id, Vec::new())).collect::>() + }); sender .send(Task::Diagnostics(DiagnosticsTaskKind::Semantic( generation, diags, From 0271b6b156e602dd8157e3284d37933377c1476c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Fri, 30 Jan 2026 18:25:19 +0100 Subject: [PATCH 166/319] regression test for alias-relate changes in lub --- .../generalize/relate-alias-in-lub.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs diff --git a/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs new file mode 100644 index 000000000000..0a948989f981 --- /dev/null +++ b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs @@ -0,0 +1,30 @@ +//@ revisions: old next +//@[next] compile-flags: -Znext-solver +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ check-pass +// Reproduces https://github.com/rust-lang/rust/pull/151746#issuecomment-3822930803. +// +// The change we tried to make there caused relating a type variable with an alias inside lub, +// In 5bd20bbd0ba6c0285664e55a1ffc677d7487c98b, we moved around code +// that adds an alias-relate predicate to be earlier, from one shared codepath into several +// distinct code paths. However, we forgot one codepath, through lub, causing an ICE in serde. +// In the end we dropped said commit, but the reproducer is still a useful as test. + +use std::marker::PhantomData; + +pub trait Trait { + type Error; +} + +pub struct Struct(PhantomData); + +impl Trait for () { + type Error = (); +} + +fn main() { + let _: Struct<<() as Trait>::Error> = match loop {} { + b => loop {}, + a => Struct(PhantomData), + }; +} From 5f76adf7b368b4bec6fb3771fe645ab762ef8236 Mon Sep 17 00:00:00 2001 From: reddevilmidzy Date: Sat, 31 Jan 2026 07:37:52 +0900 Subject: [PATCH 167/319] fix adk installer link --- src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index d2680d40853f..2267f9b490f7 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -22,8 +22,8 @@ specifically designed to make analyzing rustc easier. You can install WPR and WPA as part of the Windows Performance Toolkit which itself is an option as part of downloading the Windows Assessment and Deployment Kit (ADK). You can download the ADK -installer [here](https://go.microsoft.com/fwlink/?linkid=2086042). Make sure to select the Windows -Performance Toolkit (you don't need to select anything else). +installer [here](https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install). Make +sure to select the Windows Performance Toolkit (you don't need to select anything else). ## Recording From 5cb916370bf2b770b15ee9b915fbe8f99b36890e Mon Sep 17 00:00:00 2001 From: "R. Yin" <88910093+thealtofwar@users.noreply.github.com> Date: Fri, 30 Jan 2026 20:02:48 -0600 Subject: [PATCH 168/319] Fix typo in debugging.md --- src/doc/rustc-dev-guide/src/autodiff/debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/autodiff/debugging.md b/src/doc/rustc-dev-guide/src/autodiff/debugging.md index 7c7af8589868..3b2278f1a075 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/debugging.md +++ b/src/doc/rustc-dev-guide/src/autodiff/debugging.md @@ -16,7 +16,7 @@ Before generating the llvm-ir, keep in mind two techniques that can help ensure ## 1) Generate an llvm-ir reproducer ```sh -RUSTFLAGS="-Z autodiff=Enable,PrintModbefore" cargo +enzyme build --release &> out.ll +RUSTFLAGS="-Z autodiff=Enable,PrintModBefore" cargo +enzyme build --release &> out.ll ``` This also captures a few warnings and info messages above and below your module. open out.ll and remove every line above `; moduleid = `. Now look at the end of the file and remove everything that's not part of llvm-ir, i.e. remove errors and warnings. The last line of your llvm-ir should now start with `! = `, i.e. `!40831 = !{i32 0, i32 1037508, i32 1037538, i32 1037559}` or `!43760 = !dilocation(line: 297, column: 5, scope: !43746)`. From c8d2f3e6f16ff797d8789fe9818f2c6a144153dc Mon Sep 17 00:00:00 2001 From: The rustc-josh-sync Cronjob Bot Date: Sat, 31 Jan 2026 02:06:09 +0000 Subject: [PATCH 169/319] Prepare for merging from rust-lang/rust This updates the rust-version file to 44e34e1ac6d7e69b40856cf1403d3da145319c30. --- src/doc/rustc-dev-guide/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index 795271ee0ef0..209f4226eae7 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -370143facfb348ad3b29749c0393402d76b280c3 +44e34e1ac6d7e69b40856cf1403d3da145319c30 From 04e6afe20a6b5ea3b1a0e17a25df486c43a66df4 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 26 Jan 2026 14:15:15 +1100 Subject: [PATCH 170/319] Re-export `hashbrown::hash_table` from `rustc_data_structures` --- Cargo.lock | 2 -- compiler/rustc_data_structures/src/lib.rs | 4 ++++ compiler/rustc_data_structures/src/sharded.rs | 4 ++-- compiler/rustc_mir_transform/Cargo.toml | 1 - compiler/rustc_mir_transform/src/gvn.rs | 2 +- compiler/rustc_query_system/Cargo.toml | 5 ----- compiler/rustc_query_system/src/query/plumbing.rs | 5 ++--- 7 files changed, 9 insertions(+), 14 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6be4565374af..b63bb619b64e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4354,7 +4354,6 @@ name = "rustc_mir_transform" version = "0.0.0" dependencies = [ "either", - "hashbrown 0.16.1", "itertools", "rustc_abi", "rustc_arena", @@ -4565,7 +4564,6 @@ dependencies = [ name = "rustc_query_system" version = "0.0.0" dependencies = [ - "hashbrown 0.16.1", "parking_lot", "rustc_abi", "rustc_ast", diff --git a/compiler/rustc_data_structures/src/lib.rs b/compiler/rustc_data_structures/src/lib.rs index 85877d73519a..4467a2811181 100644 --- a/compiler/rustc_data_structures/src/lib.rs +++ b/compiler/rustc_data_structures/src/lib.rs @@ -49,6 +49,10 @@ pub use std::{assert_matches, debug_assert_matches}; pub use atomic_ref::AtomicRef; pub use ena::{snapshot_vec, undo_log, unify}; +// Re-export `hashbrown::hash_table`, because it's part of our API +// (via `ShardedHashMap`), and because it lets other compiler crates use the +// lower-level `HashTable` API without a tricky `hashbrown` dependency. +pub use hashbrown::hash_table; pub use rustc_index::static_assert_size; // Re-export some data-structure crates which are part of our public API. pub use {either, indexmap, smallvec, thin_vec}; diff --git a/compiler/rustc_data_structures/src/sharded.rs b/compiler/rustc_data_structures/src/sharded.rs index 5de9413cf15d..e10ccccad5bb 100644 --- a/compiler/rustc_data_structures/src/sharded.rs +++ b/compiler/rustc_data_structures/src/sharded.rs @@ -3,7 +3,7 @@ use std::hash::{Hash, Hasher}; use std::{iter, mem}; use either::Either; -use hashbrown::hash_table::{Entry, HashTable}; +use hashbrown::hash_table::{self, Entry, HashTable}; use crate::fx::FxHasher; use crate::sync::{CacheAligned, Lock, LockGuard, Mode, is_dyn_thread_safe}; @@ -140,7 +140,7 @@ pub fn shards() -> usize { 1 } -pub type ShardedHashMap = Sharded>; +pub type ShardedHashMap = Sharded>; impl ShardedHashMap { pub fn with_capacity(cap: usize) -> Self { diff --git a/compiler/rustc_mir_transform/Cargo.toml b/compiler/rustc_mir_transform/Cargo.toml index 22de197d374a..404531eb3c91 100644 --- a/compiler/rustc_mir_transform/Cargo.toml +++ b/compiler/rustc_mir_transform/Cargo.toml @@ -6,7 +6,6 @@ edition = "2024" [dependencies] # tidy-alphabetical-start either = "1" -hashbrown = { version = "0.16.1", default-features = false } itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_arena = { path = "../rustc_arena" } diff --git a/compiler/rustc_mir_transform/src/gvn.rs b/compiler/rustc_mir_transform/src/gvn.rs index 820998eed100..e06b3625aa7b 100644 --- a/compiler/rustc_mir_transform/src/gvn.rs +++ b/compiler/rustc_mir_transform/src/gvn.rs @@ -88,7 +88,6 @@ use std::borrow::Cow; use std::hash::{Hash, Hasher}; use either::Either; -use hashbrown::hash_table::{Entry, HashTable}; use itertools::Itertools as _; use rustc_abi::{self as abi, BackendRepr, FIRST_VARIANT, FieldIdx, Primitive, Size, VariantIdx}; use rustc_arena::DroplessArena; @@ -99,6 +98,7 @@ use rustc_const_eval::interpret::{ }; use rustc_data_structures::fx::FxHasher; use rustc_data_structures::graph::dominators::Dominators; +use rustc_data_structures::hash_table::{Entry, HashTable}; use rustc_hir::def::DefKind; use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexVec, newtype_index}; diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 73ab03576dec..0ad8143c5a4f 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -23,8 +23,3 @@ rustc_thread_pool = { path = "../rustc_thread_pool" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" # tidy-alphabetical-end - -[dependencies.hashbrown] -version = "0.16.1" -default-features = false -features = ["nightly"] # for may_dangle diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 04902d0ab10c..7e34957364e4 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -7,9 +7,8 @@ use std::fmt::Debug; use std::hash::Hash; use std::mem; -use hashbrown::HashTable; -use hashbrown::hash_table::Entry; use rustc_data_structures::fingerprint::Fingerprint; +use rustc_data_structures::hash_table::{self, Entry, HashTable}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_data_structures::sync::LockGuard; @@ -35,7 +34,7 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { } pub struct QueryState<'tcx, K> { - active: Sharded)>>, + active: Sharded)>>, } /// Indicates the state of a query for a given key in a query map. From 85ae47f83e0750e82e75229ca8adeace7d0e1239 Mon Sep 17 00:00:00 2001 From: delta17920 Date: Sat, 31 Jan 2026 06:44:24 +0000 Subject: [PATCH 171/319] moved 7 tests to organized locations --- .../issue-2074.rs => closures/local-enums-in-closure-2074.rs} | 0 .../struct-function-same-name-2445-b.rs} | 0 .../issue-2445.rs => resolve/struct-function-same-name-2445.rs} | 0 .../struct-function-same-name-2487-a.rs} | 0 .../issue-2502.rs => resolve/struct-function-same-name-2502.rs} | 0 .../issue-2550.rs => resolve/struct-function-same-name-2550.rs} | 0 .../issue-2463.rs => structs/struct-update-syntax-2463.rs} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-2074.rs => closures/local-enums-in-closure-2074.rs} (100%) rename tests/ui/{issues/issue-2445-b.rs => resolve/struct-function-same-name-2445-b.rs} (100%) rename tests/ui/{issues/issue-2445.rs => resolve/struct-function-same-name-2445.rs} (100%) rename tests/ui/{issues/issue-2487-a.rs => resolve/struct-function-same-name-2487-a.rs} (100%) rename tests/ui/{issues/issue-2502.rs => resolve/struct-function-same-name-2502.rs} (100%) rename tests/ui/{issues/issue-2550.rs => resolve/struct-function-same-name-2550.rs} (100%) rename tests/ui/{issues/issue-2463.rs => structs/struct-update-syntax-2463.rs} (100%) diff --git a/tests/ui/issues/issue-2074.rs b/tests/ui/closures/local-enums-in-closure-2074.rs similarity index 100% rename from tests/ui/issues/issue-2074.rs rename to tests/ui/closures/local-enums-in-closure-2074.rs diff --git a/tests/ui/issues/issue-2445-b.rs b/tests/ui/resolve/struct-function-same-name-2445-b.rs similarity index 100% rename from tests/ui/issues/issue-2445-b.rs rename to tests/ui/resolve/struct-function-same-name-2445-b.rs diff --git a/tests/ui/issues/issue-2445.rs b/tests/ui/resolve/struct-function-same-name-2445.rs similarity index 100% rename from tests/ui/issues/issue-2445.rs rename to tests/ui/resolve/struct-function-same-name-2445.rs diff --git a/tests/ui/issues/issue-2487-a.rs b/tests/ui/resolve/struct-function-same-name-2487-a.rs similarity index 100% rename from tests/ui/issues/issue-2487-a.rs rename to tests/ui/resolve/struct-function-same-name-2487-a.rs diff --git a/tests/ui/issues/issue-2502.rs b/tests/ui/resolve/struct-function-same-name-2502.rs similarity index 100% rename from tests/ui/issues/issue-2502.rs rename to tests/ui/resolve/struct-function-same-name-2502.rs diff --git a/tests/ui/issues/issue-2550.rs b/tests/ui/resolve/struct-function-same-name-2550.rs similarity index 100% rename from tests/ui/issues/issue-2550.rs rename to tests/ui/resolve/struct-function-same-name-2550.rs diff --git a/tests/ui/issues/issue-2463.rs b/tests/ui/structs/struct-update-syntax-2463.rs similarity index 100% rename from tests/ui/issues/issue-2463.rs rename to tests/ui/structs/struct-update-syntax-2463.rs From a77b9e62ccf1f5fd93c6d899a37d074dd84d3eac Mon Sep 17 00:00:00 2001 From: Ada Alakbarova <58857108+ada4a@users.noreply.github.com> Date: Sat, 31 Jan 2026 07:58:24 +0100 Subject: [PATCH 172/319] Fix tense --- src/doc/rustc-dev-guide/src/offload/internals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/offload/internals.md b/src/doc/rustc-dev-guide/src/offload/internals.md index 77a4cadbcb98..520c48d95896 100644 --- a/src/doc/rustc-dev-guide/src/offload/internals.md +++ b/src/doc/rustc-dev-guide/src/offload/internals.md @@ -13,4 +13,4 @@ We use a single-source, two-pass compilation approach. First we compile all functions that should be offloaded for the device (e.g nvptx64, amdgcn-amd-amdhsa, intel in the future). Currently we require cumbersome `#cfg(target_os="")` annotations, but we intend to recognize those in the future based on our offload intrinsic. -We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), from the device, or both (e.g. `&mut [f64]`). We then launched the kernel, after which we inform the runtime to end this environment and move data back (as far as needed). +We then compile the code for the host (e.g. x86-64), where most of the offloading logic happens. On the host side, we generate calls to the openmp offload runtime, to inform it about the layout of the types (a simplified version of the autodiff TypeTrees). We also use the type system to figure out whether kernel arguments have to be moved only to the device (e.g. `&[f32;1024]`), from the device, or both (e.g. `&mut [f64]`). We then launch the kernel, after which we inform the runtime to end this environment and move data back (as far as needed). From 65f075792bc92bc8027806e3152a8fd396954eab Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sun, 25 Jan 2026 23:22:43 +0300 Subject: [PATCH 173/319] resolve: Use `IdentKey` in `resolve_ident_in_scope_set` --- compiler/rustc_resolve/src/diagnostics.rs | 18 +++-- compiler/rustc_resolve/src/ident.rs | 71 +++++++++++++------ .../rustc_resolve/src/late/diagnostics.rs | 2 +- compiler/rustc_resolve/src/lib.rs | 5 +- 4 files changed, 65 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6f2d3e79d10a..6884ed1891a3 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -32,7 +32,7 @@ use rustc_span::edit_distance::find_best_match_for_name; use rustc_span::edition::Edition; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::{SourceMap, Spanned}; -use rustc_span::{BytePos, DUMMY_SP, Ident, Span, Symbol, SyntaxContext, kw, sym}; +use rustc_span::{BytePos, Ident, Span, Symbol, SyntaxContext, kw, sym}; use thin_vec::{ThinVec, thin_vec}; use tracing::{debug, instrument}; @@ -41,6 +41,7 @@ use crate::errors::{ ExplicitUnsafeTraits, MacroDefinedLater, MacroRulesNot, MacroSuggMovePosition, MaybeMissingMacroRulesName, }; +use crate::hygiene::Macros20NormalizedSyntaxContext; use crate::imports::{Import, ImportKind}; use crate::late::{DiagMetadata, PatternSource, Rib}; use crate::{ @@ -1163,11 +1164,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { suggestions: &mut Vec, scope_set: ScopeSet<'ra>, ps: &ParentScope<'ra>, - ctxt: SyntaxContext, + sp: Span, filter_fn: &impl Fn(Res) -> bool, ) { - let ctxt = DUMMY_SP.with_ctxt(ctxt); - self.cm().visit_scopes(scope_set, ps, ctxt, None, |this, scope, use_prelude, _| { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, ps, ctxt, sp, None, |this, scope, use_prelude, _| { match scope { Scope::DeriveHelpers(expn_id) => { let res = Res::NonMacroAttr(NonMacroAttrKind::DeriveHelper); @@ -1269,8 +1270,13 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { filter_fn: &impl Fn(Res) -> bool, ) -> Option { let mut suggestions = Vec::new(); - let ctxt = ident.span.ctxt(); - self.add_scope_set_candidates(&mut suggestions, scope_set, parent_scope, ctxt, filter_fn); + self.add_scope_set_candidates( + &mut suggestions, + scope_set, + parent_scope, + ident.span, + filter_fn, + ); // Make sure error reporting is deterministic. suggestions.sort_by(|a, b| a.candidate.as_str().cmp(b.candidate.as_str())); diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 4fbde60d8679..5c85f721c0b8 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -54,9 +54,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { mut self: CmResolver<'r, 'ra, 'tcx>, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, - // Location of the span is not significant, but pass a `Span` instead of `SyntaxContext` - // to avoid extracting and re-packaging the syntax context unnecessarily. - orig_ctxt: Span, + mut ctxt: Macros20NormalizedSyntaxContext, + orig_ident_span: Span, derive_fallback_lint_id: Option, mut visitor: impl FnMut( CmResolver<'_, 'ra, 'tcx>, @@ -128,7 +127,6 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { TypeNS | ValueNS => Scope::ModuleNonGlobs(module, None), MacroNS => Scope::DeriveHelpers(parent_scope.expansion), }; - let mut ctxt = Macros20NormalizedSyntaxContext::new(orig_ctxt.ctxt()); let mut use_prelude = !module.no_implicit_prelude; loop { @@ -153,7 +151,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { true } Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..) => true, - Scope::MacroUsePrelude => use_prelude || orig_ctxt.edition().is_rust_2015(), + Scope::MacroUsePrelude => use_prelude || orig_ident_span.is_rust_2015(), Scope::BuiltinAttrs => true, Scope::ExternPreludeItems | Scope::ExternPreludeFlags => { use_prelude || module_and_extern_prelude || extern_prelude @@ -396,9 +394,30 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { finalize: Option, ignore_decl: Option>, ignore_import: Option>, + ) -> Result, Determinacy> { + self.resolve_ident_in_scope_set_inner( + IdentKey::new(orig_ident), + orig_ident.span, + scope_set, + parent_scope, + finalize, + ignore_decl, + ignore_import, + ) + } + + fn resolve_ident_in_scope_set_inner<'r>( + self: CmResolver<'r, 'ra, 'tcx>, + ident: IdentKey, + orig_ident_span: Span, + scope_set: ScopeSet<'ra>, + parent_scope: &ParentScope<'ra>, + finalize: Option, + ignore_decl: Option>, + ignore_import: Option>, ) -> Result, Determinacy> { // Make sure `self`, `super` etc produce an error when passed to here. - if !matches!(scope_set, ScopeSet::Module(..)) && orig_ident.is_path_segment_keyword() { + if !matches!(scope_set, ScopeSet::Module(..)) && ident.name.is_path_segment_keyword() { return Err(Determinacy::Determined); } @@ -432,13 +451,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { let break_result = self.visit_scopes( scope_set, parent_scope, - orig_ident.span, + ident.ctxt, + orig_ident_span, derive_fallback_lint_id, |mut this, scope, use_prelude, ctxt| { - let ident = IdentKey { name: orig_ident.name, ctxt }; + let ident = IdentKey { name: ident.name, ctxt }; let res = match this.reborrow().resolve_ident_in_scope( ident, - orig_ident.span, + orig_ident_span, ns, scope, use_prelude, @@ -472,7 +492,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(&(innermost_decl, _)) = innermost_results.first() { // Found another solution, if the first one was "weak", report an error. if this.get_mut().maybe_push_ambiguity( - orig_ident, + ident, + orig_ident_span, ns, scope_set, parent_scope, @@ -695,8 +716,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Scope::StdLibPrelude => { let mut result = Err(Determinacy::Determined); if let Some(prelude) = self.prelude - && let Ok(decl) = self.reborrow().resolve_ident_in_scope_set( - ident.orig(orig_ident_span.with_ctxt(*ident.ctxt)), + && let Ok(decl) = self.reborrow().resolve_ident_in_scope_set_inner( + ident, + orig_ident_span, ScopeSet::Module(ns, prelude), parent_scope, None, @@ -749,7 +771,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { fn maybe_push_ambiguity( &mut self, - orig_ident: Ident, + ident: IdentKey, + orig_ident_span: Span, ns: Namespace, scope_set: ScopeSet<'ra>, parent_scope: &ParentScope<'ra>, @@ -775,7 +798,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else if innermost_res == derive_helper_compat { Some(AmbiguityKind::DeriveHelper) } else if res == derive_helper_compat && innermost_res != derive_helper { - span_bug!(orig_ident.span, "impossible inner resolution kind") + span_bug!(orig_ident_span, "impossible inner resolution kind") } else if matches!(innermost_scope, Scope::MacroRules(_)) && matches!(scope, Scope::ModuleNonGlobs(..) | Scope::ModuleGlobs(..)) && !self.disambiguate_macro_rules_vs_modularized(innermost_decl, decl) @@ -790,7 +813,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // we visit all macro_rules scopes (e.g. textual scope macros) // before we visit any modules (e.g. path-based scope macros) span_bug!( - orig_ident.span, + orig_ident_span, "ambiguous scoped macro resolutions with path-based \ scope resolution as first candidate" ) @@ -839,8 +862,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } else { // Turn ambiguity errors for core vs std panic into warnings. // FIXME: Remove with lang team approval. - let is_issue_147319_hack = orig_ident.span.edition() <= Edition::Edition2024 - && matches!(orig_ident.name, sym::panic) + let is_issue_147319_hack = orig_ident_span.edition() <= Edition::Edition2024 + && matches!(ident.name, sym::panic) && matches!(scope, Scope::StdLibPrelude) && matches!(innermost_scope, Scope::ModuleGlobs(_, _)) && ((self.is_specific_builtin_macro(res, sym::std_panic) @@ -852,7 +875,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { self.ambiguity_errors.push(AmbiguityError { kind, - ident: orig_ident, + ident: ident.orig(orig_ident_span), b1: innermost_decl, b2: decl, scope1: innermost_scope, @@ -1145,8 +1168,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None => return Err(ControlFlow::Continue(Undetermined)), }; let tmp_parent_scope; - let (mut adjusted_parent_scope, mut ctxt) = (parent_scope, *ident.ctxt); - match ctxt.glob_adjust(module.expansion, glob_import.span) { + let (mut adjusted_parent_scope, mut adjusted_ident) = (parent_scope, ident); + match adjusted_ident + .ctxt + .update_unchecked(|ctxt| ctxt.glob_adjust(module.expansion, glob_import.span)) + { Some(Some(def)) => { tmp_parent_scope = ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; @@ -1155,8 +1181,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { Some(None) => {} None => continue, }; - let result = self.reborrow().resolve_ident_in_scope_set( - ident.orig(orig_ident_span.with_ctxt(ctxt)), + let result = self.reborrow().resolve_ident_in_scope_set_inner( + adjusted_ident, + orig_ident_span, ScopeSet::Module(ns, module), adjusted_parent_scope, None, diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index a3ee17ec4a9a..8794c4ff8b02 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -2677,7 +2677,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &mut names, ScopeSet::All(ns), parent_scope, - ctxt, + segment.ident.span.with_ctxt(ctxt), filter_fn, ); break; diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 5fe1be039a88..dd5ac55d30d8 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1923,7 +1923,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { &mut self, current_trait: Option>, parent_scope: &ParentScope<'ra>, - ctxt: Span, + sp: Span, assoc_item: Option<(Symbol, Namespace)>, ) -> Vec { let mut found_traits = Vec::new(); @@ -1940,7 +1940,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } let scope_set = ScopeSet::All(TypeNS); - self.cm().visit_scopes(scope_set, parent_scope, ctxt, None, |mut this, scope, _, _| { + let ctxt = Macros20NormalizedSyntaxContext::new(sp.ctxt()); + self.cm().visit_scopes(scope_set, parent_scope, ctxt, sp, None, |mut this, scope, _, _| { match scope { Scope::ModuleNonGlobs(module, _) => { this.get_mut().traits_in_module(module, assoc_item, &mut found_traits); From 726a0a68b1d9867f1beed0168bd2a9a87aad4e81 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 30 Jan 2026 16:36:42 +0300 Subject: [PATCH 174/319] resolve: Inline `resolve_ident_in_virt_module_unadjusted` into `resolve_ident_in_module` --- compiler/rustc_resolve/src/ident.rs | 56 +++++++---------------------- 1 file changed, 13 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 5c85f721c0b8..5b3fd805ebb8 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -912,55 +912,24 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ignore_decl: Option>, ignore_import: Option>, ) -> Result, Determinacy> { - let tmp_parent_scope; - let mut adjusted_parent_scope = parent_scope; match module { - ModuleOrUniformRoot::Module(m) => { - if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(m.expansion) { + ModuleOrUniformRoot::Module(module) => { + let tmp_parent_scope; + let mut adjusted_parent_scope = parent_scope; + if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(module.expansion) { tmp_parent_scope = ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; adjusted_parent_scope = &tmp_parent_scope; } + self.resolve_ident_in_scope_set( + ident, + ScopeSet::Module(ns, module), + &adjusted_parent_scope, + finalize, + ignore_decl, + ignore_import, + ) } - ModuleOrUniformRoot::ExternPrelude => { - ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); - } - ModuleOrUniformRoot::ModuleAndExternPrelude(..) | ModuleOrUniformRoot::CurrentScope => { - // No adjustments - } - } - self.resolve_ident_in_virt_module_unadjusted( - module, - ident, - ns, - adjusted_parent_scope, - finalize, - ignore_decl, - ignore_import, - ) - } - - /// Attempts to resolve `ident` in namespace `ns` of `module`. - #[instrument(level = "debug", skip(self))] - fn resolve_ident_in_virt_module_unadjusted<'r>( - self: CmResolver<'r, 'ra, 'tcx>, - module: ModuleOrUniformRoot<'ra>, - ident: Ident, - ns: Namespace, - parent_scope: &ParentScope<'ra>, - finalize: Option, - ignore_decl: Option>, - ignore_import: Option>, - ) -> Result, Determinacy> { - match module { - ModuleOrUniformRoot::Module(module) => self.resolve_ident_in_scope_set( - ident, - ScopeSet::Module(ns, module), - parent_scope, - finalize, - ignore_decl, - ignore_import, - ), ModuleOrUniformRoot::ModuleAndExternPrelude(module) => self.resolve_ident_in_scope_set( ident, ScopeSet::ModuleAndExternPrelude(ns, module), @@ -973,6 +942,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ns != TypeNS { Err(Determined) } else { + ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); self.resolve_ident_in_scope_set( ident, ScopeSet::ExternPrelude, From 161fcdeac00c9bc8b284bd62eef207dd5698cc05 Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 30 Jan 2026 17:05:57 +0300 Subject: [PATCH 175/319] resolve: Avoid double normalization in `resolve_ident_in_module` --- compiler/rustc_resolve/src/ident.rs | 25 ++++++++++++------------- compiler/rustc_resolve/src/lib.rs | 17 ++++++++++++++++- compiler/rustc_span/src/hygiene.rs | 2 +- 3 files changed, 29 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 5b3fd805ebb8..0e73c349c8cd 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -905,7 +905,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { pub(crate) fn resolve_ident_in_module<'r>( self: CmResolver<'r, 'ra, 'tcx>, module: ModuleOrUniformRoot<'ra>, - mut ident: Ident, + ident: Ident, ns: Namespace, parent_scope: &ParentScope<'ra>, finalize: Option, @@ -914,15 +914,14 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ) -> Result, Determinacy> { match module { ModuleOrUniformRoot::Module(module) => { - let tmp_parent_scope; - let mut adjusted_parent_scope = parent_scope; - if let Some(def) = ident.span.normalize_to_macros_2_0_and_adjust(module.expansion) { - tmp_parent_scope = - ParentScope { module: self.expn_def_scope(def), ..*parent_scope }; - adjusted_parent_scope = &tmp_parent_scope; - } - self.resolve_ident_in_scope_set( - ident, + let (ident_key, def) = IdentKey::new_adjusted(ident, module.expansion); + let adjusted_parent_scope = match def { + Some(def) => ParentScope { module: self.expn_def_scope(def), ..*parent_scope }, + None => *parent_scope, + }; + self.resolve_ident_in_scope_set_inner( + ident_key, + ident.span, ScopeSet::Module(ns, module), &adjusted_parent_scope, finalize, @@ -942,9 +941,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if ns != TypeNS { Err(Determined) } else { - ident.span.normalize_to_macros_2_0_and_adjust(ExpnId::root()); - self.resolve_ident_in_scope_set( - ident, + self.resolve_ident_in_scope_set_inner( + IdentKey::new_adjusted(ident, ExpnId::root()).0, + ident.span, ScopeSet::ExternPrelude, parent_scope, finalize, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index dd5ac55d30d8..75bd2c62f9b4 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -575,6 +575,12 @@ impl IdentKey { IdentKey { name: ident.name, ctxt: Macros20NormalizedSyntaxContext::new(ident.span.ctxt()) } } + #[inline] + fn new_adjusted(ident: Ident, expn_id: ExpnId) -> (IdentKey, Option) { + let (ctxt, def) = Macros20NormalizedSyntaxContext::new_adjusted(ident.span.ctxt(), expn_id); + (IdentKey { name: ident.name, ctxt }, def) + } + #[inline] fn with_root_ctxt(name: Symbol) -> Self { let ctxt = Macros20NormalizedSyntaxContext::new_unchecked(SyntaxContext::root()); @@ -2724,7 +2730,7 @@ mod ref_mut { } mod hygiene { - use rustc_span::SyntaxContext; + use rustc_span::{ExpnId, SyntaxContext}; /// A newtype around `SyntaxContext` that can only keep contexts produced by /// [SyntaxContext::normalize_to_macros_2_0]. @@ -2737,6 +2743,15 @@ mod hygiene { Macros20NormalizedSyntaxContext(ctxt.normalize_to_macros_2_0()) } + #[inline] + pub(crate) fn new_adjusted( + mut ctxt: SyntaxContext, + expn_id: ExpnId, + ) -> (Macros20NormalizedSyntaxContext, Option) { + let def = ctxt.normalize_to_macros_2_0_and_adjust(expn_id); + (Macros20NormalizedSyntaxContext(ctxt), def) + } + #[inline] pub(crate) fn new_unchecked(ctxt: SyntaxContext) -> Macros20NormalizedSyntaxContext { debug_assert_eq!(ctxt, ctxt.normalize_to_macros_2_0()); diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 2e8037b3f9e4..05f4c8e268a9 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -804,7 +804,7 @@ impl SyntaxContext { /// Like `SyntaxContext::adjust`, but also normalizes `self` to macros 2.0. #[inline] - pub(crate) fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { + pub fn normalize_to_macros_2_0_and_adjust(&mut self, expn_id: ExpnId) -> Option { HygieneData::with(|data| { *self = data.normalize_to_macros_2_0(*self); data.adjust(self, expn_id) From 996425b295bdc783725f42df6ad25311886a1bc8 Mon Sep 17 00:00:00 2001 From: Frank King Date: Fri, 30 Jan 2026 11:06:27 +0800 Subject: [PATCH 176/319] refactor: add an `enum DerefAdjustKind` in favor of `Option` --- compiler/rustc_hir_typeck/src/autoderef.rs | 22 ++++++++++--------- compiler/rustc_hir_typeck/src/coercion.rs | 9 ++++---- .../rustc_hir_typeck/src/expr_use_visitor.rs | 9 ++++---- .../rustc_hir_typeck/src/fn_ctxt/_impl.rs | 8 ++++--- compiler/rustc_hir_typeck/src/place_op.rs | 7 +++--- compiler/rustc_lint/src/autorefs.rs | 10 ++++++--- compiler/rustc_lint/src/noop_method_call.rs | 7 ++++-- compiler/rustc_middle/src/ty/adjustment.rs | 8 ++++++- compiler/rustc_mir_build/src/thir/cx/expr.rs | 6 ++--- .../error_reporting/infer/need_type_info.rs | 4 ++-- .../clippy/clippy_lints/src/eta_reduction.rs | 4 ++-- .../clippy/clippy_lints/src/format_args.rs | 6 ++--- .../src/loops/while_let_on_iterator.rs | 4 ++-- .../clippy_lints/src/methods/map_clone.rs | 4 ++-- .../src/methods/option_as_ref_deref.rs | 2 +- .../src/methods/swap_with_temporary.rs | 4 ++-- .../src/methods/type_id_on_box.rs | 4 ++-- .../src/methods/unnecessary_to_owned.rs | 10 ++++----- .../clippy_lints/src/methods/useless_asref.rs | 4 ++-- .../clippy/clippy_lints/src/non_copy_const.rs | 4 ++-- .../clippy/clippy_utils/src/eager_or_lazy.rs | 11 +++------- src/tools/clippy/clippy_utils/src/lib.rs | 10 +++++---- src/tools/clippy/clippy_utils/src/ty/mod.rs | 5 +++-- 23 files changed, 90 insertions(+), 72 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/autoderef.rs b/compiler/rustc_hir_typeck/src/autoderef.rs index 4fe77278706e..f7700179d2a3 100644 --- a/compiler/rustc_hir_typeck/src/autoderef.rs +++ b/compiler/rustc_hir_typeck/src/autoderef.rs @@ -6,7 +6,7 @@ use itertools::Itertools; use rustc_hir_analysis::autoderef::{Autoderef, AutoderefKind}; use rustc_infer::infer::InferOk; use rustc_infer::traits::PredicateObligations; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{self, Ty}; use rustc_span::Span; @@ -45,22 +45,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty())); let steps: Vec<_> = steps .iter() - .map(|&(source, kind)| { - if let AutoderefKind::Overloaded = kind { - self.try_overloaded_deref(autoderef.span(), source).and_then( - |InferOk { value: method, obligations: o }| { + .map(|&(source, kind)| match kind { + AutoderefKind::Overloaded => { + self.try_overloaded_deref(autoderef.span(), source) + .and_then(|InferOk { value: method, obligations: o }| { obligations.extend(o); // FIXME: we should assert the sig is &T here... there's no reason for this to be fallible. if let ty::Ref(_, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { mutbl, span: autoderef.span() }) + Some(DerefAdjustKind::Overloaded(OverloadedDeref { + mutbl, + span: autoderef.span(), + })) } else { None } - }, - ) - } else { - None + }) + .unwrap_or(DerefAdjustKind::Builtin) } + AutoderefKind::Builtin => DerefAdjustKind::Builtin, }) .zip_eq(targets) .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) diff --git a/compiler/rustc_hir_typeck/src/coercion.rs b/compiler/rustc_hir_typeck/src/coercion.rs index 0e87b90dc6c1..36a07b361d9d 100644 --- a/compiler/rustc_hir_typeck/src/coercion.rs +++ b/compiler/rustc_hir_typeck/src/coercion.rs @@ -50,7 +50,8 @@ use rustc_infer::traits::{ }; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + PointerCoercion, }; use rustc_middle::ty::error::TypeError; use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt}; @@ -595,7 +596,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { let mutbl = AutoBorrowMutability::new(mutbl_b, AllowTwoPhase::No); Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(mutbl)), target: Ty::new_ref(self.tcx, r_borrow, ty_a, mutbl_b), @@ -606,7 +607,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { coerce_mutbls(mt_a, mt_b)?; Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: ty_a }, Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), target: Ty::new_ptr(self.tcx, ty_a, mt_b), @@ -936,7 +937,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and( a_raw, b, - [Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }], + [Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: mt_a.ty }], Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), ForceLeakCheck::No, ) diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index ad34994526ed..171184d3a562 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -22,6 +22,7 @@ use rustc_middle::hir::place::ProjectionKind; pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; use rustc_middle::mir::FakeReadCause; use rustc_middle::thir::DerefPatBorrowMode; +use rustc_middle::ty::adjustment::DerefAdjustKind; use rustc_middle::ty::{ self, BorrowKind, Ty, TyCtxt, TypeFoldable, TypeVisitableExt as _, adjustment, }; @@ -733,14 +734,14 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx self.consume_or_copy(&place_with_id, place_with_id.hir_id); } - adjustment::Adjust::Deref(None) => {} + adjustment::Adjust::Deref(DerefAdjustKind::Builtin) => {} // Autoderefs for overloaded Deref calls in fact reference // their receiver. That is, if we have `(*x)` where `x` // is of type `Rc`, then this in fact is equivalent to // `x.deref()`. Since `deref()` is declared with `&self`, // this is an autoref of `x`. - adjustment::Adjust::Deref(Some(ref deref)) => { + adjustment::Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { let bk = ty::BorrowKind::from_mutbl(deref.mutbl); self.delegate.borrow_mut().borrow(&place_with_id, place_with_id.hir_id, bk); } @@ -1272,9 +1273,9 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx { let target = self.cx.resolve_vars_if_possible(adjustment.target); match adjustment.kind { - adjustment::Adjust::Deref(overloaded) => { + adjustment::Adjust::Deref(deref_kind) => { // Equivalent to *expr or something similar. - let base = if let Some(deref) = overloaded { + let base = if let DerefAdjustKind::Overloaded(deref) = deref_kind { let ref_ty = Ty::new_ref( self.cx.tcx(), self.cx.tcx().lifetimes.re_erased, diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index 91f91d911444..67007523a067 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -20,7 +20,9 @@ use rustc_hir_analysis::hir_ty_lowering::{ use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, +}; use rustc_middle::ty::{ self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, SizedTraitKind, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, @@ -266,7 +268,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target); } } - Adjust::Deref(Some(overloaded_deref)) => { + Adjust::Deref(DerefAdjustKind::Overloaded(overloaded_deref)) => { self.enforce_context_effects( None, expr.span, @@ -274,7 +276,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx.mk_args(&[expr_ty.into()]), ); } - Adjust::Deref(None) => { + Adjust::Deref(DerefAdjustKind::Builtin) => { // FIXME(const_trait_impl): We *could* enforce `&T: [const] Deref` here. } Adjust::Pointer(_pointer_coercion) => { diff --git a/compiler/rustc_hir_typeck/src/place_op.rs b/compiler/rustc_hir_typeck/src/place_op.rs index a48db2cc855c..c33f8bdaffe2 100644 --- a/compiler/rustc_hir_typeck/src/place_op.rs +++ b/compiler/rustc_hir_typeck/src/place_op.rs @@ -4,8 +4,8 @@ use rustc_infer::infer::InferOk; use rustc_infer::traits::{Obligation, ObligationCauseCode}; use rustc_middle::span_bug; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, OverloadedDeref, - PointerCoercion, + Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, + OverloadedDeref, PointerCoercion, }; use rustc_middle::ty::{self, Ty}; use rustc_span::{Span, sym}; @@ -298,7 +298,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id); if let Some(mut adjustments) = previous_adjustments { for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind + if let Adjust::Deref(DerefAdjustKind::Overloaded(ref mut deref)) = + adjustment.kind && let Some(ok) = self.try_mutable_overloaded_place_op( expr.span, source, diff --git a/compiler/rustc_lint/src/autorefs.rs b/compiler/rustc_lint/src/autorefs.rs index ed7ac0e33244..5bb7df80ffb3 100644 --- a/compiler/rustc_lint/src/autorefs.rs +++ b/compiler/rustc_lint/src/autorefs.rs @@ -1,7 +1,9 @@ use rustc_ast::{BorrowKind, UnOp}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{Expr, ExprKind, Mutability, find_attr}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, OverloadedDeref}; +use rustc_middle::ty::adjustment::{ + Adjust, Adjustment, AutoBorrow, DerefAdjustKind, OverloadedDeref, +}; use rustc_session::{declare_lint, declare_lint_pass}; use crate::lints::{ @@ -165,12 +167,14 @@ fn peel_derefs_adjustments<'a>(mut adjs: &'a [Adjustment<'a>]) -> &'a [Adjustmen /// an implicit borrow (or has an implicit borrow via an overloaded deref). fn has_implicit_borrow(Adjustment { kind, .. }: &Adjustment<'_>) -> Option<(Mutability, bool)> { match kind { - &Adjust::Deref(Some(OverloadedDeref { mutbl, .. })) => Some((mutbl, true)), + &Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { mutbl, .. })) => { + Some((mutbl, true)) + } &Adjust::Borrow(AutoBorrow::Ref(mutbl)) => Some((mutbl.into(), false)), Adjust::NeverToAny | Adjust::Pointer(..) | Adjust::ReborrowPin(..) - | Adjust::Deref(None) + | Adjust::Deref(DerefAdjustKind::Builtin) | Adjust::Borrow(AutoBorrow::RawPtr(..)) => None, } } diff --git a/compiler/rustc_lint/src/noop_method_call.rs b/compiler/rustc_lint/src/noop_method_call.rs index 24682c4562a0..66b4e3c16e93 100644 --- a/compiler/rustc_lint/src/noop_method_call.rs +++ b/compiler/rustc_lint/src/noop_method_call.rs @@ -1,7 +1,7 @@ use rustc_hir::def::DefKind; use rustc_hir::{Expr, ExprKind}; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_session::{declare_lint, declare_lint_pass}; use rustc_span::sym; @@ -114,7 +114,10 @@ impl<'tcx> LateLintPass<'tcx> for NoopMethodCall { // If there is any user defined auto-deref step, then we don't want to warn. // https://github.com/rust-lang/rust-clippy/issues/9272 - if arg_adjustments.iter().any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) { + if arg_adjustments + .iter() + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) + { return; } diff --git a/compiler/rustc_middle/src/ty/adjustment.rs b/compiler/rustc_middle/src/ty/adjustment.rs index c806366b518a..6d546aede4cf 100644 --- a/compiler/rustc_middle/src/ty/adjustment.rs +++ b/compiler/rustc_middle/src/ty/adjustment.rs @@ -97,7 +97,7 @@ pub enum Adjust { NeverToAny, /// Dereference once, producing a place. - Deref(Option), + Deref(DerefAdjustKind), /// Take the address and produce either a `&` or `*` pointer. Borrow(AutoBorrow), @@ -108,6 +108,12 @@ pub enum Adjust { ReborrowPin(hir::Mutability), } +#[derive(Copy, Clone, Debug, TyEncodable, TyDecodable, HashStable, TypeFoldable, TypeVisitable)] +pub enum DerefAdjustKind { + Builtin, + Overloaded(OverloadedDeref), +} + /// An overloaded autoderef step, representing a `Deref(Mut)::deref(_mut)` /// call, with the signature `&'a T -> &'a U` or `&'a mut T -> &'a mut U`. /// The target type is `U` in both cases, with the region and mutability diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 8e02424706ee..0117a10e3a8c 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -14,7 +14,7 @@ use rustc_middle::middle::region; use rustc_middle::mir::{self, AssignOp, BinOp, BorrowKind, UnOp}; use rustc_middle::thir::*; use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, PointerCoercion, + Adjust, Adjustment, AutoBorrow, AutoBorrowMutability, DerefAdjustKind, PointerCoercion, }; use rustc_middle::ty::{ self, AdtKind, GenericArgs, InlineConstArgs, InlineConstArgsParts, ScalarInt, Ty, UpvarArgs, @@ -140,11 +140,11 @@ impl<'tcx> ThirBuildCx<'tcx> { } Adjust::NeverToAny if adjustment.target.is_never() => return expr, Adjust::NeverToAny => ExprKind::NeverToAny { source: self.thir.exprs.push(expr) }, - Adjust::Deref(None) => { + Adjust::Deref(DerefAdjustKind::Builtin) => { adjust_span(&mut expr); ExprKind::Deref { arg: self.thir.exprs.push(expr) } } - Adjust::Deref(Some(deref)) => { + Adjust::Deref(DerefAdjustKind::Overloaded(deref)) => { // We don't need to do call adjust_span here since // deref coercions always start with a built-in deref. let call_def_id = deref.method_call(self.tcx); diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index e3c8bfe4a452..ca846bca874e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -12,7 +12,7 @@ use rustc_hir::{ }; use rustc_middle::bug; use rustc_middle::hir::nested_filter; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind, @@ -615,7 +615,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // first adjustment was not a builtin deref. let adjustment = match typeck_results.expr_adjustments(receiver) { [ - Adjustment { kind: Adjust::Deref(None), target: _ }, + Adjustment { kind: Adjust::Deref(DerefAdjustKind::Builtin), target: _ }, .., Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), target: _ }, ] => "", diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs index 21385ee4fdc7..3e46c370fb70 100644 --- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs +++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs @@ -10,7 +10,7 @@ use rustc_hir::attrs::AttributeKind; use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, GenericArgs, Param, PatKind, QPath, Safety, TyKind, find_attr}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::{LateContext, LateLintPass}; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{ self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults, }; @@ -236,7 +236,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx .iter() .rev() .fold(0, |acc, adjustment| match adjustment.kind { - Adjust::Deref(Some(_)) => acc + 1, + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => acc + 1, Adjust::Deref(_) if acc > 0 => acc + 1, _ => acc, }) diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs index 011cbf8c5d41..5fb1a0b80f1a 100644 --- a/src/tools/clippy/clippy_lints/src/format_args.rs +++ b/src/tools/clippy/clippy_lints/src/format_args.rs @@ -24,7 +24,7 @@ use rustc_errors::SuggestionStyle::{CompletelyHidden, ShowCode}; use rustc_hir::attrs::AttributeKind; use rustc_hir::{Expr, ExprKind, LangItem, RustcVersion, find_attr}; use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{self, GenericArg, List, TraitRef, Ty, TyCtxt, Upcast}; use rustc_session::impl_lint_pass; use rustc_span::edition::Edition::Edition2021; @@ -704,12 +704,12 @@ where let mut n_needed = 0; loop { if let Some(Adjustment { - kind: Adjust::Deref(overloaded_deref), + kind: Adjust::Deref(deref), target, }) = iter.next() { n_total += 1; - if overloaded_deref.is_some() { + if let DerefAdjustKind::Overloaded(..) = deref { n_needed = n_total; } ty = *target; diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs index 6c95c7b54c50..fc0789894cc2 100644 --- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs +++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs @@ -12,7 +12,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{Closure, Expr, ExprKind, HirId, LetStmt, Mutability, UnOp}; use rustc_lint::LateContext; use rustc_middle::hir::nested_filter::OnlyBodies; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use rustc_span::symbol::sym; @@ -88,7 +88,7 @@ fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option, e: &hir::Expr<'_>, recv: &hir::Expr<'_ && cx.tcx.lang_items().clone_trait() == Some(trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) { let obj_ty = cx.typeck_results().expr_ty(obj); if let ty::Ref(_, ty, mutability) = obj_ty.kind() { diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs index 3d489075ce8a..1239d8927acf 100644 --- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs @@ -58,7 +58,7 @@ pub(super) fn check( .iter() .map(|x| &x.kind) .collect::>() - && let [ty::adjustment::Adjust::Deref(None), ty::adjustment::Adjust::Borrow(_)] = *adj + && let [ty::adjustment::Adjust::Deref(ty::adjustment::DerefAdjustKind::Builtin), ty::adjustment::Adjust::Borrow(_)] = *adj && let method_did = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id).unwrap() && let Some(method_name) = cx.tcx.get_diagnostic_name(method_did) { diff --git a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs index e378cbd6ae0a..0b8f6ae9f277 100644 --- a/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs +++ b/src/tools/clippy/clippy_lints/src/methods/swap_with_temporary.rs @@ -4,7 +4,7 @@ use rustc_ast::BorrowKind; use rustc_errors::{Applicability, Diag}; use rustc_hir::{Expr, ExprKind, Node, QPath}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::sym; use super::SWAP_WITH_TEMPORARY; @@ -47,7 +47,7 @@ impl<'tcx> ArgKind<'tcx> { && let adjustments = cx.typeck_results().expr_adjustments(arg) && adjustments .first() - .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(None))) + .is_some_and(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) && adjustments .last() .is_some_and(|adj| matches!(adj.kind, Adjust::Borrow(_))) diff --git a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs index e67ba5c4d314..98f319be7a45 100644 --- a/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs +++ b/src/tools/clippy/clippy_lints/src/methods/type_id_on_box.rs @@ -4,7 +4,7 @@ use clippy_utils::source::snippet; use rustc_errors::Applicability; use rustc_hir::Expr; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::print::with_forced_trimmed_paths; use rustc_middle::ty::{self, ExistentialPredicate, Ty}; use rustc_span::{Span, sym}; @@ -58,7 +58,7 @@ pub(super) fn check(cx: &LateContext<'_>, receiver: &Expr<'_>, call_span: Span) |diag| { let derefs = recv_adjusts .iter() - .filter(|adj| matches!(adj.kind, Adjust::Deref(None))) + .filter(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Builtin))) .count(); diag.note( diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs index 74e8dbc15a6c..607aaef9627b 100644 --- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs +++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs @@ -14,7 +14,7 @@ use rustc_hir::{BorrowKind, Expr, ExprKind, ItemKind, LangItem, Node}; use rustc_infer::infer::TyCtxtInferExt; use rustc_lint::LateContext; use rustc_middle::mir::Mutability; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind, OverloadedDeref}; use rustc_middle::ty::{ self, ClauseKind, GenericArg, GenericArgKind, GenericArgsRef, ParamTy, ProjectionPredicate, TraitPredicate, Ty, }; @@ -78,7 +78,7 @@ fn check_addr_of_expr( // For matching uses of `Cow::from` [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -89,7 +89,7 @@ fn check_addr_of_expr( // For matching uses of arrays | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { @@ -104,11 +104,11 @@ fn check_addr_of_expr( // For matching everything else | [ Adjustment { - kind: Adjust::Deref(None), + kind: Adjust::Deref(DerefAdjustKind::Builtin), target: referent_ty, }, Adjustment { - kind: Adjust::Deref(Some(OverloadedDeref { .. })), + kind: Adjust::Deref(DerefAdjustKind::Overloaded(OverloadedDeref { .. })), .. }, Adjustment { diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs index f852080d0f2a..3737249a40cb 100644 --- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs +++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs @@ -7,7 +7,7 @@ use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; use rustc_hir::{self as hir, LangItem, Node}; use rustc_lint::LateContext; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor}; use rustc_span::{Span, Symbol, sym}; @@ -161,7 +161,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool { && cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id) // no autoderefs && !cx.typeck_results().expr_adjustments(obj).iter() - .any(|a| matches!(a.kind, Adjust::Deref(Some(..)))) + .any(|a| matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(..)))) && obj.res_local_id() == Some(local_id) { true diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs index 9b0008a29c6b..f99748127a8f 100644 --- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs +++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs @@ -33,7 +33,7 @@ use rustc_hir::{ }; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_middle::mir::{ConstValue, UnevaluatedConst}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::{ self, AliasTyKind, EarlyBinder, GenericArgs, GenericArgsRef, Instance, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeckResults, TypingEnv, @@ -907,7 +907,7 @@ fn does_adjust_borrow(adjust: &Adjustment<'_>) -> Option { match adjust.kind { Adjust::Borrow(_) => Some(BorrowCause::AutoBorrow), // Custom deref calls `::deref(&x)` resulting in a borrow. - Adjust::Deref(Some(_)) => Some(BorrowCause::AutoDeref), + Adjust::Deref(DerefAdjustKind::Overloaded(_)) => Some(BorrowCause::AutoDeref), // All other adjustments read the value. _ => None, } diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs index 2bdd5739a557..d184744162e4 100644 --- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs +++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs @@ -19,7 +19,7 @@ use rustc_hir::intravisit::{Visitor, walk_expr}; use rustc_hir::{BinOpKind, Block, Expr, ExprKind, QPath, UnOp}; use rustc_lint::LateContext; use rustc_middle::ty; -use rustc_middle::ty::adjustment::Adjust; +use rustc_middle::ty::adjustment::{Adjust, DerefAdjustKind}; use rustc_span::Symbol; use std::{cmp, ops}; @@ -132,7 +132,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS .typeck_results() .expr_adjustments(e) .iter() - .any(|adj| matches!(adj.kind, Adjust::Deref(Some(_)))) + .any(|adj| matches!(adj.kind, Adjust::Deref(DerefAdjustKind::Overloaded(_)))) { self.eagerness |= NoChange; return; @@ -211,12 +211,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS // Custom `Deref` impl might have side effects ExprKind::Unary(UnOp::Deref, e) - if self - .cx - .typeck_results() - .expr_ty(e) - .builtin_deref(true) - .is_none() => + if self.cx.typeck_results().expr_ty(e).builtin_deref(true).is_none() => { self.eagerness |= NoChange; }, diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs index 6b10f4b51442..16bedf199e20 100644 --- a/src/tools/clippy/clippy_utils/src/lib.rs +++ b/src/tools/clippy/clippy_utils/src/lib.rs @@ -108,7 +108,7 @@ use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, PointerCoercion}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, DerefAdjustKind, PointerCoercion}; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, @@ -477,8 +477,8 @@ pub fn expr_custom_deref_adjustment(cx: &LateContext<'_>, e: &Expr<'_>) -> Optio .expr_adjustments(e) .iter() .find_map(|a| match a.kind { - Adjust::Deref(Some(d)) => Some(Some(d.mutbl)), - Adjust::Deref(None) => None, + Adjust::Deref(DerefAdjustKind::Overloaded(d)) => Some(Some(d.mutbl)), + Adjust::Deref(DerefAdjustKind::Builtin) => None, _ => Some(None), }) .and_then(|x| x) @@ -3537,7 +3537,9 @@ pub fn expr_adjustment_requires_coercion(cx: &LateContext<'_>, expr: &Expr<'_>) cx.typeck_results().expr_adjustments(expr).iter().any(|adj| { matches!( adj.kind, - Adjust::Deref(Some(_)) | Adjust::Pointer(PointerCoercion::Unsize) | Adjust::NeverToAny + Adjust::Deref(DerefAdjustKind::Overloaded(_)) + | Adjust::Pointer(PointerCoercion::Unsize) + | Adjust::NeverToAny ) }) } diff --git a/src/tools/clippy/clippy_utils/src/ty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/mod.rs index 0f11df98fc17..46456528fdf8 100644 --- a/src/tools/clippy/clippy_utils/src/ty/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ty/mod.rs @@ -17,7 +17,7 @@ use rustc_lint::LateContext; use rustc_middle::mir::ConstValue; use rustc_middle::mir::interpret::Scalar; use rustc_middle::traits::EvaluationResult; -use rustc_middle::ty::adjustment::{Adjust, Adjustment}; +use rustc_middle::ty::adjustment::{Adjust, Adjustment, DerefAdjustKind}; use rustc_middle::ty::layout::ValidityRequirement; use rustc_middle::ty::{ self, AdtDef, AliasTy, AssocItem, AssocTag, Binder, BoundRegion, BoundVarIndexKind, FnSig, GenericArg, @@ -1345,6 +1345,7 @@ pub fn get_field_idx_by_name(ty: Ty<'_>, name: Symbol) -> Option { pub fn adjust_derefs_manually_drop<'tcx>(adjustments: &'tcx [Adjustment<'tcx>], mut ty: Ty<'tcx>) -> bool { adjustments.iter().any(|a| { let ty = mem::replace(&mut ty, a.target); - matches!(a.kind, Adjust::Deref(Some(op)) if op.mutbl == Mutability::Mut) && is_manually_drop(ty) + matches!(a.kind, Adjust::Deref(DerefAdjustKind::Overloaded(op)) if op.mutbl == Mutability::Mut) + && is_manually_drop(ty) }) } From 565b772fa5ed898981f7db684c2756338e8daa77 Mon Sep 17 00:00:00 2001 From: ThanhNguyxn Date: Thu, 22 Jan 2026 18:28:54 +0700 Subject: [PATCH 177/319] Add regression test for ICE with undefined type in async fn --- tests/ui/mir/gvn-opt-138225.rs | 16 ++++++++++++++++ tests/ui/mir/gvn-opt-138225.stderr | 19 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/ui/mir/gvn-opt-138225.rs create mode 100644 tests/ui/mir/gvn-opt-138225.stderr diff --git a/tests/ui/mir/gvn-opt-138225.rs b/tests/ui/mir/gvn-opt-138225.rs new file mode 100644 index 000000000000..46359e044e2e --- /dev/null +++ b/tests/ui/mir/gvn-opt-138225.rs @@ -0,0 +1,16 @@ +//! Regression test for + +pub struct A { + name: NestedOption>, + //~^ ERROR cannot find type `NestedOption` in this scope +} + +impl A { + pub async fn func1() -> &'static A { + //~^ ERROR `async fn` is not permitted in Rust 2015 + static RES: A = A { name: None }; + &RES + } +} + +fn main() {} diff --git a/tests/ui/mir/gvn-opt-138225.stderr b/tests/ui/mir/gvn-opt-138225.stderr new file mode 100644 index 000000000000..b2e3d4476bf8 --- /dev/null +++ b/tests/ui/mir/gvn-opt-138225.stderr @@ -0,0 +1,19 @@ +error[E0670]: `async fn` is not permitted in Rust 2015 + --> $DIR/gvn-opt-138225.rs:9:9 + | +LL | pub async fn func1() -> &'static A { + | ^^^^^ to use `async fn`, switch to Rust 2018 or later + | + = help: pass `--edition 2024` to `rustc` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0425]: cannot find type `NestedOption` in this scope + --> $DIR/gvn-opt-138225.rs:4:11 + | +LL | name: NestedOption>, + | ^^^^^^^^^^^^ not found in this scope + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0425, E0670. +For more information about an error, try `rustc --explain E0425`. From dc48704f98f77822e8f6aea7fdac255c5aea101f Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 31 Jan 2026 02:05:07 +0000 Subject: [PATCH 178/319] Fix ICE when parsing frontmatter without newline --- compiler/rustc_parse/src/lexer/mod.rs | 2 +- .../frontmatter-no-trailing-newline/rmake.rs | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 tests/run-make/frontmatter-no-trailing-newline/rmake.rs diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f9bf50de091a..76f610df1eb0 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -623,7 +623,7 @@ impl<'psess, 'src> Lexer<'psess, 'src> { self.dcx().emit_err(errors::FrontmatterInvalidInfostring { span }); } - let last_line_start = real_s.rfind('\n').map_or(0, |i| i + 1); + let last_line_start = real_s.rfind('\n').map_or(line_end, |i| i + 1); let content = &real_s[line_end..last_line_start]; if let Some(cr_offset) = content.find('\r') { diff --git a/tests/run-make/frontmatter-no-trailing-newline/rmake.rs b/tests/run-make/frontmatter-no-trailing-newline/rmake.rs new file mode 100644 index 000000000000..204062201ad3 --- /dev/null +++ b/tests/run-make/frontmatter-no-trailing-newline/rmake.rs @@ -0,0 +1,19 @@ +// Regression test for issue #151882 +// See https://github.com/rust-lang/rust/issues/151882 + +//@ only-nightly +//@ needs-target-std + +use run_make_support::{rfs, rustc}; + +fn main() { + rfs::write("test.rs", b"----"); + + // Ensure rustc does not ICE when parsing a file with frontmatter syntax + // that has no trailing newline + rustc() + .input("test.rs") + .run_fail() + .assert_stderr_contains("invalid infostring for frontmatter") + .assert_stderr_not_contains("unexpectedly panicked"); +} From 1689fcd1cc8a7288b0d3ae88211549ad8de9df7d Mon Sep 17 00:00:00 2001 From: kouhe3 <25522053+kouhe3@users.noreply.github.com> Date: Thu, 29 Jan 2026 17:23:06 +0800 Subject: [PATCH 179/319] feat(windows): add Unix domain socket support This commit introduces initial, unstable support for Unix domain sockets (UDS) on Windows, behind the `windows_unix_domain_sockets` feature gate Added types: - `std::os::windows::net::SocketAddr`: represents a UDS address with support for pathname addresses (abstract and unnamed are parsed but not yet fully supported). - `std::os::windows::net::UnixListener`: server-side UDS listener. - `std::os::windows::net::UnixStream`: client/server stream for UDS. Key features: - Binding and connecting using filesystem paths. - Basic I/O via `Read`/`Write`. - Address querying (`local_addr`, `peer_addr`). - Non-blocking mode, timeouts, and socket duplication. - Includes basic test coverage for smoke, echo, path length, and bind reuse. --- library/std/src/os/windows/mod.rs | 2 + library/std/src/os/windows/net/addr.rs | 173 +++++++++ library/std/src/os/windows/net/listener.rs | 342 +++++++++++++++++ library/std/src/os/windows/net/mod.rs | 6 + library/std/src/os/windows/net/stream.rs | 421 +++++++++++++++++++++ library/std/src/sys/pal/windows/mod.rs | 5 + library/std/tests/windows_unix_socket.rs | 86 +++++ 7 files changed, 1035 insertions(+) create mode 100644 library/std/src/os/windows/net/addr.rs create mode 100644 library/std/src/os/windows/net/listener.rs create mode 100644 library/std/src/os/windows/net/mod.rs create mode 100644 library/std/src/os/windows/net/stream.rs create mode 100644 library/std/tests/windows_unix_socket.rs diff --git a/library/std/src/os/windows/mod.rs b/library/std/src/os/windows/mod.rs index f452403ee842..53c33d17a9f6 100644 --- a/library/std/src/os/windows/mod.rs +++ b/library/std/src/os/windows/mod.rs @@ -29,6 +29,8 @@ pub mod ffi; pub mod fs; pub mod io; +#[unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +pub mod net; pub mod process; pub mod raw; pub mod thread; diff --git a/library/std/src/os/windows/net/addr.rs b/library/std/src/os/windows/net/addr.rs new file mode 100644 index 000000000000..ef2263edcf61 --- /dev/null +++ b/library/std/src/os/windows/net/addr.rs @@ -0,0 +1,173 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::bstr::ByteStr; +use crate::ffi::OsStr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCKADDR, SOCKADDR_UN}; +use crate::sys::cvt_nz; +use crate::{fmt, io, mem, ptr}; + +#[cfg(not(doc))] +pub fn sockaddr_un(path: &Path) -> io::Result<(SOCKADDR_UN, usize)> { + // SAFETY: All zeros is a valid representation for `sockaddr_un`. + let mut addr: SOCKADDR_UN = unsafe { mem::zeroed() }; + addr.sun_family = AF_UNIX; + + // path to UTF-8 bytes + let bytes = path + .to_str() + .ok_or(io::const_error!(io::ErrorKind::InvalidInput, "path must be valid UTF-8"))? + .as_bytes(); + if bytes.len() >= addr.sun_path.len() { + return Err(io::const_error!(io::ErrorKind::InvalidInput, "path too long")); + } + // SAFETY: `bytes` and `addr.sun_path` are not overlapping and + // both point to valid memory. + // NOTE: We zeroed the memory above, so the path is already null + // terminated. + unsafe { + ptr::copy_nonoverlapping(bytes.as_ptr(), addr.sun_path.as_mut_ptr().cast(), bytes.len()) + }; + + let len = SUN_PATH_OFFSET + bytes.len() + 1; + Ok((addr, len)) +} +#[cfg(not(doc))] +const SUN_PATH_OFFSET: usize = mem::offset_of!(SOCKADDR_UN, sun_path); +pub struct SocketAddr { + #[cfg(not(doc))] + pub(super) addr: SOCKADDR_UN, + pub(super) len: u32, // Use u32 here as same as libc::socklen_t +} +impl fmt::Debug for SocketAddr { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.address() { + AddressKind::Unnamed => write!(fmt, "(unnamed)"), + AddressKind::Abstract(name) => write!(fmt, "{name:?} (abstract)"), + AddressKind::Pathname(path) => write!(fmt, "{path:?} (pathname)"), + } + } +} + +impl SocketAddr { + #[cfg(not(doc))] + pub(super) fn new(f: F) -> io::Result + where + F: FnOnce(*mut SOCKADDR, *mut i32) -> i32, + { + unsafe { + let mut addr: SOCKADDR_UN = mem::zeroed(); + let mut len = mem::size_of::() as i32; + cvt_nz(f(&raw mut addr as *mut _, &mut len))?; + SocketAddr::from_parts(addr, len) + } + } + #[cfg(not(doc))] + pub(super) fn from_parts(addr: SOCKADDR_UN, len: i32) -> io::Result { + if addr.sun_family != AF_UNIX { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address family")) + } else if len < SUN_PATH_OFFSET as _ || len > mem::size_of::() as _ { + Err(io::const_error!(io::ErrorKind::InvalidInput, "invalid address length")) + } else { + Ok(SocketAddr { addr, len: len as _ }) + } + } + + /// Returns the contents of this address if it is a `pathname` address. + /// + /// # Examples + /// + /// With a pathname: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// use std::path::Path; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.as_pathname(), Some(Path::new("/tmp/sock"))); + /// Ok(()) + /// } + /// ``` + pub fn as_pathname(&self) -> Option<&Path> { + if let AddressKind::Pathname(path) = self.address() { Some(path) } else { None } + } + + /// Constructs a `SockAddr` with the family `AF_UNIX` and the provided path. + /// + /// # Errors + /// + /// Returns an error if the path is longer than `SUN_LEN` or if it contains + /// NULL bytes. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// use std::path::Path; + /// + /// # fn main() -> std::io::Result<()> { + /// let address = SocketAddr::from_pathname("/path/to/socket")?; + /// assert_eq!(address.as_pathname(), Some(Path::new("/path/to/socket"))); + /// # Ok(()) + /// # } + /// ``` + /// + /// Creating a `SocketAddr` with a NULL byte results in an error. + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::SocketAddr; + /// + /// assert!(SocketAddr::from_pathname("/path/with/\0/bytes").is_err()); + /// ``` + pub fn from_pathname

(path: P) -> io::Result + where + P: AsRef, + { + sockaddr_un(path.as_ref()).map(|(addr, len)| SocketAddr { addr, len: len as _ }) + } + fn address(&self) -> AddressKind<'_> { + let len = self.len as usize - SUN_PATH_OFFSET; + let path = unsafe { mem::transmute::<&[i8], &[u8]>(&self.addr.sun_path) }; + + if len == 0 { + AddressKind::Unnamed + } else if self.addr.sun_path[0] == 0 { + AddressKind::Abstract(ByteStr::from_bytes(&path[1..len])) + } else { + AddressKind::Pathname(unsafe { + OsStr::from_encoded_bytes_unchecked(&path[..len - 1]).as_ref() + }) + } + } + + /// Returns `true` if the address is unnamed. + /// + /// # Examples + /// + /// A named address: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixListener::bind("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// assert_eq!(addr.is_unnamed(), false); + /// Ok(()) + /// } + /// ``` + pub fn is_unnamed(&self) -> bool { + matches!(self.address(), AddressKind::Unnamed) + } +} +enum AddressKind<'a> { + Unnamed, + Pathname(&'a Path), + Abstract(&'a ByteStr), +} diff --git a/library/std/src/os/windows/net/listener.rs b/library/std/src/os/windows/net/listener.rs new file mode 100644 index 000000000000..332b116ee1a3 --- /dev/null +++ b/library/std/src/os/windows/net/listener.rs @@ -0,0 +1,342 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket}; +use crate::os::windows::net::{SocketAddr, UnixStream}; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{AF_UNIX, SOCK_STREAM, SOCKADDR_UN, bind, getsockname, listen}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::{fmt, io}; + +/// A structure representing a Unix domain socket server. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// // accept connections and process them, spawning a new thread for each one +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// /* connection succeeded */ +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// /* connection failed */ +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct UnixListener(Socket); + +impl fmt::Debug for UnixListener { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixListener"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + builder.finish() + } +} +impl UnixListener { + /// Creates a new `UnixListener` bound to the specified socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// let listener = match UnixListener::bind("/path/to/the/socket") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn bind>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::bind_addr(&socket_addr) + } + + /// Creates a new `UnixListener` bound to the specified [`socket address`]. + /// + /// [`socket address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener1 = UnixListener::bind("path/to/socket")?; + /// let addr = listener1.local_addr()?; + /// + /// let listener2 = match UnixListener::bind_addr(&addr) { + /// Ok(sock) => sock, + /// Err(err) => { + /// println!("Couldn't bind: {err:?}"); + /// return Err(err); + /// } + /// }; + /// Ok(()) + /// } + /// ``` + pub fn bind_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(bind(inner.as_raw(), &raw const socket_addr.addr as _, socket_addr.len as _))?; + cvt_nz(listen(inner.as_raw(), 128))?; + } + Ok(UnixListener(inner)) + } + + /// Accepts a new incoming connection to this listener. + /// + /// This function will block the calling thread until a new Unix connection + /// is established. When established, the corresponding [`UnixStream`] and + /// the remote peer's address will be returned. + /// + /// [`UnixStream`]: crate::os::windows::net::UnixStream + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// match listener.accept() { + /// Ok((socket, addr)) => println!("Got a client: {addr:?}"), + /// Err(e) => println!("accept function failed: {e:?}"), + /// } + /// Ok(()) + /// } + /// ``` + pub fn accept(&self) -> io::Result<(UnixStream, SocketAddr)> { + let mut storage = SOCKADDR_UN::default(); + let mut len = size_of::() as _; + let inner = self.0.accept(&raw mut storage as *mut _, &raw mut len)?; + let addr = SocketAddr::from_parts(storage, len)?; + Ok((UnixStream(inner), addr)) + } + + /// Returns the local socket address of this listener. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixListener` is a reference to the same socket that this + /// object references. Both handles can be used to accept incoming + /// connections and options set on one listener will affect the other. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let listener_copy = listener.try_clone().expect("try_clone failed"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixListener) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// This will result in the `accept` operation becoming nonblocking, + /// i.e., immediately returning from their calls. If the IO operation is + /// successful, `Ok` is returned and no further action is required. If the + /// IO operation could not be completed and needs to be retried, an error + /// with kind [`io::ErrorKind::WouldBlock`] is returned. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// listener.set_nonblocking(true).expect("Couldn't set non blocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixListener; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/tmp/sock")?; + /// + /// if let Ok(Some(err)) = listener.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Returns an iterator over incoming connections. + /// + /// The iterator will never return [`None`] and will also not yield the + /// peer's [`SocketAddr`] structure. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::thread; + /// use std::os::windows::net::{UnixStream, UnixListener}; + /// + /// fn handle_client(stream: UnixStream) { + /// // ... + /// } + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// + /// for stream in listener.incoming() { + /// match stream { + /// Ok(stream) => { + /// thread::spawn(|| handle_client(stream)); + /// } + /// Err(err) => { + /// break; + /// } + /// } + /// } + /// Ok(()) + /// } + /// ``` + pub fn incoming(&self) -> Incoming<'_> { + Incoming { listener: self } + } +} + +/// An iterator over incoming connections to a [`UnixListener`]. +/// +/// It will never return [`None`]. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::thread; +/// use std::os::windows::net::{UnixStream, UnixListener}; +/// +/// fn handle_client(stream: UnixStream) { +/// // ... +/// } +/// +/// fn main() -> std::io::Result<()> { +/// let listener = UnixListener::bind("/path/to/the/socket")?; +/// +/// for stream in listener.incoming() { +/// match stream { +/// Ok(stream) => { +/// thread::spawn(|| handle_client(stream)); +/// } +/// Err(err) => { +/// break; +/// } +/// } +/// } +/// Ok(()) +/// } +/// ``` +pub struct Incoming<'a> { + listener: &'a UnixListener, +} + +impl<'a> Iterator for Incoming<'a> { + type Item = io::Result; + + fn next(&mut self) -> Option> { + Some(self.listener.accept().map(|s| s.0)) + } + + fn size_hint(&self) -> (usize, Option) { + (usize::MAX, None) + } +} + +impl AsRawSocket for UnixListener { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixListener { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + UnixListener(unsafe { Socket::from_raw_socket(sock) }) + } +} + +impl IntoRawSocket for UnixListener { + #[inline] + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} + +impl<'a> IntoIterator for &'a UnixListener { + type Item = io::Result; + type IntoIter = Incoming<'a>; + + fn into_iter(self) -> Incoming<'a> { + self.incoming() + } +} diff --git a/library/std/src/os/windows/net/mod.rs b/library/std/src/os/windows/net/mod.rs new file mode 100644 index 000000000000..6b3f062b8ab4 --- /dev/null +++ b/library/std/src/os/windows/net/mod.rs @@ -0,0 +1,6 @@ +mod addr; +mod listener; +mod stream; +pub use addr::*; +pub use listener::*; +pub use stream::*; diff --git a/library/std/src/os/windows/net/stream.rs b/library/std/src/os/windows/net/stream.rs new file mode 100644 index 000000000000..c31f03fdf53f --- /dev/null +++ b/library/std/src/os/windows/net/stream.rs @@ -0,0 +1,421 @@ +#![unstable(feature = "windows_unix_domain_sockets", issue = "150487")] +use crate::net::Shutdown; +use crate::os::windows::io::{ + AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, RawSocket, +}; +use crate::os::windows::net::SocketAddr; +use crate::path::Path; +#[cfg(not(doc))] +use crate::sys::c::{ + AF_UNIX, SO_RCVTIMEO, SO_SNDTIMEO, SOCK_STREAM, connect, getpeername, getsockname, +}; +use crate::sys::net::Socket; +#[cfg(not(doc))] +use crate::sys::winsock::startup; +use crate::sys::{AsInner, cvt_nz}; +use crate::time::Duration; +use crate::{fmt, io}; +/// A Unix stream socket. +/// +/// # Examples +/// +/// ```no_run +/// #![feature(windows_unix_domain_sockets)] +/// use std::os::windows::net::UnixStream; +/// use std::io::prelude::*; +/// +/// fn main() -> std::io::Result<()> { +/// let mut stream = UnixStream::connect("/path/to/my/socket")?; +/// stream.write_all(b"hello world")?; +/// let mut response = String::new(); +/// stream.read_to_string(&mut response)?; +/// println!("{response}"); +/// Ok(()) +/// } +/// ``` +pub struct UnixStream(pub(super) Socket); +impl fmt::Debug for UnixStream { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut builder = fmt.debug_struct("UnixStream"); + builder.field("sock", self.0.as_inner()); + if let Ok(addr) = self.local_addr() { + builder.field("local", &addr); + } + if let Ok(addr) = self.peer_addr() { + builder.field("peer", &addr); + } + builder.finish() + } +} +impl UnixStream { + /// Connects to the socket named by `path`. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// let socket = match UnixStream::connect("/tmp/sock") { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return + /// } + /// }; + /// ``` + pub fn connect>(path: P) -> io::Result { + let socket_addr = SocketAddr::from_pathname(path)?; + Self::connect_addr(&socket_addr) + } + + /// Connects to the socket specified by [`address`]. + /// + /// [`address`]: crate::os::windows::net::SocketAddr + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::{UnixListener, UnixStream}; + /// + /// fn main() -> std::io::Result<()> { + /// let listener = UnixListener::bind("/path/to/the/socket")?; + /// let addr = listener.local_addr()?; + /// + /// let sock = match UnixStream::connect_addr(&addr) { + /// Ok(sock) => sock, + /// Err(e) => { + /// println!("Couldn't connect: {e:?}"); + /// return Err(e) + /// } + /// }; + /// Ok(()) + /// } + /// ```` + pub fn connect_addr(socket_addr: &SocketAddr) -> io::Result { + startup(); + let inner = Socket::new(AF_UNIX as _, SOCK_STREAM)?; + unsafe { + cvt_nz(connect( + inner.as_raw(), + &raw const socket_addr.addr as *const _, + socket_addr.len as _, + ))?; + } + Ok(UnixStream(inner)) + } + + /// Returns the socket address of the local half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.local_addr().expect("Couldn't get local address"); + /// Ok(()) + /// } + /// ``` + pub fn local_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getsockname(self.0.as_raw(), addr, len) }) + } + + /// Returns the socket address of the remote half of this connection. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let addr = socket.peer_addr().expect("Couldn't get peer address"); + /// Ok(()) + /// } + /// ``` + pub fn peer_addr(&self) -> io::Result { + SocketAddr::new(|addr, len| unsafe { getpeername(self.0.as_raw(), addr, len) }) + } + + /// Returns the read timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// assert_eq!(socket.read_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn read_timeout(&self) -> io::Result> { + self.0.timeout(SO_RCVTIMEO) + } + + /// Moves the socket into or out of nonblocking mode. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_nonblocking(true).expect("Couldn't set nonblocking"); + /// Ok(()) + /// } + /// ``` + pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> { + self.0.set_nonblocking(nonblocking) + } + + /// Sets the read timeout for the socket. + /// + /// If the provided value is [`None`], then [`read`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_read_timeout(Some(Duration::new(1, 0))).expect("Couldn't set read timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_read_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_read_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_RCVTIMEO) + } + + /// Sets the write timeout for the socket. + /// + /// If the provided value is [`None`], then [`write`] calls will block + /// indefinitely. An [`Err`] is returned if the zero [`Duration`] is + /// passed to this method. + /// + /// [`read`]: io::Read::read + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// Ok(()) + /// } + /// ``` + /// + /// An [`Err`] is returned if the zero [`Duration`] is passed to this + /// method: + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::io; + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let result = socket.set_write_timeout(Some(Duration::new(0, 0))); + /// let err = result.unwrap_err(); + /// assert_eq!(err.kind(), io::ErrorKind::InvalidInput); + /// Ok(()) + /// } + /// ``` + pub fn set_write_timeout(&self, dur: Option) -> io::Result<()> { + self.0.set_timeout(dur, SO_SNDTIMEO) + } + + /// Shuts down the read, write, or both halves of this connection. + /// + /// This function will cause all pending and future I/O calls on the + /// specified portions to immediately return with an appropriate value + /// (see the documentation of [`Shutdown`]). + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::net::Shutdown; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.shutdown(Shutdown::Both).expect("shutdown function failed"); + /// Ok(()) + /// } + /// ``` + pub fn shutdown(&self, how: Shutdown) -> io::Result<()> { + self.0.shutdown(how) + } + + /// Returns the value of the `SO_ERROR` option. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// if let Ok(Some(err)) = socket.take_error() { + /// println!("Got error: {err:?}"); + /// } + /// Ok(()) + /// } + /// ``` + pub fn take_error(&self) -> io::Result> { + self.0.take_error() + } + + /// Creates a new independently owned handle to the underlying socket. + /// + /// The returned `UnixStream` is a reference to the same stream that this + /// object references. Both handles will read and write the same stream of + /// data, and options set on one stream will be propagated to the other + /// stream. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// let sock_copy = socket.try_clone().expect("Couldn't clone socket"); + /// Ok(()) + /// } + /// ``` + pub fn try_clone(&self) -> io::Result { + self.0.duplicate().map(UnixStream) + } + + /// Returns the write timeout of this socket. + /// + /// # Examples + /// + /// ```no_run + /// #![feature(windows_unix_domain_sockets)] + /// use std::os::windows::net::UnixStream; + /// use std::time::Duration; + /// + /// fn main() -> std::io::Result<()> { + /// let socket = UnixStream::connect("/tmp/sock")?; + /// socket.set_write_timeout(Some(Duration::new(1, 0))) + /// .expect("Couldn't set write timeout"); + /// assert_eq!(socket.write_timeout()?, Some(Duration::new(1, 0))); + /// Ok(()) + /// } + /// ``` + pub fn write_timeout(&self) -> io::Result> { + self.0.timeout(SO_SNDTIMEO) + } +} + +impl io::Read for UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + io::Read::read(&mut &*self, buf) + } +} + +impl<'a> io::Read for &'a UnixStream { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + self.0.read(buf) + } +} + +impl io::Write for UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + io::Write::write(&mut &*self, buf) + } + + fn flush(&mut self) -> io::Result<()> { + io::Write::flush(&mut &*self) + } +} +impl<'a> io::Write for &'a UnixStream { + fn write(&mut self, buf: &[u8]) -> io::Result { + self.write_vectored(&[io::IoSlice::new(buf)]) + } + #[inline] + fn flush(&mut self) -> io::Result<()> { + Ok(()) + } + fn write_vectored(&mut self, bufs: &[io::IoSlice<'_>]) -> io::Result { + self.0.write_vectored(bufs) + } + #[inline] + fn is_write_vectored(&self) -> bool { + self.0.is_write_vectored() + } +} + +impl AsSocket for UnixStream { + #[inline] + fn as_socket(&self) -> BorrowedSocket<'_> { + self.0.as_socket() + } +} + +impl AsRawSocket for UnixStream { + #[inline] + fn as_raw_socket(&self) -> RawSocket { + self.0.as_raw_socket() + } +} + +impl FromRawSocket for UnixStream { + #[inline] + unsafe fn from_raw_socket(sock: RawSocket) -> Self { + unsafe { UnixStream(Socket::from_raw_socket(sock)) } + } +} + +impl IntoRawSocket for UnixStream { + fn into_raw_socket(self) -> RawSocket { + self.0.into_raw_socket() + } +} diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs index 17e3cdbecd5c..0cd915261471 100644 --- a/library/std/src/sys/pal/windows/mod.rs +++ b/library/std/src/sys/pal/windows/mod.rs @@ -233,6 +233,11 @@ pub fn cvt(i: I) -> io::Result { if i.is_zero() { Err(io::Error::last_os_error()) } else { Ok(i) } } +#[allow(dead_code)] +pub fn cvt_nz(i: I) -> crate::io::Result<()> { + if i.is_zero() { Ok(()) } else { Err(crate::io::Error::last_os_error()) } +} + pub fn dur2timeout(dur: Duration) -> u32 { // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the // timeouts in windows APIs are typically u32 milliseconds. To translate, we diff --git a/library/std/tests/windows_unix_socket.rs b/library/std/tests/windows_unix_socket.rs new file mode 100644 index 000000000000..18f4c52e72c2 --- /dev/null +++ b/library/std/tests/windows_unix_socket.rs @@ -0,0 +1,86 @@ +#![cfg(windows)] +#![feature(windows_unix_domain_sockets)] +// Now only test windows_unix_domain_sockets feature +// in the future, will test both unix and windows uds +use std::io::{Read, Write}; +use std::os::windows::net::{UnixListener, UnixStream}; +use std::thread; + +#[test] +fn win_uds_smoke_bind_connect() { + let tmp = std::env::temp_dir(); + let sock_path = tmp.join("rust-test-uds-smoke.sock"); + let _ = std::fs::remove_file(&sock_path); + let listener = UnixListener::bind(&sock_path).expect("bind failed"); + let sock_path_clone = sock_path.clone(); + let tx = thread::spawn(move || { + let mut stream = UnixStream::connect(&sock_path_clone).expect("connect failed"); + stream.write_all(b"hello").expect("write failed"); + }); + + let (mut stream, _) = listener.accept().expect("accept failed"); + let mut buf = [0; 5]; + stream.read_exact(&mut buf).expect("read failed"); + assert_eq!(&buf, b"hello"); + + tx.join().unwrap(); + + drop(listener); + let _ = std::fs::remove_file(&sock_path); +} + +#[test] +fn win_uds_echo() { + let tmp = std::env::temp_dir(); + let sock_path = tmp.join("rust-test-uds-echo.sock"); + let _ = std::fs::remove_file(&sock_path); + + let listener = UnixListener::bind(&sock_path).expect("bind failed"); + let srv = thread::spawn(move || { + let (mut stream, _) = listener.accept().expect("accept failed"); + let mut buf = [0u8; 128]; + loop { + let n = match stream.read(&mut buf) { + Ok(0) => break, + Ok(n) => n, + Err(e) => panic!("read error: {}", e), + }; + stream.write_all(&buf[..n]).expect("write_all failed"); + } + }); + + let sock_path_clone = sock_path.clone(); + let cli = thread::spawn(move || { + let mut stream = UnixStream::connect(&sock_path_clone).expect("connect failed"); + let req = b"hello windows uds"; + stream.write_all(req).expect("write failed"); + let mut resp = vec![0u8; req.len()]; + stream.read_exact(&mut resp).expect("read failed"); + assert_eq!(resp, req); + }); + + cli.join().unwrap(); + srv.join().unwrap(); + + let _ = std::fs::remove_file(&sock_path); +} + +#[test] +fn win_uds_path_too_long() { + let tmp = std::env::temp_dir(); + let long_path = tmp.join("a".repeat(200)); + let result = UnixListener::bind(&long_path); + assert!(result.is_err()); + let _ = std::fs::remove_file(&long_path); +} +#[test] +fn win_uds_existing_bind() { + let tmp = std::env::temp_dir(); + let sock_path = tmp.join("rust-test-uds-existing.sock"); + let _ = std::fs::remove_file(&sock_path); + let listener = UnixListener::bind(&sock_path).expect("bind failed"); + let result = UnixListener::bind(&sock_path); + assert!(result.is_err()); + drop(listener); + let _ = std::fs::remove_file(&sock_path); +} From 8c0d93bc54f857bf04c294a25bdbfbe1e29b0900 Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 31 Jan 2026 11:01:45 +0000 Subject: [PATCH 180/319] Skip overlapping spans in argument error suggestions --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 10 ++- .../disjoint-spans-issue-151607.rs | 16 ++++ .../disjoint-spans-issue-151607.stderr | 85 +++++++++++++++++++ 3 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs create mode 100644 tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index c07cbfae256d..2e85410c8960 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -2647,7 +2647,15 @@ impl<'a, 'b, 'tcx> FnCallDiagCtxt<'a, 'b, 'tcx> { // To suggest a multipart suggestion when encountering `foo(1, "")` where the def // was `fn foo(())`. let (_, expected_ty) = self.formal_and_expected_inputs[expected_idx]; - suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + // Check if the new suggestion would overlap with any existing suggestion. + // This can happen when we have both removal suggestions (which may include + // adjacent commas) and type replacement suggestions for the same span. + let dominated = suggestions + .iter() + .any(|(span, _)| span.contains(*arg_span) || arg_span.overlaps(*span)); + if !dominated { + suggestions.push((*arg_span, self.ty_to_snippet(expected_ty, expected_idx))); + } } } } diff --git a/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs new file mode 100644 index 000000000000..31390faa488b --- /dev/null +++ b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.rs @@ -0,0 +1,16 @@ +// Regression test for #151607 +// The ICE was "all spans must be disjoint" when emitting diagnostics +// with overlapping suggestion spans. + +struct B; +struct D; +struct F; +fn foo(g: F, y: F, e: &E) { + //~^ ERROR cannot find type `E` in this scope + foo(B, g, D, E, F, G) + //~^ ERROR this function takes 3 arguments but 6 arguments were supplied + //~| ERROR cannot find value `E` in this scope + //~| ERROR cannot find value `G` in this scope +} + +fn main() {} diff --git a/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr new file mode 100644 index 000000000000..f1bda1203347 --- /dev/null +++ b/tests/ui/argument-suggestions/disjoint-spans-issue-151607.stderr @@ -0,0 +1,85 @@ +error[E0425]: cannot find type `E` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:8:24 + | +LL | struct B; + | --------- similarly named struct `B` defined here +... +LL | fn foo(g: F, y: F, e: &E) { + | ^ + | +help: a struct with a similar name exists + | +LL - fn foo(g: F, y: F, e: &E) { +LL + fn foo(g: F, y: F, e: &B) { + | +help: you might be missing a type parameter + | +LL | fn foo(g: F, y: F, e: &E) { + | +++ + +error[E0425]: cannot find value `E` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:10:18 + | +LL | foo(B, g, D, E, F, G) + | ^ + | +help: a local variable with a similar name exists + | +LL - foo(B, g, D, E, F, G) +LL + foo(B, g, D, e, F, G) + | +help: consider importing one of these constants + | +LL + use std::f128::consts::E; + | +LL + use std::f16::consts::E; + | +LL + use std::f32::consts::E; + | +LL + use std::f64::consts::E; + | + +error[E0425]: cannot find value `G` in this scope + --> $DIR/disjoint-spans-issue-151607.rs:10:24 + | +LL | foo(B, g, D, E, F, G) + | ^ + | +help: a local variable with a similar name exists + | +LL - foo(B, g, D, E, F, G) +LL + foo(B, g, D, E, F, g) + | +help: you might be missing a const parameter + | +LL | fn foo(g: F, y: F, e: &E) { + | +++++++++++++++++++++ + +error[E0061]: this function takes 3 arguments but 6 arguments were supplied + --> $DIR/disjoint-spans-issue-151607.rs:10:5 + | +LL | foo(B, g, D, E, F, G) + | ^^^ - - - unexpected argument #4 + | | | + | | unexpected argument #3 of type `D` + | unexpected argument #1 of type `B` + | +note: function defined here + --> $DIR/disjoint-spans-issue-151607.rs:8:4 + | +LL | fn foo(g: F, y: F, e: &E) { + | ^^^ ----- +help: consider borrowing here + | +LL | foo(B, g, D, E, F, &G) + | + +help: remove the extra arguments + | +LL - foo(B, g, D, E, F, G) +LL + foo(, g, F, G) + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0061, E0425. +For more information about an error, try `rustc --explain E0061`. From e60c475471ab934c6047465e00d5d2849c3711e3 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 31 Jan 2026 22:56:49 +1100 Subject: [PATCH 181/319] Remove unused method `DroplessArena::contains_slice` --- compiler/rustc_arena/src/lib.rs | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/compiler/rustc_arena/src/lib.rs b/compiler/rustc_arena/src/lib.rs index 524baf5b07fe..6217bf46a942 100644 --- a/compiler/rustc_arena/src/lib.rs +++ b/compiler/rustc_arena/src/lib.rs @@ -510,19 +510,6 @@ impl DroplessArena { } } - /// Used by `Lift` to check whether this slice is allocated - /// in this arena. - #[inline] - pub fn contains_slice(&self, slice: &[T]) -> bool { - for chunk in self.chunks.borrow_mut().iter_mut() { - let ptr = slice.as_ptr().cast::().cast_mut(); - if chunk.start() <= ptr && chunk.end() >= ptr { - return true; - } - } - false - } - /// Allocates a string slice that is copied into the `DroplessArena`, returning a /// reference to it. Will panic if passed an empty string. /// From 4df307acc1ff152518eab3cc142fbd0a394143e4 Mon Sep 17 00:00:00 2001 From: zedddie Date: Fri, 30 Jan 2026 16:27:13 +0100 Subject: [PATCH 182/319] move some tests --- .../associated-const-access.rs} | 0 .../issue-49824.rs => closures/nested-closure-escape-borrow.rs} | 0 .../nested-closure-escape-borrow.stderr} | 0 .../ui/{issues/issue-17651.rs => closures/unsized_value_move.rs} | 0 .../issue-17651.stderr => closures/unsized_value_move.stderr} | 0 tests/ui/{issues/issue-18767.rs => deref/deref-in-for-loop.rs} | 0 .../ui/{issues/issue-34569.rs => match/closure-in-match-guard.rs} | 0 tests/ui/{issues/issue-29740.rs => match/large-match-mir-gen.rs} | 0 .../issue-32004.rs => pattern/constructor-type-mismatch.rs} | 0 .../constructor-type-mismatch.stderr} | 0 .../{issues/issue-3038.rs => pattern/multiple-bindings-on-var.rs} | 0 .../issue-3038.stderr => pattern/multiple-bindings-on-var.stderr} | 0 .../issue-2849.rs => pattern/or-pattern-binding-mismatch.rs} | 0 .../or-pattern-binding-mismatch.stderr} | 0 .../or-pattern-mismatched-variable-and-variant.rs} | 0 .../or-pattern-mismatched-variable-and-variant.stderr} | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-31267.rs => associated-consts/associated-const-access.rs} (100%) rename tests/ui/{issues/issue-49824.rs => closures/nested-closure-escape-borrow.rs} (100%) rename tests/ui/{issues/issue-49824.stderr => closures/nested-closure-escape-borrow.stderr} (100%) rename tests/ui/{issues/issue-17651.rs => closures/unsized_value_move.rs} (100%) rename tests/ui/{issues/issue-17651.stderr => closures/unsized_value_move.stderr} (100%) rename tests/ui/{issues/issue-18767.rs => deref/deref-in-for-loop.rs} (100%) rename tests/ui/{issues/issue-34569.rs => match/closure-in-match-guard.rs} (100%) rename tests/ui/{issues/issue-29740.rs => match/large-match-mir-gen.rs} (100%) rename tests/ui/{issues/issue-32004.rs => pattern/constructor-type-mismatch.rs} (100%) rename tests/ui/{issues/issue-32004.stderr => pattern/constructor-type-mismatch.stderr} (100%) rename tests/ui/{issues/issue-3038.rs => pattern/multiple-bindings-on-var.rs} (100%) rename tests/ui/{issues/issue-3038.stderr => pattern/multiple-bindings-on-var.stderr} (100%) rename tests/ui/{issues/issue-2849.rs => pattern/or-pattern-binding-mismatch.rs} (100%) rename tests/ui/{issues/issue-2849.stderr => pattern/or-pattern-binding-mismatch.stderr} (100%) rename tests/ui/{issues/issue-2848.rs => pattern/or-pattern-mismatched-variable-and-variant.rs} (100%) rename tests/ui/{issues/issue-2848.stderr => pattern/or-pattern-mismatched-variable-and-variant.stderr} (100%) diff --git a/tests/ui/issues/issue-31267.rs b/tests/ui/associated-consts/associated-const-access.rs similarity index 100% rename from tests/ui/issues/issue-31267.rs rename to tests/ui/associated-consts/associated-const-access.rs diff --git a/tests/ui/issues/issue-49824.rs b/tests/ui/closures/nested-closure-escape-borrow.rs similarity index 100% rename from tests/ui/issues/issue-49824.rs rename to tests/ui/closures/nested-closure-escape-borrow.rs diff --git a/tests/ui/issues/issue-49824.stderr b/tests/ui/closures/nested-closure-escape-borrow.stderr similarity index 100% rename from tests/ui/issues/issue-49824.stderr rename to tests/ui/closures/nested-closure-escape-borrow.stderr diff --git a/tests/ui/issues/issue-17651.rs b/tests/ui/closures/unsized_value_move.rs similarity index 100% rename from tests/ui/issues/issue-17651.rs rename to tests/ui/closures/unsized_value_move.rs diff --git a/tests/ui/issues/issue-17651.stderr b/tests/ui/closures/unsized_value_move.stderr similarity index 100% rename from tests/ui/issues/issue-17651.stderr rename to tests/ui/closures/unsized_value_move.stderr diff --git a/tests/ui/issues/issue-18767.rs b/tests/ui/deref/deref-in-for-loop.rs similarity index 100% rename from tests/ui/issues/issue-18767.rs rename to tests/ui/deref/deref-in-for-loop.rs diff --git a/tests/ui/issues/issue-34569.rs b/tests/ui/match/closure-in-match-guard.rs similarity index 100% rename from tests/ui/issues/issue-34569.rs rename to tests/ui/match/closure-in-match-guard.rs diff --git a/tests/ui/issues/issue-29740.rs b/tests/ui/match/large-match-mir-gen.rs similarity index 100% rename from tests/ui/issues/issue-29740.rs rename to tests/ui/match/large-match-mir-gen.rs diff --git a/tests/ui/issues/issue-32004.rs b/tests/ui/pattern/constructor-type-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-32004.rs rename to tests/ui/pattern/constructor-type-mismatch.rs diff --git a/tests/ui/issues/issue-32004.stderr b/tests/ui/pattern/constructor-type-mismatch.stderr similarity index 100% rename from tests/ui/issues/issue-32004.stderr rename to tests/ui/pattern/constructor-type-mismatch.stderr diff --git a/tests/ui/issues/issue-3038.rs b/tests/ui/pattern/multiple-bindings-on-var.rs similarity index 100% rename from tests/ui/issues/issue-3038.rs rename to tests/ui/pattern/multiple-bindings-on-var.rs diff --git a/tests/ui/issues/issue-3038.stderr b/tests/ui/pattern/multiple-bindings-on-var.stderr similarity index 100% rename from tests/ui/issues/issue-3038.stderr rename to tests/ui/pattern/multiple-bindings-on-var.stderr diff --git a/tests/ui/issues/issue-2849.rs b/tests/ui/pattern/or-pattern-binding-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-2849.rs rename to tests/ui/pattern/or-pattern-binding-mismatch.rs diff --git a/tests/ui/issues/issue-2849.stderr b/tests/ui/pattern/or-pattern-binding-mismatch.stderr similarity index 100% rename from tests/ui/issues/issue-2849.stderr rename to tests/ui/pattern/or-pattern-binding-mismatch.stderr diff --git a/tests/ui/issues/issue-2848.rs b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs similarity index 100% rename from tests/ui/issues/issue-2848.rs rename to tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs diff --git a/tests/ui/issues/issue-2848.stderr b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr similarity index 100% rename from tests/ui/issues/issue-2848.stderr rename to tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr From 086dc0258bd5d12570a79f23b73ea030c0acbd00 Mon Sep 17 00:00:00 2001 From: zedddie Date: Fri, 30 Jan 2026 16:27:49 +0100 Subject: [PATCH 183/319] clean up some tests --- tests/ui/associated-consts/associated-const-access.rs | 2 +- tests/ui/closures/nested-closure-escape-borrow.rs | 1 + tests/ui/closures/nested-closure-escape-borrow.stderr | 2 +- tests/ui/closures/unsized_value_move.rs | 1 + tests/ui/closures/unsized_value_move.stderr | 2 +- tests/ui/deref/deref-in-for-loop.rs | 1 + tests/ui/match/closure-in-match-guard.rs | 1 + tests/ui/match/large-match-mir-gen.rs | 5 +++-- tests/ui/pattern/constructor-type-mismatch.rs | 1 + tests/ui/pattern/constructor-type-mismatch.stderr | 4 ++-- tests/ui/pattern/multiple-bindings-on-var.rs | 1 + tests/ui/pattern/multiple-bindings-on-var.stderr | 6 +++--- tests/ui/pattern/or-pattern-binding-mismatch.rs | 1 + tests/ui/pattern/or-pattern-binding-mismatch.stderr | 2 +- .../pattern/or-pattern-mismatched-variable-and-variant.rs | 1 + .../or-pattern-mismatched-variable-and-variant.stderr | 4 ++-- 16 files changed, 22 insertions(+), 13 deletions(-) diff --git a/tests/ui/associated-consts/associated-const-access.rs b/tests/ui/associated-consts/associated-const-access.rs index d6081bb87439..d6508d4922ae 100644 --- a/tests/ui/associated-consts/associated-const-access.rs +++ b/tests/ui/associated-consts/associated-const-access.rs @@ -1,5 +1,5 @@ +//! regression test for //@ run-pass -// Regression test for issue #31267 struct Foo; diff --git a/tests/ui/closures/nested-closure-escape-borrow.rs b/tests/ui/closures/nested-closure-escape-borrow.rs index bc1cd6856bc9..afd440ba4250 100644 --- a/tests/ui/closures/nested-closure-escape-borrow.rs +++ b/tests/ui/closures/nested-closure-escape-borrow.rs @@ -1,3 +1,4 @@ +//! regression test for fn main() { let mut x = 0; || { diff --git a/tests/ui/closures/nested-closure-escape-borrow.stderr b/tests/ui/closures/nested-closure-escape-borrow.stderr index 1c77090de27b..5a77652fa376 100644 --- a/tests/ui/closures/nested-closure-escape-borrow.stderr +++ b/tests/ui/closures/nested-closure-escape-borrow.stderr @@ -1,5 +1,5 @@ error: captured variable cannot escape `FnMut` closure body - --> $DIR/issue-49824.rs:4:9 + --> $DIR/nested-closure-escape-borrow.rs:5:9 | LL | let mut x = 0; | ----- variable defined here diff --git a/tests/ui/closures/unsized_value_move.rs b/tests/ui/closures/unsized_value_move.rs index 7629a5a3be1e..da39cc0f35f4 100644 --- a/tests/ui/closures/unsized_value_move.rs +++ b/tests/ui/closures/unsized_value_move.rs @@ -1,3 +1,4 @@ +//! regression test for // Test that moves of unsized values within closures are caught // and rejected. diff --git a/tests/ui/closures/unsized_value_move.stderr b/tests/ui/closures/unsized_value_move.stderr index 9519507320d8..a9a26a42d167 100644 --- a/tests/ui/closures/unsized_value_move.stderr +++ b/tests/ui/closures/unsized_value_move.stderr @@ -1,5 +1,5 @@ error[E0277]: the size for values of type `[{integer}]` cannot be known at compilation time - --> $DIR/issue-17651.rs:5:18 + --> $DIR/unsized_value_move.rs:6:18 | LL | (|| Box::new(*(&[0][..])))(); | -------- ^^^^^^^^^^^ doesn't have a size known at compile-time diff --git a/tests/ui/deref/deref-in-for-loop.rs b/tests/ui/deref/deref-in-for-loop.rs index 87762406da60..26921c3f1dda 100644 --- a/tests/ui/deref/deref-in-for-loop.rs +++ b/tests/ui/deref/deref-in-for-loop.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass // Test that regionck uses the right memcat for patterns in for loops // and doesn't ICE. diff --git a/tests/ui/match/closure-in-match-guard.rs b/tests/ui/match/closure-in-match-guard.rs index 25b2e7fbe160..c3f16dad4bfb 100644 --- a/tests/ui/match/closure-in-match-guard.rs +++ b/tests/ui/match/closure-in-match-guard.rs @@ -1,3 +1,4 @@ +//! regression test for //@ run-pass //@ compile-flags:-g diff --git a/tests/ui/match/large-match-mir-gen.rs b/tests/ui/match/large-match-mir-gen.rs index e26e2c882dc3..bb084a9ef0d0 100644 --- a/tests/ui/match/large-match-mir-gen.rs +++ b/tests/ui/match/large-match-mir-gen.rs @@ -1,7 +1,8 @@ +//! regression test for //@ check-pass #![allow(dead_code)] -// Regression test for #29740. Inefficient MIR matching algorithms -// generated way too much code for this sort of case, leading to OOM. +// Inefficient MIR matching algorithms generated way +// too much code for this sort of case, leading to OOM. #![allow(non_snake_case)] pub mod KeyboardEventConstants { diff --git a/tests/ui/pattern/constructor-type-mismatch.rs b/tests/ui/pattern/constructor-type-mismatch.rs index b3493508c5a9..77b8e5312dca 100644 --- a/tests/ui/pattern/constructor-type-mismatch.rs +++ b/tests/ui/pattern/constructor-type-mismatch.rs @@ -1,3 +1,4 @@ +//! regression test for enum Foo { Bar(i32), Baz diff --git a/tests/ui/pattern/constructor-type-mismatch.stderr b/tests/ui/pattern/constructor-type-mismatch.stderr index fcbec97661b4..6610e12cdc75 100644 --- a/tests/ui/pattern/constructor-type-mismatch.stderr +++ b/tests/ui/pattern/constructor-type-mismatch.stderr @@ -1,5 +1,5 @@ error[E0532]: expected unit struct, unit variant or constant, found tuple variant `Foo::Bar` - --> $DIR/issue-32004.rs:10:9 + --> $DIR/constructor-type-mismatch.rs:11:9 | LL | Bar(i32), | -------- `Foo::Bar` defined here @@ -20,7 +20,7 @@ LL + Foo::Baz => {} | error[E0532]: expected tuple struct or tuple variant, found unit struct `S` - --> $DIR/issue-32004.rs:16:9 + --> $DIR/constructor-type-mismatch.rs:17:9 | LL | struct S; | --------- `S` defined here diff --git a/tests/ui/pattern/multiple-bindings-on-var.rs b/tests/ui/pattern/multiple-bindings-on-var.rs index cf3ba009f00c..f84276257b67 100644 --- a/tests/ui/pattern/multiple-bindings-on-var.rs +++ b/tests/ui/pattern/multiple-bindings-on-var.rs @@ -1,3 +1,4 @@ +//! regression test for enum F { G(isize, isize) } enum H { I(J, K) } diff --git a/tests/ui/pattern/multiple-bindings-on-var.stderr b/tests/ui/pattern/multiple-bindings-on-var.stderr index 210da2ceff9b..c2ec11c43465 100644 --- a/tests/ui/pattern/multiple-bindings-on-var.stderr +++ b/tests/ui/pattern/multiple-bindings-on-var.stderr @@ -1,17 +1,17 @@ error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:12:15 + --> $DIR/multiple-bindings-on-var.rs:13:15 | LL | F::G(x, x) => { println!("{}", x + x); } | ^ used in a pattern more than once error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:17:32 + --> $DIR/multiple-bindings-on-var.rs:18:32 | LL | H::I(J::L(x, _), K::M(_, x)) | ^ used in a pattern more than once error[E0416]: identifier `x` is bound more than once in the same pattern - --> $DIR/issue-3038.rs:23:13 + --> $DIR/multiple-bindings-on-var.rs:24:13 | LL | (x, x) => { x } | ^ used in a pattern more than once diff --git a/tests/ui/pattern/or-pattern-binding-mismatch.rs b/tests/ui/pattern/or-pattern-binding-mismatch.rs index 787ab0e28960..a207c3f81899 100644 --- a/tests/ui/pattern/or-pattern-binding-mismatch.rs +++ b/tests/ui/pattern/or-pattern-binding-mismatch.rs @@ -1,3 +1,4 @@ +//! regression test for enum Foo { Alpha, Beta(isize) } fn main() { diff --git a/tests/ui/pattern/or-pattern-binding-mismatch.stderr b/tests/ui/pattern/or-pattern-binding-mismatch.stderr index ef5cdb42e610..46984fac03ce 100644 --- a/tests/ui/pattern/or-pattern-binding-mismatch.stderr +++ b/tests/ui/pattern/or-pattern-binding-mismatch.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `i` is not bound in all patterns - --> $DIR/issue-2849.rs:5:7 + --> $DIR/or-pattern-binding-mismatch.rs:6:7 | LL | Foo::Alpha | Foo::Beta(i) => {} | ^^^^^^^^^^ - variable not in all patterns diff --git a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs index 8499459cec26..0447fd2ad589 100644 --- a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs +++ b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.rs @@ -1,3 +1,4 @@ +//! regression test for #[allow(non_camel_case_types)] mod bar { diff --git a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr index 1cef27c34635..66c7d7d348c9 100644 --- a/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr +++ b/tests/ui/pattern/or-pattern-mismatched-variable-and-variant.stderr @@ -1,5 +1,5 @@ error[E0408]: variable `beta` is not bound in all patterns - --> $DIR/issue-2848.rs:14:7 + --> $DIR/or-pattern-mismatched-variable-and-variant.rs:15:7 | LL | alpha | beta => {} | ^^^^^ ---- variable not in all patterns @@ -7,7 +7,7 @@ LL | alpha | beta => {} | pattern doesn't bind `beta` error[E0170]: pattern binding `beta` is named the same as one of the variants of the type `bar::foo` - --> $DIR/issue-2848.rs:14:15 + --> $DIR/or-pattern-mismatched-variable-and-variant.rs:15:15 | LL | alpha | beta => {} | ^^^^ help: to match on the variant, qualify the path: `bar::foo::beta` From 7bb156395edb2bdc591b4774bc2cea9dd28d94da Mon Sep 17 00:00:00 2001 From: JayanAXHF Date: Sat, 31 Jan 2026 23:37:56 +0530 Subject: [PATCH 184/319] fix: fix `handle_explain` to always respect `--color always` --- compiler/rustc_driver_impl/src/lib.rs | 37 ++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..2b837cc8442b 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -491,10 +491,18 @@ fn handle_explain(early_dcx: &EarlyDiagCtxt, registry: Registry, code: &str, col } text.push('\n'); } + + // If output is a terminal, use a pager to display the content. if io::stdout().is_terminal() { show_md_content_with_pager(&text, color); } else { - safe_print!("{text}"); + // Otherwise, if the user has requested colored output + // print the content in color, else print the md content. + if color == ColorConfig::Always { + show_colored_md_content(&text); + } else { + safe_print!("{text}"); + } } } else { early_dcx.early_fatal(format!("{code} is not a valid error code")); @@ -564,6 +572,33 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { safe_print!("{content}"); } +/// Prints the markdown content with colored output. +/// +/// This function is used when the output is not a terminal, +/// but the user has requested colored output with `--color=always`. +fn show_colored_md_content(content: &str) { + // Try to prettify the raw markdown text. + let mut pretty_data = { + let mdstream = markdown::MdStream::parse_str(content); + let bufwtr = markdown::create_stdout_bufwtr(); + let mut mdbuf = Vec::new(); + if mdstream.write_anstream_buf(&mut mdbuf, Some(&highlighter::highlight)).is_ok() { + Some((bufwtr, mdbuf)) + } else { + None + } + }; + + if let Some((bufwtr, mdbuf)) = &mut pretty_data + && bufwtr.write_all(&mdbuf).is_ok() + { + return; + } + + // Everything failed. Print the raw markdown text. + safe_print!("{content}"); +} + fn process_rlink(sess: &Session, compiler: &interface::Compiler) { assert!(sess.opts.unstable_opts.link_only); let dcx = sess.dcx(); From a3e94c843398b096b79c05506ee248638afd1e6c Mon Sep 17 00:00:00 2001 From: JayanAXHF Date: Sun, 1 Feb 2026 00:23:04 +0530 Subject: [PATCH 185/319] test: add tests to ensure that `--color always` is always respected --- tests/ui/explain/ensure-color-always-works.rs | 2 + .../explain/ensure-color-always-works.stdout | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 tests/ui/explain/ensure-color-always-works.rs create mode 100644 tests/ui/explain/ensure-color-always-works.stdout diff --git a/tests/ui/explain/ensure-color-always-works.rs b/tests/ui/explain/ensure-color-always-works.rs new file mode 100644 index 000000000000..81ee716e78b4 --- /dev/null +++ b/tests/ui/explain/ensure-color-always-works.rs @@ -0,0 +1,2 @@ +//@ compile-flags: --explain E0591 --color always --error-format=human +//@ check-pass diff --git a/tests/ui/explain/ensure-color-always-works.stdout b/tests/ui/explain/ensure-color-always-works.stdout new file mode 100644 index 000000000000..7e5358bcfb7e --- /dev/null +++ b/tests/ui/explain/ensure-color-always-works.stdout @@ -0,0 +1,50 @@ +Per ]8;;https://github.com/rust-lang/rfcs/blob/master/text/0401-coercions.md\RFC 401]8;;\, if you have a function declaration foo: + +struct S; + +// For the purposes of this explanation, all of these +// different kinds of `fn` declarations are equivalent: + +fn foo(x: S) { /* ... */ } +extern "C" { + fn foo(x: S); +} +impl S { + fn foo(self) { /* ... */ } +} + +the type of foo is not fn(S), as one might expect. Rather, it is a unique, zero-sized marker type written here as typeof(foo). However, typeof(foo) + can be coerced to a function pointer fn(S), so you rarely notice this: + +let x: fn(S) = foo; // OK, coerces + +The reason that this matter is that the type fn(S) is not specific to any particular function: it's a function pointer. So calling x() +results in a virtual call, whereas foo() is statically dispatched, because the type of foo tells us precisely what function is being called. + +As noted above, coercions mean that most code doesn't have to be concerned with this distinction. However, you can tell the difference when +using transmute to convert a fn item into a fn pointer. + +This is sometimes done as part of an FFI: + +extern "C" fn foo(userdata: Box<i32>) { + /* ... */ +} + +unsafe { + let f: extern "C" fn(*mut i32) = transmute(foo); + callback(f); +} + +Here, transmute is being used to convert the types of the fn arguments. This pattern is incorrect because the type of foo is a function item +(typeof(foo)), which is zero-sized, and the target type (fn()) is a function pointer, which is not zero-sized. This pattern should be +rewritten. There are a few possible ways to do this: + +* change the original fn declaration to match the expected signature, and do the cast in the fn body (the preferred option) +* cast the fn item of a fn pointer before calling transmute, as shown here: + +let f: extern "C" fn(*mut i32) = transmute(foo as extern "C" fn(_)); +let f: extern "C" fn(*mut i32) = transmute(foo as usize); // works too + +The same applies to transmutes to *mut fn(), which were observed in practice. Note though that use of this type is generally incorrect. The +intention is typically to describe a function pointer, but just fn() alone suffices for that. *mut fn() is a pointer to a fn pointer. (Since these +values are typically just passed to C code, however, this rarely makes a difference in practice.) From 49f9fe75ee257070af2d689265ccd16f1c9ddf76 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 31 Jan 2026 00:15:00 +0100 Subject: [PATCH 186/319] Remove the `dep_node` argument from `try_mark_previous_green` --- .../rustc_query_system/src/dep_graph/graph.rs | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index cf3d211d1cc7..36f5ccfc68f4 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -872,6 +872,8 @@ impl DepGraphData { // Return None if the dep node didn't exist in the previous session let prev_index = self.previous.node_to_index_opt(dep_node)?; + debug_assert_eq!(self.previous.index_to_node(prev_index), dep_node); + match self.colors.get(prev_index) { DepNodeColor::Green(dep_node_index) => Some((prev_index, dep_node_index)), DepNodeColor::Red => None, @@ -880,7 +882,7 @@ impl DepGraphData { // in the previous compilation session too, so we can try to // mark it as green by recursively marking all of its // dependencies green. - self.try_mark_previous_green(qcx, prev_index, dep_node, None) + self.try_mark_previous_green(qcx, prev_index, None) .map(|dep_node_index| (prev_index, dep_node_index)) } } @@ -928,8 +930,7 @@ impl DepGraphData { dep_dep_node, dep_dep_node.hash, ); - let node_index = - self.try_mark_previous_green(qcx, parent_dep_node_index, dep_dep_node, Some(frame)); + let node_index = self.try_mark_previous_green(qcx, parent_dep_node_index, Some(frame)); if node_index.is_some() { debug!("managed to MARK dependency {dep_dep_node:?} as green"); @@ -981,15 +982,15 @@ impl DepGraphData { &self, qcx: Qcx, prev_dep_node_index: SerializedDepNodeIndex, - dep_node: &DepNode, frame: Option<&MarkFrame<'_>>, ) -> Option { let frame = MarkFrame { index: prev_dep_node_index, parent: frame }; // We never try to mark eval_always nodes as green - debug_assert!(!qcx.dep_context().is_eval_always(dep_node.kind)); - - debug_assert_eq!(self.previous.index_to_node(prev_dep_node_index), dep_node); + debug_assert!( + !qcx.dep_context() + .is_eval_always(self.previous.index_to_node(prev_dep_node_index).kind) + ); let prev_deps = self.previous.edge_targets_from(prev_dep_node_index); @@ -1010,7 +1011,10 @@ impl DepGraphData { // ... and finally storing a "Green" entry in the color map. // Multiple threads can all write the same color here - debug!("successfully marked {dep_node:?} as green"); + debug!( + "successfully marked {:?} as green", + self.previous.index_to_node(prev_dep_node_index) + ); Some(dep_node_index) } } From 4cf181bfa40957696c46b7637e6d2250fa18617b Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 31 Jan 2026 21:55:02 +0200 Subject: [PATCH 187/319] suggested.md: improve a bit It is awkward to continue a sentence after a code block --- src/doc/rustc-dev-guide/src/building/suggested.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index b5c9b9b4e3d1..5eb214fac904 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -277,14 +277,14 @@ This often helps reviewing. ## Configuring `rustup` to use nightly Some parts of the bootstrap process uses pinned, nightly versions of tools like rustfmt. -To make things like `cargo fmt` work correctly in your repo, run +To make things like `cargo fmt` work correctly in your repo, +[install a nightly toolchain] with rustup, then run this command: ```console cd rustup override set nightly ``` -After [installing a nightly toolchain] with `rustup`. Don't forget to do this for all directories you have [setup a worktree for]. You may need to use the pinned nightly version from `src/stage0`, but often the normal `nightly` channel will work. @@ -297,7 +297,7 @@ toolchain for your bootstrapped compiler You still have to use `x` to work on the compiler or standard library, this just lets you use `cargo fmt`. -[installing a nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html?highlight=nightl#working-with-nightly-rust +[install a nightly toolchain]: https://rust-lang.github.io/rustup/concepts/channels.html?highlight=nightl#working-with-nightly-rust [setup a worktree for]: ./suggested.md#working-on-multiple-branches-at-the-same-time [the section on vscode]: suggested.md#configuring-rust-analyzer-for-rustc [the section on rustup]: how-to-build-and-run.md?highlight=rustup#creating-a-rustup-toolchain From c8ce14018f082a747307a5bbc5b46ec24c2642dd Mon Sep 17 00:00:00 2001 From: Takayuki Maeda Date: Sun, 1 Feb 2026 05:27:13 +0900 Subject: [PATCH 188/319] check if ProjectionTy or ProjectionConst fmt --- .../src/error_reporting/traits/fulfillment_errors.rs | 2 ++ .../opaque-alias-relate-issue-151331.rs | 10 ++++++++++ .../opaque-alias-relate-issue-151331.stderr | 9 +++++++++ 3 files changed, 21 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs create mode 100644 tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.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 6872d038fb7f..45e4f5fe23a6 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 @@ -1607,6 +1607,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { }; if let Some(lhs) = lhs.to_alias_term() + && let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst = lhs.kind(self.tcx) && let Some((better_type_err, expected_term)) = derive_better_type_error(lhs, rhs) { @@ -1615,6 +1616,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { better_type_err, ) } else if let Some(rhs) = rhs.to_alias_term() + && let ty::AliasTermKind::ProjectionTy | ty::AliasTermKind::ProjectionConst = rhs.kind(self.tcx) && let Some((better_type_err, expected_term)) = derive_better_type_error(rhs, lhs) { diff --git a/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs new file mode 100644 index 000000000000..684f2498d584 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.rs @@ -0,0 +1,10 @@ +//@ compile-flags: -Znext-solver=globally +#![feature(type_alias_impl_trait)] + +type Foo = Vec; + +#[define_opaque(Foo)] +fn make_foo() -> Foo {} +//~^ ERROR type mismatch resolving + +fn main() {} diff --git a/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr new file mode 100644 index 000000000000..dd73ed1a247c --- /dev/null +++ b/tests/ui/type-alias-impl-trait/opaque-alias-relate-issue-151331.stderr @@ -0,0 +1,9 @@ +error[E0271]: type mismatch resolving `Foo == ()` + --> $DIR/opaque-alias-relate-issue-151331.rs:7:18 + | +LL | fn make_foo() -> Foo {} + | ^^^ types differ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0271`. From 4e02b6db0aac951f02d13fa316ea7d7e3acc0e98 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sun, 1 Feb 2026 00:00:26 +0200 Subject: [PATCH 189/319] typeck_root_def_id: improve doc comment This was initially written to be exhaustive, but one more type that can only be type-checked with its containing item has since been added, and was not mentioned. So, make it future-proof by mentioning just the one example. Also, a previous refactor left this less readable. --- compiler/rustc_middle/src/ty/util.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index c4212eee8e40..d6e6ffae7300 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -642,12 +642,8 @@ impl<'tcx> TyCtxt<'tcx> { /// has its own type-checking context or "inference environment". /// /// For example, a closure has its own `DefId`, but it is type-checked - /// with the containing item. Similarly, an inline const block has its - /// own `DefId` but it is type-checked together with the containing item. - /// - /// Therefore, when we fetch the - /// `typeck` the closure, for example, we really wind up - /// fetching the `typeck` the enclosing fn item. + /// with the containing item. Therefore, when we fetch the `typeck` of the closure, + /// for example, we really wind up fetching the `typeck` of the enclosing fn item. pub fn typeck_root_def_id(self, def_id: DefId) -> DefId { let mut def_id = def_id; while self.is_typeck_child(def_id) { From 16844173269ade1ea68f302c24ac1ebc8efedbb4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sun, 1 Feb 2026 00:31:55 +0200 Subject: [PATCH 190/319] ty::context: clean some code a little The explicit `return` was not needed --- compiler/rustc_middle/src/ty/context.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index a6960523f6cf..41e1388e3146 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -3470,10 +3470,9 @@ impl<'tcx> TyCtxt<'tcx> { pub fn intrinsic(self, def_id: impl IntoQueryParam + Copy) -> Option { match self.def_kind(def_id) { - DefKind::Fn | DefKind::AssocFn => {} - _ => return None, + DefKind::Fn | DefKind::AssocFn => self.intrinsic_raw(def_id), + _ => None, } - self.intrinsic_raw(def_id) } pub fn next_trait_solver_globally(self) -> bool { From f0b76d5a1591fc39d8a6f9c35772695a3fad0036 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 31 Jan 2026 18:57:32 -0600 Subject: [PATCH 191/319] Use with_capacity in query_key_hash_verify --- .../rustc_data_structures/src/vec_cache.rs | 4 ++++ compiler/rustc_query_impl/src/plumbing.rs | 3 +-- .../rustc_query_system/src/query/caches.rs | 18 ++++++++++++++++++ 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_data_structures/src/vec_cache.rs b/compiler/rustc_data_structures/src/vec_cache.rs index 599970663db8..c3baae204f66 100644 --- a/compiler/rustc_data_structures/src/vec_cache.rs +++ b/compiler/rustc_data_structures/src/vec_cache.rs @@ -331,6 +331,10 @@ where } } } + + pub fn len(&self) -> usize { + self.len.load(Ordering::Acquire) + } } #[cfg(test)] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..55c5ff911c31 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -415,9 +415,8 @@ pub(crate) fn query_key_hash_verify<'tcx>( ) { let _timer = qcx.tcx.prof.generic_activity_with_arg("query_key_hash_verify_for", query.name()); - let mut map = UnordMap::default(); - let cache = query.query_cache(qcx); + let mut map = UnordMap::with_capacity(cache.len()); cache.iter(&mut |key, _, _| { let node = DepNode::construct(qcx.tcx, query.dep_kind(), key); if let Some(other_key) = map.insert(node, *key) { diff --git a/compiler/rustc_query_system/src/query/caches.rs b/compiler/rustc_query_system/src/query/caches.rs index 30b5d7e59549..67ad767d4d31 100644 --- a/compiler/rustc_query_system/src/query/caches.rs +++ b/compiler/rustc_query_system/src/query/caches.rs @@ -30,6 +30,8 @@ pub trait QueryCache: Sized { fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex); fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)); + + fn len(&self) -> usize; } /// In-memory cache for queries whose keys aren't suitable for any of the @@ -71,6 +73,10 @@ where } } } + + fn len(&self) -> usize { + self.cache.len() + } } /// In-memory cache for queries whose key type only has one value (e.g. `()`). @@ -107,6 +113,10 @@ where f(&(), &value.0, value.1) } } + + fn len(&self) -> usize { + self.cache.get().is_some().into() + } } /// In-memory cache for queries whose key is a [`DefId`]. @@ -157,6 +167,10 @@ where }); self.foreign.iter(f); } + + fn len(&self) -> usize { + self.local.len() + self.foreign.len() + } } impl QueryCache for VecCache @@ -180,4 +194,8 @@ where fn iter(&self, f: &mut dyn FnMut(&Self::Key, &Self::Value, DepNodeIndex)) { self.iter(f) } + + fn len(&self) -> usize { + self.len() + } } From e251fba475714560d60555a26b9c1a4264776bc8 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sun, 1 Feb 2026 10:42:57 +0900 Subject: [PATCH 192/319] Replace plain text link with md link to cargo-pgo repo --- src/doc/rustc/src/profile-guided-optimization.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index 4050494793a3..fa6e0912f7f0 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -151,7 +151,9 @@ to use PGO with Rust. As an alternative to directly using the compiler for Profile-Guided Optimization, you may choose to go with `cargo-pgo`, which has an intuitive command-line API and saves you the trouble of doing all the manual work. You can read more about -it in their repository accessible from this link: https://github.com/Kobzol/cargo-pgo +it in [their repository][cargo-pgo]. + +[cargo-pgo]: https://github.com/Kobzol/cargo-pgo For the sake of completeness, here are the corresponding steps using `cargo-pgo`: From 9b04184cbc7f1387f38a35e2e219cd88f7089cf6 Mon Sep 17 00:00:00 2001 From: Sergei Izmailov Date: Sun, 1 Feb 2026 10:46:02 +0900 Subject: [PATCH 193/319] reword --- src/doc/rustc/src/profile-guided-optimization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc/src/profile-guided-optimization.md b/src/doc/rustc/src/profile-guided-optimization.md index fa6e0912f7f0..eeeeffe65a02 100644 --- a/src/doc/rustc/src/profile-guided-optimization.md +++ b/src/doc/rustc/src/profile-guided-optimization.md @@ -151,7 +151,7 @@ to use PGO with Rust. As an alternative to directly using the compiler for Profile-Guided Optimization, you may choose to go with `cargo-pgo`, which has an intuitive command-line API and saves you the trouble of doing all the manual work. You can read more about -it in [their repository][cargo-pgo]. +it in [cargo-pgo repository][cargo-pgo]. [cargo-pgo]: https://github.com/Kobzol/cargo-pgo From 60f567916cc39add8a9b6571c1865577204ca729 Mon Sep 17 00:00:00 2001 From: Cameron Steffen Date: Sat, 31 Jan 2026 19:11:06 -0600 Subject: [PATCH 194/319] Add capacity to PlaceholderExpander --- compiler/rustc_expand/src/expand.rs | 4 +++- compiler/rustc_expand/src/placeholders.rs | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cfa7725c7400..dc2f28992105 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -508,6 +508,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { // Unresolved macros produce dummy outputs as a recovery measure. invocations.reverse(); let mut expanded_fragments = Vec::new(); + let mut expanded_fragments_len = 0; let mut undetermined_invocations = Vec::new(); let (mut progress, mut force) = (false, !self.monotonic); loop { @@ -602,6 +603,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { expanded_fragments.push(Vec::new()); } expanded_fragments[depth - 1].push((expn_id, expanded_fragment)); + expanded_fragments_len += 1; invocations.extend(derive_invocations.into_iter().rev()); } ExpandResult::Retry(invoc) => { @@ -622,7 +624,7 @@ impl<'a, 'b> MacroExpander<'a, 'b> { self.cx.force_mode = orig_force_mode; // Finally incorporate all the expanded macros into the input AST fragment. - let mut placeholder_expander = PlaceholderExpander::default(); + let mut placeholder_expander = PlaceholderExpander::with_capacity(expanded_fragments_len); while let Some(expanded_fragments) = expanded_fragments.pop() { for (expn_id, expanded_fragment) in expanded_fragments.into_iter().rev() { placeholder_expander diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 05f9a5aa43f7..2db18429a521 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -218,12 +218,17 @@ pub(crate) fn placeholder( } } -#[derive(Default)] pub(crate) struct PlaceholderExpander { expanded_fragments: FxHashMap, } impl PlaceholderExpander { + pub(crate) fn with_capacity(capacity: usize) -> Self { + PlaceholderExpander { + expanded_fragments: FxHashMap::with_capacity_and_hasher(capacity, Default::default()), + } + } + pub(crate) fn add(&mut self, id: ast::NodeId, mut fragment: AstFragment) { fragment.mut_visit_with(self); self.expanded_fragments.insert(id, fragment); From bf2536ae6498d04029ee9afac1cc28f7895bf725 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sat, 31 Jan 2026 22:17:10 +1100 Subject: [PATCH 195/319] Rename `QueryResult` to `ActiveKeyStatus` --- .../rustc_query_system/src/query/plumbing.rs | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 7e34957364e4..98bbd3ebc4a0 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -33,13 +33,24 @@ fn equivalent_key(k: &K) -> impl Fn(&(K, V)) -> bool + '_ { move |x| x.0 == *k } +/// For a particular query, keeps track of "active" keys, i.e. keys whose +/// evaluation has started but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) pub struct QueryState<'tcx, K> { - active: Sharded)>>, + active: Sharded)>>, } -/// Indicates the state of a query for a given key in a query map. -enum QueryResult<'tcx> { - /// An already executing query. The query job can be used to await for its completion. +/// For a particular query and key, tracks the status of a query evaluation +/// that has started, but has not yet finished successfully. +/// +/// (Successful query evaluation for a key is represented by an entry in the +/// query's in-memory cache.) +enum ActiveKeyStatus<'tcx> { + /// Some thread is already evaluating the query for this key. + /// + /// The enclosed [`QueryJob`] can be used to wait for it to finish. Started(QueryJob<'tcx>), /// The query panicked. Queries trying to wait on this will raise a fatal error which will @@ -47,8 +58,9 @@ enum QueryResult<'tcx> { Poisoned, } -impl<'tcx> QueryResult<'tcx> { - /// Unwraps the query job expecting that it has started. +impl<'tcx> ActiveKeyStatus<'tcx> { + /// Obtains the enclosed [`QueryJob`], or panics if this query evaluation + /// was poisoned by a panic. fn expect_job(self) -> QueryJob<'tcx> { match self { Self::Started(job) => job, @@ -76,9 +88,9 @@ where ) -> Option<()> { let mut active = Vec::new(); - let mut collect = |iter: LockGuard<'_, HashTable<(K, QueryResult<'tcx>)>>| { + let mut collect = |iter: LockGuard<'_, HashTable<(K, ActiveKeyStatus<'tcx>)>>| { for (k, v) in iter.iter() { - if let QueryResult::Started(ref job) = *v { + if let ActiveKeyStatus::Started(ref job) = *v { active.push((*k, job.clone())); } } @@ -222,7 +234,7 @@ where Err(_) => panic!(), Ok(occupied) => { let ((key, value), vacant) = occupied.remove(); - vacant.insert((key, QueryResult::Poisoned)); + vacant.insert((key, ActiveKeyStatus::Poisoned)); value.expect_job() } } @@ -319,7 +331,7 @@ where let shard = query.query_state(qcx).active.lock_shard_by_hash(key_hash); match shard.find(key_hash, equivalent_key(&key)) { // The query we waited on panicked. Continue unwinding here. - Some((_, QueryResult::Poisoned)) => FatalError.raise(), + Some((_, ActiveKeyStatus::Poisoned)) => FatalError.raise(), _ => panic!( "query '{}' result must be in the cache or the query must be poisoned after a wait", query.name() @@ -373,7 +385,7 @@ where // state map. let id = qcx.next_job_id(); let job = QueryJob::new(id, span, current_job_id); - entry.insert((key, QueryResult::Started(job))); + entry.insert((key, ActiveKeyStatus::Started(job))); // Drop the lock before we start executing the query drop(state_lock); @@ -382,7 +394,7 @@ where } Entry::Occupied(mut entry) => { match &mut entry.get_mut().1 { - QueryResult::Started(job) => { + ActiveKeyStatus::Started(job) => { if sync::is_dyn_thread_safe() { // Get the latch out let latch = job.latch(); @@ -400,7 +412,7 @@ where // so we just return the error. cycle_error(query, qcx, id, span) } - QueryResult::Poisoned => FatalError.raise(), + ActiveKeyStatus::Poisoned => FatalError.raise(), } } } From b4bf57b7aa46ae7dfd31c9a7468102342f91c713 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 25 Jan 2026 14:25:18 +1100 Subject: [PATCH 196/319] Move the `fingerprint_style` special case into `DepKindVTable` creation --- compiler/rustc_query_impl/src/plumbing.rs | 8 ++++++-- compiler/rustc_query_system/src/dep_graph/dep_node.rs | 5 +++-- compiler/rustc_query_system/src/dep_graph/mod.rs | 11 +++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..cfcfffa0fdfb 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -24,7 +24,7 @@ use rustc_middle::ty::codec::TyEncoder; use rustc_middle::ty::print::with_reduced_queries; use rustc_middle::ty::tls::{self, ImplicitCtxt}; use rustc_middle::ty::{self, TyCtxt}; -use rustc_query_system::dep_graph::{DepNodeParams, HasDepContext}; +use rustc_query_system::dep_graph::{DepNodeParams, FingerprintStyle, HasDepContext}; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{ QueryCache, QueryContext, QueryDispatcher, QueryJobId, QueryMap, QuerySideEffect, @@ -519,7 +519,11 @@ pub(crate) fn make_dep_kind_vtable_for_query<'tcx, Q>( where Q: QueryDispatcherUnerased<'tcx>, { - let fingerprint_style = ::Key::fingerprint_style(); + let fingerprint_style = if is_anon { + FingerprintStyle::Opaque + } else { + ::Key::fingerprint_style() + }; if is_anon || !fingerprint_style.reconstructible() { return DepKindVTable { diff --git a/compiler/rustc_query_system/src/dep_graph/dep_node.rs b/compiler/rustc_query_system/src/dep_graph/dep_node.rs index 72bdcd2d534d..b9d35bc5a937 100644 --- a/compiler/rustc_query_system/src/dep_graph/dep_node.rs +++ b/compiler/rustc_query_system/src/dep_graph/dep_node.rs @@ -237,8 +237,9 @@ pub struct DepKindVTable { /// cached within one compiler invocation. pub is_eval_always: bool, - /// Whether the query key can be recovered from the hashed fingerprint. - /// See [DepNodeParams] trait for the behaviour of each key type. + /// Indicates whether and how the query key can be recovered from its hashed fingerprint. + /// + /// The [`DepNodeParams`] trait determines the fingerprint style for each key type. pub fingerprint_style: FingerprintStyle, /// The red/green evaluation system will try to mark a specific DepNode in the diff --git a/compiler/rustc_query_system/src/dep_graph/mod.rs b/compiler/rustc_query_system/src/dep_graph/mod.rs index 8f714a2c96d6..c3ac1c7bc2d5 100644 --- a/compiler/rustc_query_system/src/dep_graph/mod.rs +++ b/compiler/rustc_query_system/src/dep_graph/mod.rs @@ -39,11 +39,7 @@ pub trait DepContext: Copy { #[inline(always)] fn fingerprint_style(self, kind: DepKind) -> FingerprintStyle { - let vtable = self.dep_kind_vtable(kind); - if vtable.is_anon { - return FingerprintStyle::Opaque; - } - vtable.fingerprint_style + self.dep_kind_vtable(kind).fingerprint_style } #[inline(always)] @@ -148,6 +144,9 @@ impl HasDepContext for (T, Q) { } /// Describes the contents of the fingerprint generated by a given query. +/// +/// This is mainly for determining whether and how we can reconstruct a key +/// from the fingerprint. #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum FingerprintStyle { /// The fingerprint is actually a DefPathHash. @@ -156,7 +155,7 @@ pub enum FingerprintStyle { HirId, /// Query key was `()` or equivalent, so fingerprint is just zero. Unit, - /// Some opaque hash. + /// The fingerprint is an opaque hash, and a key cannot be reconstructed from it. Opaque, } From a7dea5504bd3e706bf37bb158b25af8d5fe37c3c Mon Sep 17 00:00:00 2001 From: delta17920 Date: Sat, 31 Jan 2026 06:52:17 +0000 Subject: [PATCH 197/319] rename various regression tests --- .../closures/local-enums-in-closure-2074.rs | 13 ++++++-- .../struct-function-same-name-2445-b.rs | 30 ------------------- .../resolve/struct-function-same-name-2445.rs | 8 ++--- ...a.rs => struct-function-same-name-2487.rs} | 17 ++++++----- .../resolve/struct-function-same-name-2502.rs | 14 ++++----- .../resolve/struct-function-same-name-2550.rs | 10 +++---- tests/ui/structs/struct-update-syntax-2463.rs | 25 ++++++---------- 7 files changed, 41 insertions(+), 76 deletions(-) delete mode 100644 tests/ui/resolve/struct-function-same-name-2445-b.rs rename tests/ui/resolve/{struct-function-same-name-2487-a.rs => struct-function-same-name-2487.rs} (64%) diff --git a/tests/ui/closures/local-enums-in-closure-2074.rs b/tests/ui/closures/local-enums-in-closure-2074.rs index b6e3fb1fa23a..29cd6f13e3bb 100644 --- a/tests/ui/closures/local-enums-in-closure-2074.rs +++ b/tests/ui/closures/local-enums-in-closure-2074.rs @@ -1,15 +1,22 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2074 + //@ run-pass #![allow(non_camel_case_types)] pub fn main() { let one = || { - enum r { a } + enum r { + a, + } r::a as usize }; let two = || { - enum r { a } + enum r { + a, + } r::a as usize }; - one(); two(); + one(); + two(); } diff --git a/tests/ui/resolve/struct-function-same-name-2445-b.rs b/tests/ui/resolve/struct-function-same-name-2445-b.rs deleted file mode 100644 index 3a54c62a771b..000000000000 --- a/tests/ui/resolve/struct-function-same-name-2445-b.rs +++ /dev/null @@ -1,30 +0,0 @@ -//@ run-pass -#![allow(dead_code)] -#![allow(non_camel_case_types)] - - -struct c1 { - x: T, -} - -impl c1 { - pub fn f1(&self, _x: isize) { - } -} - -fn c1(x: T) -> c1 { - c1 { - x: x - } -} - -impl c1 { - pub fn f2(&self, _x: isize) { - } -} - - -pub fn main() { - c1::(3).f1(4); - c1::(3).f2(4); -} diff --git a/tests/ui/resolve/struct-function-same-name-2445.rs b/tests/ui/resolve/struct-function-same-name-2445.rs index e6c33a8fd016..8a0490efa3aa 100644 --- a/tests/ui/resolve/struct-function-same-name-2445.rs +++ b/tests/ui/resolve/struct-function-same-name-2445.rs @@ -1,8 +1,9 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2445 + //@ run-pass #![allow(dead_code)] #![allow(non_camel_case_types)] - struct c1 { x: T, } @@ -12,16 +13,13 @@ impl c1 { } fn c1(x: T) -> c1 { - c1 { - x: x - } + c1 { x } } impl c1 { pub fn f2(&self, _x: T) {} } - pub fn main() { c1::(3).f1(4); c1::(3).f2(4); diff --git a/tests/ui/resolve/struct-function-same-name-2487-a.rs b/tests/ui/resolve/struct-function-same-name-2487.rs similarity index 64% rename from tests/ui/resolve/struct-function-same-name-2487-a.rs rename to tests/ui/resolve/struct-function-same-name-2487.rs index d38616929fae..5f9a61c3260b 100644 --- a/tests/ui/resolve/struct-function-same-name-2487-a.rs +++ b/tests/ui/resolve/struct-function-same-name-2487.rs @@ -2,10 +2,8 @@ #![allow(dead_code)] #![allow(non_camel_case_types)] - struct socket { sock: isize, - } impl Drop for socket { @@ -13,19 +11,22 @@ impl Drop for socket { } impl socket { - pub fn set_identity(&self) { + pub fn set_identity(&self) { closure(|| setsockopt_bytes(self.sock.clone())) } } fn socket() -> socket { - socket { - sock: 1 - } + socket { sock: 1 } } -fn closure(f: F) where F: FnOnce() { f() } +fn closure(f: F) +where + F: FnOnce(), +{ + f() +} -fn setsockopt_bytes(_sock: isize) { } +fn setsockopt_bytes(_sock: isize) {} pub fn main() {} diff --git a/tests/ui/resolve/struct-function-same-name-2502.rs b/tests/ui/resolve/struct-function-same-name-2502.rs index 98a52a3b5a7d..5305c7d0a96f 100644 --- a/tests/ui/resolve/struct-function-same-name-2502.rs +++ b/tests/ui/resolve/struct-function-same-name-2502.rs @@ -1,11 +1,11 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2502 + //@ check-pass #![allow(dead_code)] #![allow(non_camel_case_types)] - - struct font<'a> { - fontbuf: &'a Vec , + fontbuf: &'a Vec, } impl<'a> font<'a> { @@ -14,10 +14,8 @@ impl<'a> font<'a> { } } -fn font(fontbuf: &Vec ) -> font<'_> { - font { - fontbuf: fontbuf - } +fn font(fontbuf: &Vec) -> font<'_> { + font { fontbuf } } -pub fn main() { } +pub fn main() {} diff --git a/tests/ui/resolve/struct-function-same-name-2550.rs b/tests/ui/resolve/struct-function-same-name-2550.rs index 450db9be627e..c96f58374c6d 100644 --- a/tests/ui/resolve/struct-function-same-name-2550.rs +++ b/tests/ui/resolve/struct-function-same-name-2550.rs @@ -1,20 +1,18 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2550 + //@ run-pass #![allow(dead_code)] #![allow(non_snake_case)] - struct C { x: usize, } fn C(x: usize) -> C { - C { - x: x - } + C { x } } -fn f(_x: T) { -} +fn f(_x: T) {} pub fn main() { f(C(1)); diff --git a/tests/ui/structs/struct-update-syntax-2463.rs b/tests/ui/structs/struct-update-syntax-2463.rs index 8fff9763bd9e..5b2a90a5adf9 100644 --- a/tests/ui/structs/struct-update-syntax-2463.rs +++ b/tests/ui/structs/struct-update-syntax-2463.rs @@ -1,24 +1,17 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/2463 + //@ run-pass #![allow(dead_code)] -struct Pair { f: isize, g: isize } +struct Pair { + f: isize, + g: isize, +} pub fn main() { + let x = Pair { f: 0, g: 0 }; - let x = Pair { - f: 0, - g: 0, - }; - - let _y = Pair { - f: 1, - g: 1, - .. x - }; - - let _z = Pair { - f: 1, - .. x - }; + let _y = Pair { f: 1, g: 1, ..x }; + let _z = Pair { f: 1, ..x }; } From 5c8d7dbe8ee508b35c033d25b115f164c7c72307 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:32:02 +0530 Subject: [PATCH 198/319] add cancel variant to SubResponse --- .../crates/proc-macro-api/src/bidirectional_protocol/msg.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs index 1df0c68379a5..3f0422dc5bc8 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol/msg.rs @@ -42,6 +42,9 @@ pub enum SubResponse { ByteRangeResult { range: Range, }, + Cancel { + reason: String, + }, } #[derive(Debug, Serialize, Deserialize)] From fb2cb46ea873f90c5a0a237772d4a42a2b7e4188 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:32:43 +0530 Subject: [PATCH 199/319] catch unwind on client side, and accordingly send Cancel subResponse to server --- .../proc-macro-api/src/bidirectional_protocol.rs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index 8311df23d718..ffdc6fdd4dec 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -2,6 +2,7 @@ use std::{ io::{self, BufRead, Write}, + panic::{AssertUnwindSafe, catch_unwind}, sync::Arc, }; @@ -55,9 +56,17 @@ pub fn run_conversation( return Ok(BidirectionalMessage::Response(response)); } BidirectionalMessage::SubRequest(sr) => { - let resp = callback(sr)?; - let reply = BidirectionalMessage::SubResponse(resp); - let encoded = postcard::encode(&reply).map_err(wrap_encode)?; + let resp = match catch_unwind(AssertUnwindSafe(|| callback(sr))) { + Ok(Ok(resp)) => BidirectionalMessage::SubResponse(resp), + Ok(Err(err)) => BidirectionalMessage::SubResponse(SubResponse::Cancel { + reason: err.to_string(), + }), + Err(_) => BidirectionalMessage::SubResponse(SubResponse::Cancel { + reason: "callback panicked or was cancelled".into(), + }), + }; + + let encoded = postcard::encode(&resp).map_err(wrap_encode)?; postcard::write(writer, &encoded) .map_err(wrap_io("failed to write sub-response"))?; } From d2ac252cf258f7d2d1962e31382e17363c8ff7ee Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:33:07 +0530 Subject: [PATCH 200/319] add proc-macro-client error variant --- src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index e04f744ae2b0..a6090253d3af 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -96,6 +96,14 @@ impl<'env> ProcMacroSrv<'env> { } } +#[derive(Debug)] +pub enum ProcMacroClientError { + Cancelled { reason: String }, + Io(std::io::Error), + Protocol(String), + Eof, +} + pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { From 26bc8cbc7797d8a988c5b096b14f121dac22035a Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 09:43:19 +0530 Subject: [PATCH 201/319] make sure we panic in callback so the srv panics and stops --- .../proc-macro-srv-cli/src/main_loop.rs | 84 +++++++++++++------ 1 file changed, 60 insertions(+), 24 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 758629fd1fd6..869c5f93927c 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -10,7 +10,7 @@ use std::{ use legacy::Message; -use proc_macro_srv::{EnvSnapshot, SpanId}; +use proc_macro_srv::{EnvSnapshot, ProcMacroClientError, SpanId}; struct SpanTrans; @@ -172,16 +172,26 @@ impl<'a> ProcMacroClientHandle<'a> { fn roundtrip( &mut self, req: bidirectional::SubRequest, - ) -> Option { + ) -> Result { let msg = bidirectional::BidirectionalMessage::SubRequest(req); - if msg.write(&mut *self.stdout).is_err() { - return None; - } + msg.write(&mut *self.stdout).map_err(ProcMacroClientError::Io)?; - match bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) { - Ok(Some(msg)) => Some(msg), - _ => None, + let msg = + bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) + .map_err(ProcMacroClientError::Io)? + .ok_or(ProcMacroClientError::Eof)?; + + match msg { + bidirectional::BidirectionalMessage::SubResponse(resp) => match resp { + bidirectional::SubResponse::Cancel { reason } => { + Err(ProcMacroClientError::Cancelled { reason }) + } + other => Ok(other), + }, + other => { + Err(ProcMacroClientError::Protocol(format!("expected SubResponse, got {other:?}"))) + } } } } @@ -189,10 +199,16 @@ impl<'a> ProcMacroClientHandle<'a> { impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::FilePathResult { name }, - )) => name, - _ => String::new(), + Ok(bidirectional::SubResponse::FilePathResult { name }) => name, + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in file(): {other:?}"); + } } } @@ -206,20 +222,32 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { start: range.start().into(), end: range.end().into(), }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::SourceTextResult { text }, - )) => text, - _ => None, + Ok(bidirectional::SubResponse::SourceTextResult { text }) => text, + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in source_text: {other:?}"); + } } } fn local_file(&mut self, file_id: proc_macro_srv::span::FileId) -> Option { match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id: file_id.index() }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::LocalFilePathResult { name }, - )) => name, - _ => None, + Ok(bidirectional::SubResponse::LocalFilePathResult { name }) => name, + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in local_file(): {other:?}"); + } } } @@ -230,10 +258,18 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { ast_id: anchor.ast_id.into_raw(), offset: range.start().into(), }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::LineColumnResult { line, column }, - )) => Some((line, column)), - _ => None, + Ok(bidirectional::SubResponse::LineColumnResult { line, column }) => { + Some((line, column)) + } + Err(ProcMacroClientError::Cancelled { reason }) => { + panic!("proc-macro expansion cancelled by client: {reason}"); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse in local_file(): {other:?}"); + } } } From 489245903e30492055a479b7e36b8efa505207e6 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Tue, 6 Jan 2026 21:12:39 +0530 Subject: [PATCH 202/319] don't kill server on cancellation --- .../crates/proc-macro-srv-cli/src/main_loop.rs | 11 ++++++----- .../rust-analyzer/crates/proc-macro-srv/src/lib.rs | 13 ++++++++++++- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 869c5f93927c..cae6d31f1a29 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -7,10 +7,11 @@ use std::{ io::{self, BufRead, Write}, ops::Range, }; +use std::panic::panic_any; use legacy::Message; -use proc_macro_srv::{EnvSnapshot, ProcMacroClientError, SpanId}; +use proc_macro_srv::{EnvSnapshot, ProcMacroCancelMarker, ProcMacroClientError, SpanId}; struct SpanTrans; @@ -201,7 +202,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::FilePathResult { name }) => name, Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); @@ -224,7 +225,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { }) { Ok(bidirectional::SubResponse::SourceTextResult { text }) => text, Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); @@ -240,7 +241,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { { Ok(bidirectional::SubResponse::LocalFilePathResult { name }) => name, Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); @@ -262,7 +263,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { Some((line, column)) } Err(ProcMacroClientError::Cancelled { reason }) => { - panic!("proc-macro expansion cancelled by client: {reason}"); + panic_any(ProcMacroCancelMarker { reason }); } Err(err) => { panic!("proc-macro IPC failed: {err:?}"); diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index a6090253d3af..99dfa24ca412 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -104,6 +104,11 @@ pub enum ProcMacroClientError { Eof, } +#[derive(Debug)] +pub struct ProcMacroCancelMarker { + pub reason: String, +} + pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); pub trait ProcMacroClientInterface { @@ -153,7 +158,13 @@ impl ProcMacroSrv<'_> { }); match thread.unwrap().join() { Ok(res) => res, - Err(e) => std::panic::resume_unwind(e), + + Err(payload) => { + if let Some(cancel) = payload.downcast_ref::() { + return Err(PanicMessage { message: Some(cancel.reason.clone()) }); + } + std::panic::resume_unwind(payload) + } } }); prev_env.rollback(); From 8d2811a7c312135c832432af94953c16cdf97f4c Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 10 Jan 2026 03:03:36 +0530 Subject: [PATCH 203/319] remove repititive error block in callbacks --- .../proc-macro-srv-cli/src/main_loop.rs | 56 +++++++------------ 1 file changed, 19 insertions(+), 37 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index cae6d31f1a29..534697c68684 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -197,19 +197,25 @@ impl<'a> ProcMacroClientHandle<'a> { } } -impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { +fn handle_failure(failure: Result) -> ! { + match failure { + Err(ProcMacroClientError::Cancelled { reason }) => { + panic_any(ProcMacroCancelMarker { reason }); + } + Err(err) => { + panic!("proc-macro IPC failed: {err:?}"); + } + Ok(other) => { + panic!("unexpected SubResponse {other:?}"); + } + } +} + +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::FilePathResult { name }) => name, - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in file(): {other:?}"); - } + other => handle_failure(other), } } @@ -224,15 +230,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { end: range.end().into(), }) { Ok(bidirectional::SubResponse::SourceTextResult { text }) => text, - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in source_text: {other:?}"); - } + other => handle_failure(other), } } @@ -240,15 +238,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { match self.roundtrip(bidirectional::SubRequest::LocalFilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::LocalFilePathResult { name }) => name, - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in local_file(): {other:?}"); - } + other => handle_failure(other), } } @@ -262,15 +252,7 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { Ok(bidirectional::SubResponse::LineColumnResult { line, column }) => { Some((line, column)) } - Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); - } - Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); - } - Ok(other) => { - panic!("unexpected SubResponse in local_file(): {other:?}"); - } + other => handle_failure(other), } } From 724606d3491faff9a4edb1b88ec7c3b1b4af923e Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sat, 10 Jan 2026 03:04:02 +0530 Subject: [PATCH 204/319] add error variant for cancelled expansion --- .../proc-macro-srv-cli/src/main_loop.rs | 9 +++---- .../crates/proc-macro-srv/src/lib.rs | 26 +++++++++++++++---- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 534697c68684..bb94d33ac102 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -3,11 +3,11 @@ use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, }; +use std::panic::panic_any; use std::{ io::{self, BufRead, Write}, ops::Range, }; -use std::panic::panic_any; use legacy::Message; @@ -178,10 +178,9 @@ impl<'a> ProcMacroClientHandle<'a> { msg.write(&mut *self.stdout).map_err(ProcMacroClientError::Io)?; - let msg = - bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) - .map_err(ProcMacroClientError::Io)? - .ok_or(ProcMacroClientError::Eof)?; + let msg = bidirectional::BidirectionalMessage::read(&mut *self.stdin, self.buf) + .map_err(ProcMacroClientError::Io)? + .ok_or(ProcMacroClientError::Eof)?; match msg { bidirectional::BidirectionalMessage::SubResponse(resp) => match resp { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 99dfa24ca412..0462aafd00e7 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -123,6 +123,20 @@ pub trait ProcMacroClientInterface { const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; +pub enum ExpandError { + Panic(PanicMessage), + Cancelled { reason: Option }, +} + +impl ExpandError { + pub fn into_string(self) -> Option { + match self { + ExpandError::Panic(panic_message) => panic_message.into_string(), + ExpandError::Cancelled { reason } => reason, + } + } +} + impl ProcMacroSrv<'_> { pub fn expand( &self, @@ -136,10 +150,12 @@ impl ProcMacroSrv<'_> { call_site: S, mixed_site: S, callback: Option>, - ) -> Result, PanicMessage> { + ) -> Result, ExpandError> { let snapped_env = self.env; - let expander = self.expander(lib.as_ref()).map_err(|err| PanicMessage { - message: Some(format!("failed to load macro: {err}")), + let expander = self.expander(lib.as_ref()).map_err(|err| { + ExpandError::Panic(PanicMessage { + message: Some(format!("failed to load macro: {err}")), + }) })?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); @@ -157,11 +173,11 @@ impl ProcMacroSrv<'_> { ) }); match thread.unwrap().join() { - Ok(res) => res, + Ok(res) => res.map_err(ExpandError::Panic), Err(payload) => { if let Some(cancel) = payload.downcast_ref::() { - return Err(PanicMessage { message: Some(cancel.reason.clone()) }); + return Err(ExpandError::Cancelled { reason: Some(cancel.reason.clone()) }); } std::panic::resume_unwind(payload) } From a37aa50bca6a7f424907e9f3f638a15935963ef8 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 16:49:22 +0530 Subject: [PATCH 205/319] adapt ByteRange to new roundtrip --- .../crates/proc-macro-srv-cli/src/main_loop.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index bb94d33ac102..af8c3cb39ba2 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -265,10 +265,8 @@ impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandl start: range.start().into(), end: range.end().into(), }) { - Some(bidirectional::BidirectionalMessage::SubResponse( - bidirectional::SubResponse::ByteRangeResult { range }, - )) => range, - _ => Range { start: range.start().into(), end: range.end().into() }, + Ok(bidirectional::SubResponse::ByteRangeResult { range }) => range, + other => handle_failure(other), } } } From 50e2330dce8a9d7a9adfeee2c219fc00487b4dbd Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 11 Jan 2026 18:27:30 +0530 Subject: [PATCH 206/319] add suggested changes: have a internal error variant, comment on unwindsafe --- .../src/bidirectional_protocol.rs | 2 ++ .../proc-macro-srv-cli/src/main_loop.rs | 12 ++++++--- .../crates/proc-macro-srv/src/lib.rs | 25 +++++++++++++------ 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs index ffdc6fdd4dec..ba59cb219b9a 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-api/src/bidirectional_protocol.rs @@ -56,6 +56,8 @@ pub fn run_conversation( return Ok(BidirectionalMessage::Response(response)); } BidirectionalMessage::SubRequest(sr) => { + // TODO: Avoid `AssertUnwindSafe` by making the callback `UnwindSafe` once `ExpandDatabase` + // becomes unwind-safe (currently blocked by `parking_lot::RwLock` in the VFS). let resp = match catch_unwind(AssertUnwindSafe(|| callback(sr))) { Ok(Ok(resp)) => BidirectionalMessage::SubResponse(resp), Ok(Err(err)) => BidirectionalMessage::SubResponse(SubResponse::Cancel { diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index af8c3cb39ba2..c19847b4b53d 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -11,7 +11,7 @@ use std::{ use legacy::Message; -use proc_macro_srv::{EnvSnapshot, ProcMacroCancelMarker, ProcMacroClientError, SpanId}; +use proc_macro_srv::{EnvSnapshot, ProcMacroClientError, ProcMacroPanicMarker, SpanId}; struct SpanTrans; @@ -199,13 +199,17 @@ impl<'a> ProcMacroClientHandle<'a> { fn handle_failure(failure: Result) -> ! { match failure { Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroCancelMarker { reason }); + panic_any(ProcMacroPanicMarker::Cancelled { reason }); } Err(err) => { - panic!("proc-macro IPC failed: {err:?}"); + panic_any(ProcMacroPanicMarker::Internal { + reason: format!("proc-macro IPC error: {err:?}"), + }); } Ok(other) => { - panic!("unexpected SubResponse {other:?}"); + panic_any(ProcMacroPanicMarker::Internal { + reason: format!("unexpected SubResponse {other:?}"), + }); } } } diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 0462aafd00e7..c548dc620ad1 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -105,8 +105,9 @@ pub enum ProcMacroClientError { } #[derive(Debug)] -pub struct ProcMacroCancelMarker { - pub reason: String, +pub enum ProcMacroPanicMarker { + Cancelled { reason: String }, + Internal { reason: String }, } pub type ProcMacroClientHandle<'a> = &'a mut (dyn ProcMacroClientInterface + Sync + Send); @@ -126,6 +127,7 @@ const EXPANDER_STACK_SIZE: usize = 8 * 1024 * 1024; pub enum ExpandError { Panic(PanicMessage), Cancelled { reason: Option }, + Internal { reason: Option }, } impl ExpandError { @@ -133,6 +135,7 @@ impl ExpandError { match self { ExpandError::Panic(panic_message) => panic_message.into_string(), ExpandError::Cancelled { reason } => reason, + ExpandError::Internal { reason } => reason, } } } @@ -152,10 +155,8 @@ impl ProcMacroSrv<'_> { callback: Option>, ) -> Result, ExpandError> { let snapped_env = self.env; - let expander = self.expander(lib.as_ref()).map_err(|err| { - ExpandError::Panic(PanicMessage { - message: Some(format!("failed to load macro: {err}")), - }) + let expander = self.expander(lib.as_ref()).map_err(|err| ExpandError::Internal { + reason: Some(format!("failed to load macro: {err}")), })?; let prev_env = EnvChange::apply(snapped_env, env, current_dir.as_ref().map(<_>::as_ref)); @@ -176,9 +177,17 @@ impl ProcMacroSrv<'_> { Ok(res) => res.map_err(ExpandError::Panic), Err(payload) => { - if let Some(cancel) = payload.downcast_ref::() { - return Err(ExpandError::Cancelled { reason: Some(cancel.reason.clone()) }); + if let Some(marker) = payload.downcast_ref::() { + return match marker { + ProcMacroPanicMarker::Cancelled { reason } => { + Err(ExpandError::Cancelled { reason: Some(reason.clone()) }) + } + ProcMacroPanicMarker::Internal { reason } => { + Err(ExpandError::Internal { reason: Some(reason.clone()) }) + } + }; } + std::panic::resume_unwind(payload) } } From 98380734814667ba50ad748d8130a77b06ebaa3f Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 18 Jan 2026 21:10:32 +0530 Subject: [PATCH 207/319] replace panic_any with resume_unwind on Client panic cancelled message --- .../rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index c19847b4b53d..1e956f91e6b9 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -3,7 +3,7 @@ use proc_macro_api::{ ProtocolFormat, bidirectional_protocol::msg as bidirectional, legacy_protocol::msg as legacy, version::CURRENT_API_VERSION, }; -use std::panic::panic_any; +use std::panic::{panic_any, resume_unwind}; use std::{ io::{self, BufRead, Write}, ops::Range, @@ -199,7 +199,7 @@ impl<'a> ProcMacroClientHandle<'a> { fn handle_failure(failure: Result) -> ! { match failure { Err(ProcMacroClientError::Cancelled { reason }) => { - panic_any(ProcMacroPanicMarker::Cancelled { reason }); + resume_unwind(Box::new(ProcMacroPanicMarker::Cancelled { reason })); } Err(err) => { panic_any(ProcMacroPanicMarker::Internal { From fcb55722d4ef6623f36a9787c7724bd364adb413 Mon Sep 17 00:00:00 2001 From: bit-aloo Date: Sun, 1 Feb 2026 15:19:03 +0530 Subject: [PATCH 208/319] correct handler generic input --- .../rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs index 1e956f91e6b9..9be3199a3836 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/src/main_loop.rs @@ -214,7 +214,7 @@ fn handle_failure(failure: Result proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_, C> { +impl proc_macro_srv::ProcMacroClientInterface for ProcMacroClientHandle<'_> { fn file(&mut self, file_id: proc_macro_srv::span::FileId) -> String { match self.roundtrip(bidirectional::SubRequest::FilePath { file_id: file_id.index() }) { Ok(bidirectional::SubResponse::FilePathResult { name }) => name, From 8927aa57385144c22a2cf9060a2f7a74071d2e0e Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 17:46:36 +0100 Subject: [PATCH 209/319] Add `inline` syntax for diagnostic messages --- compiler/rustc_error_messages/src/lib.rs | 11 ++- compiler/rustc_errors/src/translation.rs | 26 ++++++- .../src/diagnostics/diagnostic.rs | 15 ++-- .../src/diagnostics/diagnostic_builder.rs | 76 ++++++++++++------- .../rustc_macros/src/diagnostics/message.rs | 28 +++++++ compiler/rustc_macros/src/diagnostics/mod.rs | 1 + .../src/diagnostics/subdiagnostic.rs | 10 ++- .../rustc_macros/src/diagnostics/utils.rs | 26 ++++++- 8 files changed, 151 insertions(+), 42 deletions(-) create mode 100644 compiler/rustc_macros/src/diagnostics/message.rs diff --git a/compiler/rustc_error_messages/src/lib.rs b/compiler/rustc_error_messages/src/lib.rs index c0737edd7d65..c9e887061305 100644 --- a/compiler/rustc_error_messages/src/lib.rs +++ b/compiler/rustc_error_messages/src/lib.rs @@ -247,6 +247,9 @@ pub enum SubdiagMessage { /// Identifier of a Fluent message. Instances of this variant are generated by the /// `Subdiagnostic` derive. FluentIdentifier(FluentId), + /// An inline Fluent message. Instances of this variant are generated by the + /// `Subdiagnostic` derive. + Inline(Cow<'static, str>), /// Attribute of a Fluent message. Needs to be combined with a Fluent identifier to produce an /// actual translated message. Instances of this variant are generated by the `fluent_messages` /// macro. @@ -291,6 +294,8 @@ pub enum DiagMessage { /// /// FluentIdentifier(FluentId, Option), + /// An inline Fluent message, containing the to be translated diagnostic message. + Inline(Cow<'static, str>), } impl DiagMessage { @@ -305,21 +310,22 @@ impl DiagMessage { SubdiagMessage::FluentIdentifier(id) => { return DiagMessage::FluentIdentifier(id, None); } + SubdiagMessage::Inline(s) => return DiagMessage::Inline(s), SubdiagMessage::FluentAttr(attr) => attr, }; match self { - DiagMessage::Str(s) => DiagMessage::Str(s.clone()), DiagMessage::FluentIdentifier(id, _) => { DiagMessage::FluentIdentifier(id.clone(), Some(attr)) } + _ => panic!("Tried to add a subdiagnostic to a message without a fluent identifier"), } } pub fn as_str(&self) -> Option<&str> { match self { DiagMessage::Str(s) => Some(s), - DiagMessage::FluentIdentifier(_, _) => None, + DiagMessage::FluentIdentifier(_, _) | DiagMessage::Inline(_) => None, } } } @@ -353,6 +359,7 @@ impl From for SubdiagMessage { // There isn't really a sensible behaviour for this because it loses information but // this is the most sensible of the behaviours. DiagMessage::FluentIdentifier(_, Some(attr)) => SubdiagMessage::FluentAttr(attr), + DiagMessage::Inline(s) => SubdiagMessage::Inline(s), } } } diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 43d5fca301ec..0ee2b7b06090 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -3,11 +3,13 @@ use std::env; use std::error::Report; use std::sync::Arc; +use rustc_error_messages::langid; pub use rustc_error_messages::{FluentArgs, LazyFallbackBundle}; use tracing::{debug, trace}; use crate::error::{TranslateError, TranslateErrorKind}; -use crate::{DiagArg, DiagMessage, FluentBundle, Style}; +use crate::fluent_bundle::FluentResource; +use crate::{DiagArg, DiagMessage, FluentBundle, Style, fluent_bundle}; /// Convert diagnostic arguments (a rustc internal type that exists to implement /// `Encodable`/`Decodable`) into `FluentArgs` which is necessary to perform translation. @@ -79,6 +81,28 @@ impl Translator { return Ok(Cow::Borrowed(msg)); } DiagMessage::FluentIdentifier(identifier, attr) => (identifier, attr), + // This translates an inline fluent diagnostic message + // It does this by creating a new `FluentBundle` with only one message, + // and then translating using this bundle. + DiagMessage::Inline(msg) => { + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = + FluentResource::try_new(format!("{GENERATED_MSG_ID} = {msg}\n")).unwrap(); + let mut bundle = fluent_bundle::FluentBundle::new(vec![langid!("en-US")]); + bundle.set_use_isolating(false); + bundle.add_resource(resource).unwrap(); + let message = bundle.get_message(GENERATED_MSG_ID).unwrap(); + let value = message.value().unwrap(); + + let mut errs = vec![]; + let translated = bundle.format_pattern(value, Some(args), &mut errs).to_string(); + debug!(?translated, ?errs); + return if errs.is_empty() { + Ok(Cow::Owned(translated)) + } else { + Err(TranslateError::fluent(&Cow::Borrowed(GENERATED_MSG_ID), args, errs)) + }; + } }; let translate_with_bundle = |bundle: &'a FluentBundle| -> Result, TranslateError<'_>> { diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 7e784e3464e9..1ae6393b2860 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -27,15 +27,17 @@ impl<'a> DiagnosticDerive<'a> { let preamble = builder.preamble(variant); let body = builder.body(variant); - let Some(slug) = builder.primary_message() else { + let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().push(slug.clone()); + slugs.borrow_mut().extend(message.slug().cloned()); + let message = message.diag_message(); + let init = quote! { let mut diag = rustc_errors::Diag::new( dcx, level, - crate::fluent_generated::#slug + #message ); }; @@ -91,12 +93,13 @@ impl<'a> LintDiagnosticDerive<'a> { let preamble = builder.preamble(variant); let body = builder.body(variant); - let Some(slug) = builder.primary_message() else { + let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().push(slug.clone()); + slugs.borrow_mut().extend(message.slug().cloned()); + let message = message.diag_message(); let primary_message = quote! { - diag.primary_message(crate::fluent_generated::#slug); + diag.primary_message(#message); }; let formatting_init = &builder.formatting_init; diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 25110fd4f908..14eda017970f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -4,13 +4,14 @@ use proc_macro2::{Ident, Span, TokenStream}; use quote::{format_ident, quote, quote_spanned}; use syn::parse::ParseStream; use syn::spanned::Spanned; -use syn::{Attribute, Meta, Path, Token, Type, parse_quote}; +use syn::{Attribute, LitStr, Meta, Path, Token, Type, parse_quote}; use synstructure::{BindingInfo, Structure, VariantInfo}; use super::utils::SubdiagnosticVariant; use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; use crate::diagnostics::utils::{ FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind, build_field_mapping, is_doc_comment, report_error_if_not_applied_to_span, report_type_error, @@ -41,9 +42,9 @@ pub(crate) struct DiagnosticDeriveVariantBuilder { /// derive builder. pub field_map: FieldMap, - /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that + /// Message is a mandatory part of the struct attribute as corresponds to the Fluent message that /// has the actual diagnostic message. - pub slug: Option, + pub message: Option, /// Error codes are a optional part of the struct attribute - this is only set to detect /// multiple specifications. @@ -90,7 +91,7 @@ impl DiagnosticDeriveKind { span, field_map: build_field_mapping(variant), formatting_init: TokenStream::new(), - slug: None, + message: None, code: None, }; f(builder, variant) @@ -105,8 +106,8 @@ impl DiagnosticDeriveKind { } impl DiagnosticDeriveVariantBuilder { - pub(crate) fn primary_message(&self) -> Option<&Path> { - match self.slug.as_ref() { + pub(crate) fn primary_message(&self) -> Option<&Message> { + match self.message.as_ref() { None => { span_err(self.span, "diagnostic slug not specified") .help( @@ -116,7 +117,7 @@ impl DiagnosticDeriveVariantBuilder { .emit(); None } - Some(slug) + Some(Message::Slug(slug)) if let Some(Mismatch { slug_name, crate_name, slug_prefix }) = Mismatch::check(slug) => { @@ -126,7 +127,7 @@ impl DiagnosticDeriveVariantBuilder { .emit(); None } - Some(slug) => Some(slug), + Some(msg) => Some(msg), } } @@ -163,7 +164,7 @@ impl DiagnosticDeriveVariantBuilder { fn parse_subdiag_attribute( &self, attr: &Attribute, - ) -> Result, DiagnosticDeriveError> { + ) -> Result, DiagnosticDeriveError> { let Some(subdiag) = SubdiagnosticVariant::from_attr(attr, &self.field_map)? else { // Some attributes aren't errors - like documentation comments - but also aren't // subdiagnostics. @@ -175,15 +176,18 @@ impl DiagnosticDeriveVariantBuilder { .help("consider creating a `Subdiagnostic` instead")); } - let slug = subdiag.slug.unwrap_or_else(|| match subdiag.kind { - SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, - SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, - SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, - SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, - SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, - SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, - SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, - SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + // For subdiagnostics without a message specified, insert a placeholder slug + let slug = subdiag.slug.unwrap_or_else(|| { + Message::Slug(match subdiag.kind { + SubdiagnosticKind::Label => parse_quote! { _subdiag::label }, + SubdiagnosticKind::Note => parse_quote! { _subdiag::note }, + SubdiagnosticKind::NoteOnce => parse_quote! { _subdiag::note_once }, + SubdiagnosticKind::Help => parse_quote! { _subdiag::help }, + SubdiagnosticKind::HelpOnce => parse_quote! { _subdiag::help_once }, + SubdiagnosticKind::Warn => parse_quote! { _subdiag::warn }, + SubdiagnosticKind::Suggestion { .. } => parse_quote! { _subdiag::suggestion }, + SubdiagnosticKind::MultipartSuggestion { .. } => unreachable!(), + }) }); Ok(Some((subdiag.kind, slug, false))) @@ -210,13 +214,28 @@ impl DiagnosticDeriveVariantBuilder { let mut input = &*input; let slug_recovery_point = input.fork(); - let slug = input.parse::()?; - if input.is_empty() || input.peek(Token![,]) { - self.slug = Some(slug); + if input.peek(LitStr) { + // Parse an inline message + let message = input.parse::()?; + if !message.suffix().is_empty() { + span_err( + message.span().unwrap(), + "Inline message is not allowed to have a suffix", + ) + .emit(); + } + self.message = Some(Message::Inline(message.value())); } else { - input = &slug_recovery_point; + // Parse a slug + let slug = input.parse::()?; + if input.is_empty() || input.peek(Token![,]) { + self.message = Some(Message::Slug(slug)); + } else { + input = &slug_recovery_point; + } } + // Parse arguments while !input.is_empty() { input.parse::()?; // Allow trailing comma @@ -429,6 +448,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } + let message = slug.diag_message(); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -438,7 +458,7 @@ impl DiagnosticDeriveVariantBuilder { Ok(quote! { diag.span_suggestions_with_style( #span_field, - crate::fluent_generated::#slug, + #message, #code_field, #applicability, #style @@ -455,22 +475,24 @@ impl DiagnosticDeriveVariantBuilder { &self, field_binding: TokenStream, kind: &Ident, - fluent_attr_identifier: Path, + message: Message, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); + let message = message.diag_message(); quote! { diag.#fn_name( #field_binding, - crate::fluent_generated::#fluent_attr_identifier + #message ); } } /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug /// and `fluent_attr_identifier`. - fn add_subdiagnostic(&self, kind: &Ident, fluent_attr_identifier: Path) -> TokenStream { + fn add_subdiagnostic(&self, kind: &Ident, message: Message) -> TokenStream { + let message = message.diag_message(); quote! { - diag.#kind(crate::fluent_generated::#fluent_attr_identifier); + diag.#kind(#message); } } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs new file mode 100644 index 000000000000..cfce252fbdd8 --- /dev/null +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -0,0 +1,28 @@ +use proc_macro2::TokenStream; +use quote::quote; +use syn::Path; + +pub(crate) enum Message { + Slug(Path), + Inline(String), +} + +impl Message { + pub(crate) fn slug(&self) -> Option<&Path> { + match self { + Message::Slug(slug) => Some(slug), + Message::Inline(_) => None, + } + } + + pub(crate) fn diag_message(&self) -> TokenStream { + match self { + Message::Slug(slug) => { + quote! { crate::fluent_generated::#slug } + } + Message::Inline(message) => { + quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } + } + } + } +} diff --git a/compiler/rustc_macros/src/diagnostics/mod.rs b/compiler/rustc_macros/src/diagnostics/mod.rs index 55228248188e..09f05ce972f1 100644 --- a/compiler/rustc_macros/src/diagnostics/mod.rs +++ b/compiler/rustc_macros/src/diagnostics/mod.rs @@ -1,6 +1,7 @@ mod diagnostic; mod diagnostic_builder; mod error; +mod message; mod subdiagnostic; mod utils; diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index db2a19ab85ba..61a234f96d12 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -11,6 +11,7 @@ use super::utils::SubdiagnosticVariant; use crate::diagnostics::error::{ DiagnosticDeriveError, invalid_attr, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; use crate::diagnostics::utils::{ AllowMultipleAlternatives, FieldInfo, FieldInnerTy, FieldMap, SetOnce, SpannedOption, SubdiagnosticKind, build_field_mapping, build_suggestion_code, is_doc_comment, new_code_ident, @@ -182,7 +183,9 @@ impl<'a> FromIterator<&'a SubdiagnosticKind> for KindsStatistics { } impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { - fn identify_kind(&mut self) -> Result, DiagnosticDeriveError> { + fn identify_kind( + &mut self, + ) -> Result, DiagnosticDeriveError> { let mut kind_slugs = vec![]; for attr in self.variant.ast().attrs { @@ -532,9 +535,8 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { let message = format_ident!("__message"); - calls.extend( - quote! { let #message = #diag.eagerly_translate(crate::fluent_generated::#slug); }, - ); + let message_stream = slug.diag_message(); + calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); let call = match kind { diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index f084ba60ae3f..0718448a0513 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -16,6 +16,7 @@ use super::error::invalid_attr; use crate::diagnostics::error::{ DiagnosticDeriveError, span_err, throw_invalid_attr, throw_span_err, }; +use crate::diagnostics::message::Message; thread_local! { pub(crate) static CODE_IDENT_COUNT: RefCell = RefCell::new(0); @@ -587,7 +588,7 @@ pub(super) enum SubdiagnosticKind { pub(super) struct SubdiagnosticVariant { pub(super) kind: SubdiagnosticKind, - pub(super) slug: Option, + pub(super) slug: Option, } impl SubdiagnosticVariant { @@ -696,11 +697,31 @@ impl SubdiagnosticVariant { list.parse_args_with(|input: ParseStream<'_>| { let mut is_first = true; while !input.is_empty() { + // Try to parse an inline diagnostic message + if input.peek(LitStr) { + let message = input.parse::()?; + if !message.suffix().is_empty() { + span_err( + message.span().unwrap(), + "Inline message is not allowed to have a suffix", + ).emit(); + } + if !input.is_empty() { input.parse::()?; } + if is_first { + slug = Some(Message::Inline(message.value())); + is_first = false; + } else { + span_err(message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit(); + } + continue + } + + // Try to parse a slug instead let arg_name: Path = input.parse::()?; let arg_name_span = arg_name.span().unwrap(); if input.is_empty() || input.parse::().is_ok() { if is_first { - slug = Some(arg_name); + slug = Some(Message::Slug(arg_name)); is_first = false; } else { span_err(arg_name_span, "a diagnostic slug must be the first argument to the attribute").emit(); @@ -709,6 +730,7 @@ impl SubdiagnosticVariant { } is_first = false; + // Try to parse an argument match (arg_name.require_ident()?.to_string().as_str(), &mut kind) { ("code", SubdiagnosticKind::Suggestion { code_field, .. }) => { let code_init = build_suggestion_code( From 378c6fc6fe1ac3315364b0864068c7957b432b1a Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 19:26:33 +0100 Subject: [PATCH 210/319] Uitests for `#[diagnostic]` --- .../diagnostic-derive-inline.rs | 764 ++++++++++++++++++ .../diagnostic-derive-inline.stderr | 627 ++++++++++++++ 2 files changed, 1391 insertions(+) create mode 100644 tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs create mode 100644 tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs new file mode 100644 index 000000000000..7d9af0522f63 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs @@ -0,0 +1,764 @@ +//@ check-fail +// Tests error conditions for specifying diagnostics using #[derive(Diagnostic)] +// This test specifically tests diagnostic derives involving the inline fluent syntax. + +//@ normalize-stderr: "the following other types implement trait `IntoDiagArg`:(?:.*\n){0,9}\s+and \d+ others" -> "normalized in stderr" +//@ normalize-stderr: "(COMPILER_DIR/.*\.rs):[0-9]+:[0-9]+" -> "$1:LL:CC" + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Diagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_span; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +extern crate rustc_fluent_macro; +extern crate rustc_macros; +use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; + +extern crate rustc_middle; +use rustc_middle::ty::Ty; + +extern crate rustc_errors; +use rustc_errors::{Applicability, DiagMessage, ErrCode, MultiSpan, SubdiagMessage}; + +extern crate rustc_session; + +extern crate core; + +// E0123 and E0456 are no longer used, so we define our own constants here just for this test. +const E0123: ErrCode = ErrCode::from_u32(0123); +const E0456: ErrCode = ErrCode::from_u32(0456); + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct Hello {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +//~^ ERROR unsupported type attribute for diagnostic derive enum +enum DiagnosticOnEnum { + Foo, + //~^ ERROR diagnostic slug not specified + Bar, + //~^ ERROR diagnostic slug not specified +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[diag = "E0123"] +//~^ ERROR expected parentheses: #[diag(...)] +struct WrongStructAttrStyle {} + +#[derive(Diagnostic)] +#[nonsense("this is an example message", code = E0123)] +//~^ ERROR `#[nonsense(...)]` is not a valid attribute +//~^^ ERROR diagnostic slug not specified +//~^^^ ERROR cannot find attribute `nonsense` in this scope +struct InvalidStructAttr {} + +#[derive(Diagnostic)] +#[diag(code = E0123)] +//~^ ERROR diagnostic slug not specified +struct InvalidLitNestedAttr {} + +#[derive(Diagnostic)] +#[diag(nonsense("foo"), code = E0123, slug = "foo")] +//~^ ERROR derive(Diagnostic): diagnostic slug not specified +struct InvalidNestedStructAttr1 {} + +#[derive(Diagnostic)] +#[diag(nonsense = "...", code = E0123, slug = "foo")] +//~^ ERROR diagnostic slug not specified +struct InvalidNestedStructAttr2 {} + +#[derive(Diagnostic)] +#[diag(nonsense = 4, code = E0123, slug = "foo")] +//~^ ERROR diagnostic slug not specified +struct InvalidNestedStructAttr3 {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123, slug = "foo")] +//~^ ERROR unknown argument +struct InvalidNestedStructAttr4 {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WrongPlaceField { + #[suggestion = "bar"] + //~^ ERROR `#[suggestion = ...]` is not a valid attribute + sp: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[diag("this is an example message", code = E0456)] +//~^ ERROR specified multiple times +struct DiagSpecifiedTwice {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123, code = E0456)] +//~^ ERROR specified multiple times +struct CodeSpecifiedTwice {} + +#[derive(Diagnostic)] +#[diag("this is an example message", no_crate::example, code = E0123)] +//~^ ERROR diagnostic slug must be the first argument +struct SlugSpecifiedTwice {} + +#[derive(Diagnostic)] +struct KindNotProvided {} //~ ERROR diagnostic slug not specified + +#[derive(Diagnostic)] +#[diag(code = E0123)] +//~^ ERROR diagnostic slug not specified +struct SlugNotProvided {} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct CodeNotProvided {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MessageWrongType { + #[primary_span] + //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + foo: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidPathFieldAttr { + #[nonsense] + //~^ ERROR `#[nonsense]` is not a valid attribute + //~^^ ERROR cannot find attribute `nonsense` in this scope + foo: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithField { + name: String, + #[label("with a label")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithMessageAppliedToField { + #[label("with a label")] + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + name: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithNonexistentField { + #[suggestion("with a suggestion", code = "{name}")] + //~^ ERROR `name` doesn't refer to a field on this type + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +//~^ ERROR invalid format string: expected `}` +#[diag("this is an example message", code = E0123)] +struct ErrorMissingClosingBrace { + #[suggestion("with a suggestion", code = "{name")] + suggestion: (Span, Applicability), + name: String, + val: usize, +} + +#[derive(Diagnostic)] +//~^ ERROR invalid format string: unmatched `}` +#[diag("this is an example message", code = E0123)] +struct ErrorMissingOpeningBrace { + #[suggestion("with a suggestion", code = "name}")] + suggestion: (Span, Applicability), + name: String, + val: usize, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelOnSpan { + #[label("with a label")] + sp: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelOnNonSpan { + #[label("with a label")] + //~^ ERROR the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + id: u32, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct Suggest { + #[suggestion("with a suggestion", code = "This is the suggested code")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "normal")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "short")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "hidden")] + #[suggestion("with a suggestion", code = "This is the suggested code", style = "verbose")] + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithoutCode { + #[suggestion("with a suggestion")] + //~^ ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithBadKey { + #[suggestion("with a suggestion", nonsense = "bar")] + //~^ ERROR invalid nested attribute + //~| ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithShorthandMsg { + #[suggestion("with a suggestion", msg = "bar")] + //~^ ERROR invalid nested attribute + //~| ERROR suggestion without `code = "..."` + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithoutMsg { + #[suggestion("with a suggestion", code = "bar")] + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithTypesSwapped { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Applicability, Span), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithWrongTypeApplicabilityOnly { + #[suggestion("with a suggestion", code = "This is suggested code")] + //~^ ERROR wrong field type for suggestion + suggestion: Applicability, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithSpanOnly { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithDuplicateSpanAndApplicability { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Span, Span, Applicability), + //~^ ERROR specified multiple times +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct SuggestWithDuplicateApplicabilityAndSpan { + #[suggestion("with a suggestion", code = "This is suggested code")] + suggestion: (Applicability, Applicability, Span), + //~^ ERROR specified multiple times +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WrongKindOfAnnotation { + #[label = "bar"] + //~^ ERROR `#[label = ...]` is not a valid attribute + z: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct OptionsInErrors { + #[label("with a label")] + label: Option, + #[suggestion("with a suggestion", code = "...")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MoveOutOfBorrowError<'tcx> { + name: Ident, + ty: Ty<'tcx>, + #[primary_span] + #[label("with a label")] + span: Span, + #[label("with a label")] + other_span: Span, + #[suggestion("with a suggestion", code = "{name}.clone()")] + opt_sugg: Option<(Span, Applicability)>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithLifetime<'a> { + #[label("with a label")] + span: Span, + name: &'a str, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ArgFieldWithoutSkip { + #[primary_span] + span: Span, + other: Hello, + //~^ ERROR the trait bound `Hello: IntoDiagArg` is not satisfied +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ArgFieldWithSkip { + #[primary_span] + span: Span, + // `Hello` does not implement `IntoDiagArg` so this would result in an error if + // not for `#[skip_arg]`. + #[skip_arg] + other: Hello, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithSpannedNote { + #[note("with a note")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[note("with a note")] +struct ErrorWithNote { + val: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithSpannedHelpCustom { + #[help("with a help")] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[help("with a help")] +struct ErrorWithHelp { + val: String, +} + +#[derive(Diagnostic)] +#[help("with a help")] +#[diag("this is an example message", code = E0123)] +struct ErrorWithHelpWrongOrder { + val: String, +} + +#[derive(Diagnostic)] +#[note("with a note")] +#[diag("this is an example message", code = E0123)] +struct ErrorWithNoteWrongOrder { + val: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ApplicabilityInBoth { + #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + //~^ ERROR specified multiple times + suggestion: (Span, Applicability), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidApplicability { + #[suggestion("with a suggestion", code = "...", applicability = "batman")] + //~^ ERROR invalid applicability + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ValidApplicability { + #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct NoApplicability { + #[suggestion("with a suggestion", code = "...")] + suggestion: Span, +} + +#[derive(Subdiagnostic)] +#[note("this is an example message")] +struct Note; + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct Subdiagnostic { + #[subdiagnostic] + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct VecField { + #[primary_span] + #[label("with a label")] + spans: Vec, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct UnitField { + #[primary_span] + spans: Span, + #[help("with a help")] + bar: (), +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct OptUnitField { + #[primary_span] + spans: Span, + #[help("with a help")] + foo: Option<()>, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct BoolField { + #[primary_span] + spans: Span, + #[help("with a help")] + foo: bool, + #[help("with a help")] + //~^ ERROR the `#[help(...)]` attribute can only be applied to fields of type + // only allow plain 'bool' fields + bar: Option, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingPath { + #[label("with a label", foo)] + //~^ ERROR a diagnostic slug must be the first argument to the attribute + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingNameValue { + #[label("with a label", foo = "...")] + //~^ ERROR no nested attribute expected here + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct LabelWithTrailingList { + #[label("with a label", foo("..."))] + //~^ ERROR no nested attribute expected here + span: Span, +} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct LintsGood {} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct PrimarySpanOnLint { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct ErrorWithMultiSpan { + #[primary_span] + span: MultiSpan, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[warning("with a warning")] +struct ErrorWithWarn { + val: String, +} + +#[derive(Diagnostic)] +#[error("this is an example message", code = E0123)] +//~^ ERROR `#[error(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `error` in this scope +struct ErrorAttribute {} + +#[derive(Diagnostic)] +#[warn_("this is an example message", code = E0123)] +//~^ ERROR `#[warn_(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `warn_` in this scope +struct WarnAttribute {} + +#[derive(Diagnostic)] +#[lint("this is an example message", code = E0123)] +//~^ ERROR `#[lint(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `lint` in this scope +struct LintAttributeOnSessionDiag {} + +#[derive(LintDiagnostic)] +#[lint("this is an example message", code = E0123)] +//~^ ERROR `#[lint(...)]` is not a valid attribute +//~| ERROR diagnostic slug not specified +//~| ERROR cannot find attribute `lint` in this scope +struct LintAttributeOnLintDiag {} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct DuplicatedSuggestionCode { + #[suggestion("with a suggestion", code = "...", code = ",,,")] + //~^ ERROR specified multiple times + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct InvalidTypeInSuggestionTuple { + #[suggestion("with a suggestion", code = "...")] + suggestion: (Span, usize), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MissingApplicabilityInSuggestionTuple { + #[suggestion("with a suggestion", code = "...")] + suggestion: (Span,), + //~^ ERROR wrong types for suggestion +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct MissingCodeInSuggestion { + #[suggestion("with a suggestion")] + //~^ ERROR suggestion without `code = "..."` + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[multipart_suggestion("with a suggestion")] +//~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute +//~| ERROR cannot find attribute `multipart_suggestion` in this scope +#[multipart_suggestion()] +//~^ ERROR cannot find attribute `multipart_suggestion` in this scope +//~| ERROR `#[multipart_suggestion(...)]` is not a valid attribute +struct MultipartSuggestion { + #[multipart_suggestion("with a suggestion")] + //~^ ERROR `#[multipart_suggestion(...)]` is not a valid attribute + //~| ERROR cannot find attribute `multipart_suggestion` in this scope + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[suggestion("with a suggestion", code = "...")] +//~^ ERROR `#[suggestion(...)]` is not a valid attribute +struct SuggestionOnStruct { + #[primary_span] + suggestion: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +#[label] +//~^ ERROR `#[label]` is not a valid attribute +struct LabelOnStruct { + #[primary_span] + suggestion: Span, +} + +#[derive(Diagnostic)] +enum ExampleEnum { + #[diag("this is an example message")] + Foo { + #[primary_span] + sp: Span, + #[note("with a note")] + note_sp: Span, + }, + #[diag("this is an example message")] + Bar { + #[primary_span] + sp: Span, + }, + #[diag("this is an example message")] + Baz, +} + +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct RawIdentDiagnosticArg { + pub r#type: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBad { + #[subdiagnostic(bad)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadStr { + #[subdiagnostic = "bad"] + //~^ ERROR `#[subdiagnostic = ...]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadTwice { + #[subdiagnostic(bad, bad)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticBadLitStr { + #[subdiagnostic("bad")] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(LintDiagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerLint { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerFormerlyCorrect { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + note: Note, +} + +// Check that formatting of `correct` in suggestion doesn't move the binding for that field, making +// the `arg` call a compile error; and that isn't worked around by moving the `arg` call +// after the `span_suggestion` call - which breaks eager translation. + +#[derive(Subdiagnostic)] +#[suggestion("example message", applicability = "machine-applicable", code = "{correct}")] +pub(crate) struct SubdiagnosticWithSuggestion { + #[primary_span] + span: Span, + invalid: String, + correct: String, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SubdiagnosticEagerSuggestion { + #[subdiagnostic(eager)] + //~^ ERROR `#[subdiagnostic(...)]` is not a valid attribute + sub: SubdiagnosticWithSuggestion, +} + +/// with a doc comment on the type.. +#[derive(Diagnostic)] +#[diag("this is an example message", code = E0123)] +struct WithDocComment { + /// ..and the field + #[primary_span] + span: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsGood { + #[suggestion("with a suggestion", code("foo", "bar"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsSingleItem { + #[suggestion("with a suggestion", code("foo"))] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsNoItem { + #[suggestion("with a suggestion", code())] + //~^ ERROR expected at least one string literal for `code(...)` + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsInvalidItem { + #[suggestion("with a suggestion", code(foo))] + //~^ ERROR `code(...)` must contain only string literals + //~| ERROR unexpected token, expected `)` + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionsInvalidLiteral { + #[suggestion("with a suggestion", code = 3)] + //~^ ERROR expected string literal + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionStyleGood { + #[suggestion("with a suggestion", code = "", style = "hidden")] + sub: Span, +} + +#[derive(Diagnostic)] +#[diag("this is an example message")] +struct SuggestionOnVec { + #[suggestion("with a suggestion", code = "")] + //~^ ERROR `#[suggestion(...)]` is not a valid attribute + sub: Vec, +} diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr new file mode 100644 index 000000000000..bec07a425762 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr @@ -0,0 +1,627 @@ +error: derive(Diagnostic): unsupported type attribute for diagnostic derive enum + --> $DIR/diagnostic-derive-inline.rs:45:1 + | +LL | #[diag("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:48:5 + | +LL | Foo, + | ^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:50:5 + | +LL | Bar, + | ^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: expected parentheses: #[diag(...)] + --> $DIR/diagnostic-derive-inline.rs:56:8 + | +LL | #[diag = "E0123"] + | ^ + +error: derive(Diagnostic): `#[nonsense(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:61:1 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:61:1 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:68:1 + | +LL | #[diag(code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:73:1 + | +LL | #[diag(nonsense("foo"), code = E0123, slug = "foo")] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:78:1 + | +LL | #[diag(nonsense = "...", code = E0123, slug = "foo")] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:83:1 + | +LL | #[diag(nonsense = 4, code = E0123, slug = "foo")] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): unknown argument + --> $DIR/diagnostic-derive-inline.rs:88:52 + | +LL | #[diag("this is an example message", code = E0123, slug = "foo")] + | ^^^^ + | + = note: only the `code` parameter is valid after the slug + +error: derive(Diagnostic): `#[suggestion = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:95:5 + | +LL | #[suggestion = "bar"] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:102:38 + | +LL | #[diag("this is an example message", code = E0456)] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:101:38 + | +LL | #[diag("this is an example message", code = E0123)] + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:107:52 + | +LL | #[diag("this is an example message", code = E0123, code = E0456)] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:107:38 + | +LL | #[diag("this is an example message", code = E0123, code = E0456)] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be the first argument + --> $DIR/diagnostic-derive-inline.rs:112:38 + | +LL | #[diag("this is an example message", no_crate::example, code = E0123)] + | ^^^^^^^^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:117:1 + | +LL | struct KindNotProvided {} + | ^^^^^^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:120:1 + | +LL | #[diag(code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:131:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): `#[nonsense]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:139:5 + | +LL | #[nonsense] + | ^ + +error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:156:5 + | +LL | #[label("with a label")] + | ^ + +error: derive(Diagnostic): `name` doesn't refer to a field on this type + --> $DIR/diagnostic-derive-inline.rs:164:46 + | +LL | #[suggestion("with a suggestion", code = "{name}")] + | ^^^^^^^^ + +error: invalid format string: expected `}` but string was terminated + --> $DIR/diagnostic-derive-inline.rs:169:10 + | +LL | #[derive(Diagnostic)] + | ^^^^^^^^^^ expected `}` in format string + | + = note: if you intended to print `{`, you can escape it using `{{` + = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: invalid format string: unmatched `}` found + --> $DIR/diagnostic-derive-inline.rs:179:10 + | +LL | #[derive(Diagnostic)] + | ^^^^^^^^^^ unmatched `}` in format string + | + = note: if you intended to print `}`, you can escape it using `}}` + = note: this error originates in the derive macro `Diagnostic` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: derive(Diagnostic): the `#[label(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/diagnostic-derive-inline.rs:199:5 + | +LL | #[label("with a label")] + | ^ + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:218:5 + | +LL | #[suggestion("with a suggestion")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/diagnostic-derive-inline.rs:226:39 + | +LL | #[suggestion("with a suggestion", nonsense = "bar")] + | ^^^^^^^^ + | + = help: only `style`, `code` and `applicability` are valid nested attributes + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:226:5 + | +LL | #[suggestion("with a suggestion", nonsense = "bar")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/diagnostic-derive-inline.rs:235:39 + | +LL | #[suggestion("with a suggestion", msg = "bar")] + | ^^^ + | + = help: only `style`, `code` and `applicability` are valid nested attributes + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:235:5 + | +LL | #[suggestion("with a suggestion", msg = "bar")] + | ^ + +error: derive(Diagnostic): wrong field type for suggestion + --> $DIR/diagnostic-derive-inline.rs:258:5 + | +LL | #[suggestion("with a suggestion", code = "This is suggested code")] + | ^ + | + = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)` + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:274:24 + | +LL | suggestion: (Span, Span, Applicability), + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:274:18 + | +LL | suggestion: (Span, Span, Applicability), + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:282:33 + | +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:282:18 + | +LL | suggestion: (Applicability, Applicability, Span), + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): `#[label = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:289:5 + | +LL | #[label = "bar"] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:390:5 + | +LL | #[suggestion("with a suggestion", code = "...", applicability = "maybe-incorrect")] + | ^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:392:24 + | +LL | suggestion: (Span, Applicability), + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): invalid applicability + --> $DIR/diagnostic-derive-inline.rs:398:69 + | +LL | #[suggestion("with a suggestion", code = "...", applicability = "batman")] + | ^^^^^^^^ + +error: derive(Diagnostic): the `#[help(...)]` attribute can only be applied to fields of type `Span`, `MultiSpan`, `bool` or `()` + --> $DIR/diagnostic-derive-inline.rs:461:5 + | +LL | #[help("with a help")] + | ^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/diagnostic-derive-inline.rs:470:29 + | +LL | #[label("with a label", foo)] + | ^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive-inline.rs:478:29 + | +LL | #[label("with a label", foo = "...")] + | ^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/diagnostic-derive-inline.rs:486:29 + | +LL | #[label("with a label", foo("..."))] + | ^^^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:498:5 + | +LL | #[primary_span] + | ^ + | + = help: the `primary_span` field attribute is not valid for lint diagnostics + +error: derive(Diagnostic): `#[error(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:518:1 + | +LL | #[error("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:518:1 + | +LL | #[error("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): `#[warn_(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:525:1 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:525:1 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:532:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:532:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): `#[lint(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:539:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + +error: derive(Diagnostic): diagnostic slug not specified + --> $DIR/diagnostic-derive-inline.rs:539:1 + | +LL | #[lint("this is an example message", code = E0123)] + | ^ + | + = help: specify the slug as the first argument to the `#[diag(...)]` attribute, such as `#[diag(hir_analysis_example_error)]` + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/diagnostic-derive-inline.rs:548:53 + | +LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] + | ^^^^ + | +note: previously specified here + --> $DIR/diagnostic-derive-inline.rs:548:39 + | +LL | #[suggestion("with a suggestion", code = "...", code = ",,,")] + | ^^^^ + +error: derive(Diagnostic): wrong types for suggestion + --> $DIR/diagnostic-derive-inline.rs:557:24 + | +LL | suggestion: (Span, usize), + | ^^^^^ + | + = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` + +error: derive(Diagnostic): wrong types for suggestion + --> $DIR/diagnostic-derive-inline.rs:565:17 + | +LL | suggestion: (Span,), + | ^^^^^^^ + | + = help: `#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)` + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/diagnostic-derive-inline.rs:572:5 + | +LL | #[suggestion("with a suggestion")] + | ^ + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:579:1 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:582:1 + | +LL | #[multipart_suggestion()] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[multipart_suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:586:5 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^ + | + = help: consider creating a `Subdiagnostic` instead + +error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:594:1 + | +LL | #[suggestion("with a suggestion", code = "...")] + | ^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + +error: derive(Diagnostic): `#[label]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:603:1 + | +LL | #[label] + | ^ + | + = help: `#[label]` and `#[suggestion]` can only be applied to fields + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:637:5 + | +LL | #[subdiagnostic(bad)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic = ...]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:645:5 + | +LL | #[subdiagnostic = "bad"] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:653:5 + | +LL | #[subdiagnostic(bad, bad)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:661:5 + | +LL | #[subdiagnostic("bad")] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:669:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:677:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): `#[subdiagnostic(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:698:5 + | +LL | #[subdiagnostic(eager)] + | ^ + +error: derive(Diagnostic): expected at least one string literal for `code(...)` + --> $DIR/diagnostic-derive-inline.rs:729:44 + | +LL | #[suggestion("with a suggestion", code())] + | ^ + +error: derive(Diagnostic): `code(...)` must contain only string literals + --> $DIR/diagnostic-derive-inline.rs:737:44 + | +LL | #[suggestion("with a suggestion", code(foo))] + | ^^^ + +error: unexpected token, expected `)` + --> $DIR/diagnostic-derive-inline.rs:737:44 + | +LL | #[suggestion("with a suggestion", code(foo))] + | ^^^ + +error: expected string literal + --> $DIR/diagnostic-derive-inline.rs:746:46 + | +LL | #[suggestion("with a suggestion", code = 3)] + | ^ + +error: derive(Diagnostic): `#[suggestion(...)]` is not a valid attribute + --> $DIR/diagnostic-derive-inline.rs:761:5 + | +LL | #[suggestion("with a suggestion", code = "")] + | ^ + | + = note: `#[suggestion(...)]` applied to `Vec` field is ambiguous + = help: to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]` + = help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]` + +error: cannot find attribute `nonsense` in this scope + --> $DIR/diagnostic-derive-inline.rs:61:3 + | +LL | #[nonsense("this is an example message", code = E0123)] + | ^^^^^^^^ + +error: cannot find attribute `nonsense` in this scope + --> $DIR/diagnostic-derive-inline.rs:139:7 + | +LL | #[nonsense] + | ^^^^^^^^ + +error: cannot find attribute `error` in this scope + --> $DIR/diagnostic-derive-inline.rs:518:3 + | +LL | #[error("this is an example message", code = E0123)] + | ^^^^^ + | +help: `error` is an attribute that can be used by the derive macro `Error`, you might be missing a `derive` attribute + | +LL + #[derive(Error)] +LL | struct ErrorAttribute {} + | + +error: cannot find attribute `warn_` in this scope + --> $DIR/diagnostic-derive-inline.rs:525:3 + | +LL | #[warn_("this is an example message", code = E0123)] + | ^^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[warn_("this is an example message", code = E0123)] +LL + #[warn("this is an example message", code = E0123)] + | + +error: cannot find attribute `lint` in this scope + --> $DIR/diagnostic-derive-inline.rs:532:3 + | +LL | #[lint("this is an example message", code = E0123)] + | ^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[lint("this is an example message", code = E0123)] +LL + #[link("this is an example message", code = E0123)] + | + +error: cannot find attribute `lint` in this scope + --> $DIR/diagnostic-derive-inline.rs:539:3 + | +LL | #[lint("this is an example message", code = E0123)] + | ^^^^ + | +help: a built-in attribute with a similar name exists + | +LL - #[lint("this is an example message", code = E0123)] +LL + #[link("this is an example message", code = E0123)] + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:579:3 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL + #[derive(Subdiagnostic)] +LL | struct MultipartSuggestion { + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:582:3 + | +LL | #[multipart_suggestion()] + | ^^^^^^^^^^^^^^^^^^^^ + | +help: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + | +LL + #[derive(Subdiagnostic)] +LL | struct MultipartSuggestion { + | + +error: cannot find attribute `multipart_suggestion` in this scope + --> $DIR/diagnostic-derive-inline.rs:586:7 + | +LL | #[multipart_suggestion("with a suggestion")] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: `multipart_suggestion` is an attribute that can be used by the derive macro `Subdiagnostic`, you might be missing a `derive` attribute + +error[E0277]: the trait bound `Hello: IntoDiagArg` is not satisfied + --> $DIR/diagnostic-derive-inline.rs:330:12 + | +LL | #[derive(Diagnostic)] + | ---------- required by a bound introduced by this call +... +LL | other: Hello, + | ^^^^^ unsatisfied trait bound + | +help: the nightly-only, unstable trait `IntoDiagArg` is not implemented for `Hello` + --> $DIR/diagnostic-derive-inline.rs:42:1 + | +LL | struct Hello {} + | ^^^^^^^^^^^^ + = help: normalized in stderr +note: required by a bound in `Diag::<'a, G>::arg` + --> $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + ::: $COMPILER_DIR/rustc_errors/src/diagnostic.rs:LL:CC + | + = note: in this macro invocation + = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 79 previous errors + +For more information about this error, try `rustc --explain E0277`. From 523d9d920028753e4c170d34366871e14f13a9fc Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 19:26:57 +0100 Subject: [PATCH 211/319] Uitests for subdiagnostics --- .../subdiagnostic-derive-inline.rs | 807 ++++++++++++++++++ .../subdiagnostic-derive-inline.stderr | 549 ++++++++++++ .../subdiagnostic-derive.rs | 9 - .../subdiagnostic-derive.stderr | 168 ++-- 4 files changed, 1437 insertions(+), 96 deletions(-) create mode 100644 tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs create mode 100644 tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs new file mode 100644 index 000000000000..eaa681d40be5 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.rs @@ -0,0 +1,807 @@ +//@ check-fail +// Tests error conditions for specifying inline subdiagnostics using #[derive(Subdiagnostic)] + +// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly, +// changing the output of this test. Since Subdiagnostic is strictly internal to the compiler +// the test is just ignored on stable and beta: +//@ ignore-stage1 +//@ ignore-beta +//@ ignore-stable + +#![feature(rustc_private)] +#![crate_type = "lib"] + +extern crate rustc_errors; +extern crate rustc_fluent_macro; +extern crate rustc_macros; +extern crate rustc_session; +extern crate rustc_span; +extern crate core; + +use rustc_errors::{Applicability, DiagMessage, SubdiagMessage}; +use rustc_macros::Subdiagnostic; +use rustc_span::Span; + +#[derive(Subdiagnostic)] +#[label("example message")] +struct A { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +enum B { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, + #[label("example message")] + B { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +//~^ ERROR label without `#[primary_span]` field +struct C { + var: String, +} + +#[derive(Subdiagnostic)] +#[label] +//~^ ERROR diagnostic slug must be first argument +struct D { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[foo] +//~^ ERROR `#[foo]` is not a valid attribute +//~^^ ERROR cannot find attribute `foo` in this scope +struct E { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label = "..."] +//~^ ERROR `#[label = ...]` is not a valid attribute +struct F { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(bug = "...")] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct G { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(slug = 4)] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct J { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label(slug("..."))] +//~^ ERROR no nested attribute expected here +//~| ERROR diagnostic slug must be first argument +struct K { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label()] +//~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute +struct M { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label("example message", code = "...")] +//~^ ERROR no nested attribute expected here +struct N { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[label("example message", applicability = "machine-applicable")] +//~^ ERROR no nested attribute expected here +struct O { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[foo] +//~^ ERROR cannot find attribute `foo` in this scope +//~^^ ERROR unsupported type attribute for subdiagnostic enum +enum P { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum Q { + #[bar] + //~^ ERROR `#[bar]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum R { + #[bar = "..."] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum S { + #[bar = 4] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum T { + #[bar("...")] + //~^ ERROR `#[bar(...)]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum U { + #[label(code = "...")] + //~^ ERROR diagnostic slug must be first argument of a `#[label(...)]` attribute + //~| ERROR no nested attribute expected here + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum V { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, + B { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +//~^ ERROR label without `#[primary_span]` field +struct W { + #[primary_span] + //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + span: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct X { + #[primary_span] + span: Span, + #[applicability] + //~^ ERROR `#[applicability]` is only valid on suggestions + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct Y { + #[primary_span] + span: Span, + #[bar] + //~^ ERROR `#[bar]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct Z { + #[primary_span] + span: Span, + #[bar = "..."] + //~^ ERROR `#[bar = ...]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AA { + #[primary_span] + span: Span, + #[bar("...")] + //~^ ERROR `#[bar(...)]` is not a valid attribute + //~^^ ERROR cannot find attribute `bar` in this scope + bar: String, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AB { + #[primary_span] + span: Span, + #[skip_arg] + z: Z, +} + +#[derive(Subdiagnostic)] +union AC { + //~^ ERROR unexpected unsupported untagged union + span: u32, + b: u64, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +#[label("example message")] +struct AD { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label("example message", no_crate::example)] +//~^ ERROR a diagnostic slug must be the first argument to the attribute +struct AE { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct AF { + #[primary_span] + //~^ NOTE previously specified here + span_a: Span, + #[primary_span] + //~^ ERROR specified multiple times + span_b: Span, +} + +#[derive(Subdiagnostic)] +struct AG { + //~^ ERROR subdiagnostic kind not specified + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AH { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, +} + +#[derive(Subdiagnostic)] +enum AI { + #[suggestion("example message", code = "...")] + A { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, + }, + #[suggestion("example message", code = "...")] + B { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", code = "...")] +//~^ ERROR specified multiple times +//~^^ NOTE previously specified here +struct AJ { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AK { + #[primary_span] + span: Span, + #[applicability] + //~^ NOTE previously specified here + applicability_a: Applicability, + #[applicability] + //~^ ERROR specified multiple times + applicability_b: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AL { + #[primary_span] + span: Span, + #[applicability] + //~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability` + applicability: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +struct AM { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message")] +//~^ ERROR suggestion without `code = "..."` +struct AN { + #[primary_span] + span: Span, + #[applicability] + applicability: Applicability, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", applicability = "foo")] +//~^ ERROR invalid applicability +struct AO { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[help("example message")] +struct AP { + var: String, +} + +#[derive(Subdiagnostic)] +#[note("example message")] +struct AQ; + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +//~^ ERROR suggestion without `#[primary_span]` field +struct AR { + var: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...", applicability = "machine-applicable")] +struct AS { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[label] +//~^ ERROR unsupported type attribute for subdiagnostic enum +enum AT { + #[label("example message")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "{var}", applicability = "machine-applicable")] +struct AU { + #[primary_span] + span: Span, + var: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "{var}", applicability = "machine-applicable")] +//~^ ERROR `var` doesn't refer to a field on this type +struct AV { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +enum AW { + #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + A { + #[primary_span] + span: Span, + var: String, + }, +} + +#[derive(Subdiagnostic)] +enum AX { + #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + //~^ ERROR `var` doesn't refer to a field on this type + A { + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +#[warning("example message")] +struct AY {} + +#[derive(Subdiagnostic)] +#[warning("example message")] +struct AZ { + #[primary_span] + span: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "...")] +//~^ ERROR suggestion without `#[primary_span]` field +struct BA { + #[suggestion_part] + //~^ ERROR `#[suggestion_part]` is not a valid attribute + span: Span, + #[suggestion_part(code = "...")] + //~^ ERROR `#[suggestion_part(...)]` is not a valid attribute + span2: Span, + #[applicability] + applicability: Applicability, + var: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +//~| ERROR invalid nested attribute +struct BBa { + var: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BBb { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BBc { + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +//~^ ERROR multipart suggestion without any `#[suggestion_part(...)]` fields +struct BC { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BD { + #[suggestion_part] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span1: Span, + #[suggestion_part()] + //~^ ERROR `#[suggestion_part(...)]` attribute without `code = "..."` + span2: Span, + #[suggestion_part(foo = "bar")] + //~^ ERROR `code` is the only valid nested attribute + //~| ERROR expected `,` + span4: Span, + #[suggestion_part(code = "...")] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s1: String, + #[suggestion_part()] + //~^ ERROR the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + s2: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BE { + #[suggestion_part(code = "...", code = ",,,")] + //~^ ERROR specified multiple times + //~| NOTE previously specified here + span: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BF { + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BG { + #[applicability] + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BH { + #[applicability] + //~^ ERROR `#[applicability]` has no effect + appl: Applicability, + #[suggestion_part(code = "(")] + first: Span, + #[suggestion_part(code = ")")] + second: Span, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message", applicability = "machine-applicable")] +struct BI { + #[suggestion_part(code = "")] + spans: Vec, +} + +#[derive(Subdiagnostic)] +#[label("example message")] +struct BJ { + #[primary_span] + span: Span, + r#type: String, +} + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +#[label("example message")] +struct BK { + /// ..and the field + #[primary_span] + span: Span, +} + +/// with a doc comment on the type.. +#[derive(Subdiagnostic)] +enum BL { + /// ..and the variant.. + #[label("example message")] + Foo { + /// ..and the field + #[primary_span] + span: Span, + }, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BM { + #[suggestion_part(code("foo"))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BN { + #[suggestion_part(code("foo", "bar"))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BO { + #[suggestion_part(code(3))] + //~^ ERROR expected exactly one string literal for `code = ...` + //~| ERROR unexpected token, expected `)` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BP { + #[suggestion_part(code())] + //~^ ERROR expected exactly one string literal for `code = ...` + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion("example message")] +struct BQ { + #[suggestion_part(code = 3)] + //~^ ERROR expected string literal + span: Span, + r#type: String, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "")] +struct SuggestionStyleDefault { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "short")] +struct SuggestionStyleShort { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "hidden")] +struct SuggestionStyleHidden { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "verbose")] +struct SuggestionStyleVerbose { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "tool-only")] +struct SuggestionStyleToolOnly { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "hidden", style = "normal")] +//~^ ERROR specified multiple times +//~| NOTE previously specified here +struct SuggestionStyleTwice { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion_hidden("example message", code = "")] +//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +struct SuggestionStyleOldSyntax { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion_hidden("example message", code = "", style = "normal")] +//~^ ERROR #[suggestion_hidden(...)]` is not a valid attribute +struct SuggestionStyleOldAndNewSyntax { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = "foo")] +//~^ ERROR invalid suggestion style +struct SuggestionStyleInvalid1 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style = 42)] +//~^ ERROR expected string literal +struct SuggestionStyleInvalid2 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style)] +//~^ ERROR a diagnostic slug must be the first argument to the attribute +struct SuggestionStyleInvalid3 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "", style("foo"))] +//~^ ERROR expected `=` +struct SuggestionStyleInvalid4 { + #[primary_span] + sub: Span, +} + +#[derive(Subdiagnostic)] +#[suggestion("example message", code = "")] +//~^ ERROR suggestion without `#[primary_span]` field +struct PrimarySpanOnVec { + #[primary_span] + //~^ ERROR `#[primary_span]` is not a valid attribute + //~| NOTE there must be exactly one primary span + sub: Vec, +} + +#[derive(Subdiagnostic)] +struct NestedParent { + #[subdiagnostic] + single_sub: A, + #[subdiagnostic] + option_sub: Option, + #[subdiagnostic] + vec_sub: Vec, +} diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr new file mode 100644 index 000000000000..11753b949bc7 --- /dev/null +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive-inline.stderr @@ -0,0 +1,549 @@ +error: derive(Diagnostic): label without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:50:1 + | +LL | #[label("example message")] + | ^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:57:1 + | +LL | #[label] + | ^ + +error: derive(Diagnostic): `#[foo]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:66:1 + | +LL | #[foo] + | ^ + +error: derive(Diagnostic): `#[label = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:76:1 + | +LL | #[label = "..."] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:85:9 + | +LL | #[label(bug = "...")] + | ^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:85:1 + | +LL | #[label(bug = "...")] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:95:9 + | +LL | #[label(slug = 4)] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:95:1 + | +LL | #[label(slug = 4)] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:105:9 + | +LL | #[label(slug("..."))] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:105:1 + | +LL | #[label(slug("..."))] + | ^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:115:1 + | +LL | #[label()] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:124:28 + | +LL | #[label("example message", code = "...")] + | ^^^^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:133:28 + | +LL | #[label("example message", applicability = "machine-applicable")] + | ^^^^^^^^^^^^^ + +error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum + --> $DIR/subdiagnostic-derive-inline.rs:142:1 + | +LL | #[foo] + | ^ + +error: derive(Diagnostic): `#[bar]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:156:5 + | +LL | #[bar] + | ^ + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:168:5 + | +LL | #[bar = "..."] + | ^ + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:180:5 + | +LL | #[bar = 4] + | ^ + +error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:192:5 + | +LL | #[bar("...")] + | ^ + +error: derive(Diagnostic): no nested attribute expected here + --> $DIR/subdiagnostic-derive-inline.rs:204:13 + | +LL | #[label(code = "...")] + | ^^^^ + +error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute + --> $DIR/subdiagnostic-derive-inline.rs:204:5 + | +LL | #[label(code = "...")] + | ^ + +error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:233:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): label without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:230:1 + | +LL | #[label("example message")] + | ^ + +error: derive(Diagnostic): `#[applicability]` is only valid on suggestions + --> $DIR/subdiagnostic-derive-inline.rs:243:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): `#[bar]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:253:5 + | +LL | #[bar] + | ^ + | + = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes + +error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:264:5 + | +LL | #[bar = "..."] + | ^ + +error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:275:5 + | +LL | #[bar("...")] + | ^ + | + = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes + +error: unexpected unsupported untagged union + --> $DIR/subdiagnostic-derive-inline.rs:291:1 + | +LL | / union AC { +LL | | +LL | | span: u32, +LL | | b: u64, +LL | | } + | |_^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/subdiagnostic-derive-inline.rs:306:28 + | +LL | #[label("example message", no_crate::example)] + | ^^^^^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:319:5 + | +LL | #[primary_span] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:316:5 + | +LL | #[primary_span] + | ^ + +error: derive(Diagnostic): subdiagnostic kind not specified + --> $DIR/subdiagnostic-derive-inline.rs:325:8 + | +LL | struct AG { + | ^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:362:47 + | +LL | #[suggestion("example message", code = "...", code = "...")] + | ^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:362:33 + | +LL | #[suggestion("example message", code = "...", code = "...")] + | ^^^^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:380:5 + | +LL | #[applicability] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:377:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): the `#[applicability]` attribute can only be applied to fields of type `Applicability` + --> $DIR/subdiagnostic-derive-inline.rs:390:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): suggestion without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:403:1 + | +LL | #[suggestion("example message")] + | ^ + +error: derive(Diagnostic): invalid applicability + --> $DIR/subdiagnostic-derive-inline.rs:413:63 + | +LL | #[suggestion("example message", code = "...", applicability = "foo")] + | ^^^^^ + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:431:1 + | +LL | #[suggestion("example message", code = "...")] + | ^ + +error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum + --> $DIR/subdiagnostic-derive-inline.rs:445:1 + | +LL | #[label] + | ^ + +error: derive(Diagnostic): `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive-inline.rs:465:40 + | +LL | #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + | ^^^^^^^ + +error: derive(Diagnostic): `var` doesn't refer to a field on this type + --> $DIR/subdiagnostic-derive-inline.rs:484:44 + | +LL | #[suggestion("example message", code = "{var}", applicability = "machine-applicable")] + | ^^^^^^^ + +error: derive(Diagnostic): `#[suggestion_part]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:507:5 + | +LL | #[suggestion_part] + | ^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead + +error: derive(Diagnostic): `#[suggestion_part(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:510:5 + | +LL | #[suggestion_part(code = "...")] + | ^ + | + = help: `#[suggestion_part(...)]` is only valid in multipart suggestions + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:504:1 + | +LL | #[suggestion("example message", code = "...")] + | ^ + +error: derive(Diagnostic): invalid nested attribute + --> $DIR/subdiagnostic-derive-inline.rs:519:43 + | +LL | #[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] + | ^^^^ + | + = help: only `style` and `applicability` are valid nested attributes + +error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive-inline.rs:519:1 + | +LL | #[multipart_suggestion("example message", code = "...", applicability = "machine-applicable")] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:529:5 + | +LL | #[suggestion_part] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:537:5 + | +LL | #[suggestion_part()] + | ^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:546:5 + | +LL | #[primary_span] + | ^ + | + = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` + +error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields + --> $DIR/subdiagnostic-derive-inline.rs:543:1 + | +LL | #[multipart_suggestion("example message")] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:554:5 + | +LL | #[suggestion_part] + | ^ + +error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:557:5 + | +LL | #[suggestion_part()] + | ^ + +error: derive(Diagnostic): `code` is the only valid nested attribute + --> $DIR/subdiagnostic-derive-inline.rs:560:23 + | +LL | #[suggestion_part(foo = "bar")] + | ^^^ + +error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:564:5 + | +LL | #[suggestion_part(code = "...")] + | ^ + +error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` + --> $DIR/subdiagnostic-derive-inline.rs:567:5 + | +LL | #[suggestion_part()] + | ^ + +error: expected `,` + --> $DIR/subdiagnostic-derive-inline.rs:560:27 + | +LL | #[suggestion_part(foo = "bar")] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:575:37 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:575:23 + | +LL | #[suggestion_part(code = "...", code = ",,,")] + | ^^^^ + +error: derive(Diagnostic): `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."` + --> $DIR/subdiagnostic-derive-inline.rs:604:5 + | +LL | #[applicability] + | ^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:652:28 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:652:28 + | +LL | #[suggestion_part(code("foo"))] + | ^^^^^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:662:28 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:662:28 + | +LL | #[suggestion_part(code("foo", "bar"))] + | ^^^^^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:672:28 + | +LL | #[suggestion_part(code(3))] + | ^ + +error: unexpected token, expected `)` + --> $DIR/subdiagnostic-derive-inline.rs:672:28 + | +LL | #[suggestion_part(code(3))] + | ^ + +error: derive(Diagnostic): expected exactly one string literal for `code = ...` + --> $DIR/subdiagnostic-derive-inline.rs:682:28 + | +LL | #[suggestion_part(code())] + | ^ + +error: expected string literal + --> $DIR/subdiagnostic-derive-inline.rs:691:30 + | +LL | #[suggestion_part(code = 3)] + | ^ + +error: derive(Diagnostic): attribute specified multiple times + --> $DIR/subdiagnostic-derive-inline.rs:733:1 + | +LL | #[suggestion("example message", code = "", style = "hidden", style = "normal")] + | ^ + | +note: previously specified here + --> $DIR/subdiagnostic-derive-inline.rs:733:1 + | +LL | #[suggestion("example message", code = "", style = "hidden", style = "normal")] + | ^ + +error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:742:1 + | +LL | #[suggestion_hidden("example message", code = "")] + | ^ + | + = help: Use `#[suggestion(..., style = "hidden")]` instead + +error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:750:1 + | +LL | #[suggestion_hidden("example message", code = "", style = "normal")] + | ^ + | + = help: Use `#[suggestion(..., style = "hidden")]` instead + +error: derive(Diagnostic): invalid suggestion style + --> $DIR/subdiagnostic-derive-inline.rs:758:52 + | +LL | #[suggestion("example message", code = "", style = "foo")] + | ^^^^^ + | + = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` + +error: expected string literal + --> $DIR/subdiagnostic-derive-inline.rs:766:52 + | +LL | #[suggestion("example message", code = "", style = 42)] + | ^^ + +error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute + --> $DIR/subdiagnostic-derive-inline.rs:774:44 + | +LL | #[suggestion("example message", code = "", style)] + | ^^^^^ + +error: expected `=` + --> $DIR/subdiagnostic-derive-inline.rs:782:49 + | +LL | #[suggestion("example message", code = "", style("foo"))] + | ^ + +error: derive(Diagnostic): `#[primary_span]` is not a valid attribute + --> $DIR/subdiagnostic-derive-inline.rs:793:5 + | +LL | #[primary_span] + | ^ + | + = note: there must be exactly one primary span + = help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead + +error: derive(Diagnostic): suggestion without `#[primary_span]` field + --> $DIR/subdiagnostic-derive-inline.rs:790:1 + | +LL | #[suggestion("example message", code = "")] + | ^ + +error: cannot find attribute `foo` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:66:3 + | +LL | #[foo] + | ^^^ + +error: cannot find attribute `foo` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:142:3 + | +LL | #[foo] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:156:7 + | +LL | #[bar] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:168:7 + | +LL | #[bar = "..."] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:180:7 + | +LL | #[bar = 4] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:192:7 + | +LL | #[bar("...")] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:253:7 + | +LL | #[bar] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:264:7 + | +LL | #[bar = "..."] + | ^^^ + +error: cannot find attribute `bar` in this scope + --> $DIR/subdiagnostic-derive-inline.rs:275:7 + | +LL | #[bar("...")] + | ^^^ + +error: aborting due to 82 previous errors + diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs index b2e7b4c61daa..c06ea451b9bf 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.rs @@ -93,15 +93,6 @@ struct G { var: String, } -#[derive(Subdiagnostic)] -#[label("...")] -//~^ ERROR expected identifier -struct H { - #[primary_span] - span: Span, - var: String, -} - #[derive(Subdiagnostic)] #[label(slug = 4)] //~^ ERROR no nested attribute expected here diff --git a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr index 63634741e934..9f18f7ffabcc 100644 --- a/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr +++ b/tests/ui-fulldeps/session-diagnostic/subdiagnostic-derive.stderr @@ -34,116 +34,110 @@ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label( LL | #[label(bug = "...")] | ^ -error: expected identifier +error: derive(Diagnostic): no nested attribute expected here --> $DIR/subdiagnostic-derive.rs:97:9 | -LL | #[label("...")] - | ^^^^^ - -error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:106:9 - | LL | #[label(slug = 4)] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:106:1 + --> $DIR/subdiagnostic-derive.rs:97:1 | LL | #[label(slug = 4)] | ^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:116:9 + --> $DIR/subdiagnostic-derive.rs:107:9 | LL | #[label(slug("..."))] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:116:1 + --> $DIR/subdiagnostic-derive.rs:107:1 | LL | #[label(slug("..."))] | ^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:126:1 + --> $DIR/subdiagnostic-derive.rs:117:1 | LL | #[label()] | ^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:135:27 + --> $DIR/subdiagnostic-derive.rs:126:27 | LL | #[label(no_crate_example, code = "...")] | ^^^^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:144:27 + --> $DIR/subdiagnostic-derive.rs:135:27 | LL | #[label(no_crate_example, applicability = "machine-applicable")] | ^^^^^^^^^^^^^ error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:153:1 + --> $DIR/subdiagnostic-derive.rs:144:1 | LL | #[foo] | ^ error: derive(Diagnostic): `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:167:5 + --> $DIR/subdiagnostic-derive.rs:158:5 | LL | #[bar] | ^ error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:179:5 + --> $DIR/subdiagnostic-derive.rs:170:5 | LL | #[bar = "..."] | ^ error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:191:5 + --> $DIR/subdiagnostic-derive.rs:182:5 | LL | #[bar = 4] | ^ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:203:5 + --> $DIR/subdiagnostic-derive.rs:194:5 | LL | #[bar("...")] | ^ error: derive(Diagnostic): no nested attribute expected here - --> $DIR/subdiagnostic-derive.rs:215:13 + --> $DIR/subdiagnostic-derive.rs:206:13 | LL | #[label(code = "...")] | ^^^^ error: derive(Diagnostic): diagnostic slug must be first argument of a `#[label(...)]` attribute - --> $DIR/subdiagnostic-derive.rs:215:5 + --> $DIR/subdiagnostic-derive.rs:206:5 | LL | #[label(code = "...")] | ^ error: derive(Diagnostic): the `#[primary_span]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:244:5 + --> $DIR/subdiagnostic-derive.rs:235:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): label without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:241:1 + --> $DIR/subdiagnostic-derive.rs:232:1 | LL | #[label(no_crate_example)] | ^ error: derive(Diagnostic): `#[applicability]` is only valid on suggestions - --> $DIR/subdiagnostic-derive.rs:254:5 + --> $DIR/subdiagnostic-derive.rs:245:5 | LL | #[applicability] | ^ error: derive(Diagnostic): `#[bar]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:264:5 + --> $DIR/subdiagnostic-derive.rs:255:5 | LL | #[bar] | ^ @@ -151,13 +145,13 @@ LL | #[bar] = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: derive(Diagnostic): `#[bar = ...]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:275:5 + --> $DIR/subdiagnostic-derive.rs:266:5 | LL | #[bar = "..."] | ^ error: derive(Diagnostic): `#[bar(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:286:5 + --> $DIR/subdiagnostic-derive.rs:277:5 | LL | #[bar("...")] | ^ @@ -165,7 +159,7 @@ LL | #[bar("...")] = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes error: unexpected unsupported untagged union - --> $DIR/subdiagnostic-derive.rs:302:1 + --> $DIR/subdiagnostic-derive.rs:293:1 | LL | / union AC { LL | | @@ -175,97 +169,97 @@ LL | | } | |_^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/subdiagnostic-derive.rs:317:27 + --> $DIR/subdiagnostic-derive.rs:308:27 | LL | #[label(no_crate_example, no_crate::example)] | ^^^^^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:330:5 + --> $DIR/subdiagnostic-derive.rs:321:5 | LL | #[primary_span] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:327:5 + --> $DIR/subdiagnostic-derive.rs:318:5 | LL | #[primary_span] | ^ error: derive(Diagnostic): subdiagnostic kind not specified - --> $DIR/subdiagnostic-derive.rs:336:8 + --> $DIR/subdiagnostic-derive.rs:327:8 | LL | struct AG { | ^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:373:46 + --> $DIR/subdiagnostic-derive.rs:364:46 | LL | #[suggestion(no_crate_example, code = "...", code = "...")] | ^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:373:32 + --> $DIR/subdiagnostic-derive.rs:364:32 | LL | #[suggestion(no_crate_example, code = "...", code = "...")] | ^^^^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:391:5 + --> $DIR/subdiagnostic-derive.rs:382:5 | LL | #[applicability] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:388:5 + --> $DIR/subdiagnostic-derive.rs:379:5 | LL | #[applicability] | ^ error: derive(Diagnostic): the `#[applicability]` attribute can only be applied to fields of type `Applicability` - --> $DIR/subdiagnostic-derive.rs:401:5 + --> $DIR/subdiagnostic-derive.rs:392:5 | LL | #[applicability] | ^ error: derive(Diagnostic): suggestion without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:414:1 + --> $DIR/subdiagnostic-derive.rs:405:1 | LL | #[suggestion(no_crate_example)] | ^ error: derive(Diagnostic): invalid applicability - --> $DIR/subdiagnostic-derive.rs:424:62 + --> $DIR/subdiagnostic-derive.rs:415:62 | LL | #[suggestion(no_crate_example, code = "...", applicability = "foo")] | ^^^^^ error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:442:1 + --> $DIR/subdiagnostic-derive.rs:433:1 | LL | #[suggestion(no_crate_example, code = "...")] | ^ error: derive(Diagnostic): unsupported type attribute for subdiagnostic enum - --> $DIR/subdiagnostic-derive.rs:456:1 + --> $DIR/subdiagnostic-derive.rs:447:1 | LL | #[label] | ^ error: derive(Diagnostic): `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:476:39 + --> $DIR/subdiagnostic-derive.rs:467:39 | LL | #[suggestion(no_crate_example, code = "{var}", applicability = "machine-applicable")] | ^^^^^^^ error: derive(Diagnostic): `var` doesn't refer to a field on this type - --> $DIR/subdiagnostic-derive.rs:495:43 + --> $DIR/subdiagnostic-derive.rs:486:43 | LL | #[suggestion(no_crate_example, code = "{var}", applicability = "machine-applicable")] | ^^^^^^^ error: derive(Diagnostic): `#[suggestion_part]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:518:5 + --> $DIR/subdiagnostic-derive.rs:509:5 | LL | #[suggestion_part] | ^ @@ -273,7 +267,7 @@ LL | #[suggestion_part] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions, use `#[primary_span]` instead error: derive(Diagnostic): `#[suggestion_part(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:521:5 + --> $DIR/subdiagnostic-derive.rs:512:5 | LL | #[suggestion_part(code = "...")] | ^ @@ -281,13 +275,13 @@ LL | #[suggestion_part(code = "...")] = help: `#[suggestion_part(...)]` is only valid in multipart suggestions error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:515:1 + --> $DIR/subdiagnostic-derive.rs:506:1 | LL | #[suggestion(no_crate_example, code = "...")] | ^ error: derive(Diagnostic): invalid nested attribute - --> $DIR/subdiagnostic-derive.rs:530:42 + --> $DIR/subdiagnostic-derive.rs:521:42 | LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] | ^^^^ @@ -295,25 +289,25 @@ LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "mac = help: only `style` and `applicability` are valid nested attributes error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:530:1 + --> $DIR/subdiagnostic-derive.rs:521:1 | LL | #[multipart_suggestion(no_crate_example, code = "...", applicability = "machine-applicable")] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:540:5 + --> $DIR/subdiagnostic-derive.rs:531:5 | LL | #[suggestion_part] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:548:5 + --> $DIR/subdiagnostic-derive.rs:539:5 | LL | #[suggestion_part()] | ^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:557:5 + --> $DIR/subdiagnostic-derive.rs:548:5 | LL | #[primary_span] | ^ @@ -321,127 +315,127 @@ LL | #[primary_span] = help: multipart suggestions use one or more `#[suggestion_part]`s rather than one `#[primary_span]` error: derive(Diagnostic): multipart suggestion without any `#[suggestion_part(...)]` fields - --> $DIR/subdiagnostic-derive.rs:554:1 + --> $DIR/subdiagnostic-derive.rs:545:1 | LL | #[multipart_suggestion(no_crate_example)] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:565:5 + --> $DIR/subdiagnostic-derive.rs:556:5 | LL | #[suggestion_part] | ^ error: derive(Diagnostic): `#[suggestion_part(...)]` attribute without `code = "..."` - --> $DIR/subdiagnostic-derive.rs:568:5 + --> $DIR/subdiagnostic-derive.rs:559:5 | LL | #[suggestion_part()] | ^ error: derive(Diagnostic): `code` is the only valid nested attribute - --> $DIR/subdiagnostic-derive.rs:571:23 + --> $DIR/subdiagnostic-derive.rs:562:23 | LL | #[suggestion_part(foo = "bar")] | ^^^ error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:575:5 + --> $DIR/subdiagnostic-derive.rs:566:5 | LL | #[suggestion_part(code = "...")] | ^ error: derive(Diagnostic): the `#[suggestion_part(...)]` attribute can only be applied to fields of type `Span` or `MultiSpan` - --> $DIR/subdiagnostic-derive.rs:578:5 + --> $DIR/subdiagnostic-derive.rs:569:5 | LL | #[suggestion_part()] | ^ error: expected `,` - --> $DIR/subdiagnostic-derive.rs:571:27 + --> $DIR/subdiagnostic-derive.rs:562:27 | LL | #[suggestion_part(foo = "bar")] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:586:37 + --> $DIR/subdiagnostic-derive.rs:577:37 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:586:23 + --> $DIR/subdiagnostic-derive.rs:577:23 | LL | #[suggestion_part(code = "...", code = ",,,")] | ^^^^ error: derive(Diagnostic): `#[applicability]` has no effect if all `#[suggestion]`/`#[multipart_suggestion]` attributes have a static `applicability = "..."` - --> $DIR/subdiagnostic-derive.rs:615:5 + --> $DIR/subdiagnostic-derive.rs:606:5 | LL | #[applicability] | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:663:28 + --> $DIR/subdiagnostic-derive.rs:654:28 | LL | #[suggestion_part(code("foo"))] | ^^^^^ error: unexpected token, expected `)` - --> $DIR/subdiagnostic-derive.rs:663:28 + --> $DIR/subdiagnostic-derive.rs:654:28 | LL | #[suggestion_part(code("foo"))] | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:673:28 + --> $DIR/subdiagnostic-derive.rs:664:28 | LL | #[suggestion_part(code("foo", "bar"))] | ^^^^^ error: unexpected token, expected `)` - --> $DIR/subdiagnostic-derive.rs:673:28 + --> $DIR/subdiagnostic-derive.rs:664:28 | LL | #[suggestion_part(code("foo", "bar"))] | ^^^^^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:683:28 + --> $DIR/subdiagnostic-derive.rs:674:28 | LL | #[suggestion_part(code(3))] | ^ error: unexpected token, expected `)` - --> $DIR/subdiagnostic-derive.rs:683:28 + --> $DIR/subdiagnostic-derive.rs:674:28 | LL | #[suggestion_part(code(3))] | ^ error: derive(Diagnostic): expected exactly one string literal for `code = ...` - --> $DIR/subdiagnostic-derive.rs:693:28 + --> $DIR/subdiagnostic-derive.rs:684:28 | LL | #[suggestion_part(code())] | ^ error: expected string literal - --> $DIR/subdiagnostic-derive.rs:702:30 + --> $DIR/subdiagnostic-derive.rs:693:30 | LL | #[suggestion_part(code = 3)] | ^ error: derive(Diagnostic): attribute specified multiple times - --> $DIR/subdiagnostic-derive.rs:744:1 + --> $DIR/subdiagnostic-derive.rs:735:1 | LL | #[suggestion(no_crate_example, code = "", style = "hidden", style = "normal")] | ^ | note: previously specified here - --> $DIR/subdiagnostic-derive.rs:744:1 + --> $DIR/subdiagnostic-derive.rs:735:1 | LL | #[suggestion(no_crate_example, code = "", style = "hidden", style = "normal")] | ^ error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:753:1 + --> $DIR/subdiagnostic-derive.rs:744:1 | LL | #[suggestion_hidden(no_crate_example, code = "")] | ^ @@ -449,7 +443,7 @@ LL | #[suggestion_hidden(no_crate_example, code = "")] = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): `#[suggestion_hidden(...)]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:761:1 + --> $DIR/subdiagnostic-derive.rs:752:1 | LL | #[suggestion_hidden(no_crate_example, code = "", style = "normal")] | ^ @@ -457,7 +451,7 @@ LL | #[suggestion_hidden(no_crate_example, code = "", style = "normal")] = help: Use `#[suggestion(..., style = "hidden")]` instead error: derive(Diagnostic): invalid suggestion style - --> $DIR/subdiagnostic-derive.rs:769:51 + --> $DIR/subdiagnostic-derive.rs:760:51 | LL | #[suggestion(no_crate_example, code = "", style = "foo")] | ^^^^^ @@ -465,25 +459,25 @@ LL | #[suggestion(no_crate_example, code = "", style = "foo")] = help: valid styles are `normal`, `short`, `hidden`, `verbose` and `tool-only` error: expected string literal - --> $DIR/subdiagnostic-derive.rs:777:51 + --> $DIR/subdiagnostic-derive.rs:768:51 | LL | #[suggestion(no_crate_example, code = "", style = 42)] | ^^ error: derive(Diagnostic): a diagnostic slug must be the first argument to the attribute - --> $DIR/subdiagnostic-derive.rs:785:43 + --> $DIR/subdiagnostic-derive.rs:776:43 | LL | #[suggestion(no_crate_example, code = "", style)] | ^^^^^ error: expected `=` - --> $DIR/subdiagnostic-derive.rs:793:48 + --> $DIR/subdiagnostic-derive.rs:784:48 | LL | #[suggestion(no_crate_example, code = "", style("foo"))] | ^ error: derive(Diagnostic): `#[primary_span]` is not a valid attribute - --> $DIR/subdiagnostic-derive.rs:804:5 + --> $DIR/subdiagnostic-derive.rs:795:5 | LL | #[primary_span] | ^ @@ -492,7 +486,7 @@ LL | #[primary_span] = help: to create a suggestion with multiple spans, use `#[multipart_suggestion]` instead error: derive(Diagnostic): suggestion without `#[primary_span]` field - --> $DIR/subdiagnostic-derive.rs:801:1 + --> $DIR/subdiagnostic-derive.rs:792:1 | LL | #[suggestion(no_crate_example, code = "")] | ^ @@ -504,52 +498,52 @@ LL | #[foo] | ^^^ error: cannot find attribute `foo` in this scope - --> $DIR/subdiagnostic-derive.rs:153:3 + --> $DIR/subdiagnostic-derive.rs:144:3 | LL | #[foo] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:167:7 + --> $DIR/subdiagnostic-derive.rs:158:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:179:7 + --> $DIR/subdiagnostic-derive.rs:170:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:191:7 + --> $DIR/subdiagnostic-derive.rs:182:7 | LL | #[bar = 4] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:203:7 + --> $DIR/subdiagnostic-derive.rs:194:7 | LL | #[bar("...")] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:264:7 + --> $DIR/subdiagnostic-derive.rs:255:7 | LL | #[bar] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:275:7 + --> $DIR/subdiagnostic-derive.rs:266:7 | LL | #[bar = "..."] | ^^^ error: cannot find attribute `bar` in this scope - --> $DIR/subdiagnostic-derive.rs:286:7 + --> $DIR/subdiagnostic-derive.rs:277:7 | LL | #[bar("...")] | ^^^ -error: aborting due to 83 previous errors +error: aborting due to 82 previous errors From ca2be71a18487405d72ae87d84e8fc75bb366b82 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Fri, 30 Jan 2026 21:23:40 +0100 Subject: [PATCH 212/319] Add verification for inline fluent messages --- Cargo.lock | 2 + compiler/rustc_macros/Cargo.toml | 2 + .../src/diagnostics/diagnostic.rs | 53 ++------ .../src/diagnostics/diagnostic_builder.rs | 38 ++++-- .../rustc_macros/src/diagnostics/message.rs | 127 ++++++++++++++++-- .../src/diagnostics/subdiagnostic.rs | 9 +- .../rustc_macros/src/diagnostics/utils.rs | 2 +- .../diagnostic-derive-inline.rs | 13 ++ .../diagnostic-derive-inline.stderr | 10 +- 9 files changed, 183 insertions(+), 73 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6be4565374af..9acb62b234ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4229,6 +4229,8 @@ dependencies = [ name = "rustc_macros" version = "0.0.0" dependencies = [ + "fluent-bundle", + "fluent-syntax", "proc-macro2", "quote", "syn 2.0.110", diff --git a/compiler/rustc_macros/Cargo.toml b/compiler/rustc_macros/Cargo.toml index f9d3b7583590..f097aee54abb 100644 --- a/compiler/rustc_macros/Cargo.toml +++ b/compiler/rustc_macros/Cargo.toml @@ -8,6 +8,8 @@ proc-macro = true [dependencies] # tidy-alphabetical-start +fluent-bundle = "0.16" +fluent-syntax = "0.12" proc-macro2 = "1" quote = "1" syn = { version = "2.0.9", features = ["full"] } diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index 1ae6393b2860..b4270f45422e 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -22,7 +22,7 @@ impl<'a> DiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let DiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::Diagnostic; - let slugs = RefCell::new(Vec::new()); + let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -30,8 +30,8 @@ impl<'a> DiagnosticDerive<'a> { let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().extend(message.slug().cloned()); - let message = message.diag_message(); + messages.borrow_mut().push(message.clone()); + let message = message.diag_message(variant); let init = quote! { let mut diag = rustc_errors::Diag::new( @@ -68,7 +68,7 @@ impl<'a> DiagnosticDerive<'a> { } } }); - for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) { + for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { imp.extend(test); } imp @@ -88,7 +88,7 @@ impl<'a> LintDiagnosticDerive<'a> { pub(crate) fn into_tokens(self) -> TokenStream { let LintDiagnosticDerive { mut structure } = self; let kind = DiagnosticDeriveKind::LintDiagnostic; - let slugs = RefCell::new(Vec::new()); + let messages = RefCell::new(Vec::new()); let implementation = kind.each_variant(&mut structure, |mut builder, variant| { let preamble = builder.preamble(variant); let body = builder.body(variant); @@ -96,8 +96,8 @@ impl<'a> LintDiagnosticDerive<'a> { let Some(message) = builder.primary_message() else { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; - slugs.borrow_mut().extend(message.slug().cloned()); - let message = message.diag_message(); + messages.borrow_mut().push(message.clone()); + let message = message.diag_message(variant); let primary_message = quote! { diag.primary_message(#message); }; @@ -125,47 +125,10 @@ impl<'a> LintDiagnosticDerive<'a> { } } }); - for test in slugs.borrow().iter().map(|s| generate_test(s, &structure)) { + for test in messages.borrow().iter().map(|s| s.generate_test(&structure)) { imp.extend(test); } imp } } - -/// Generates a `#[test]` that verifies that all referenced variables -/// exist on this structure. -fn generate_test(slug: &syn::Path, structure: &Structure<'_>) -> TokenStream { - // FIXME: We can't identify variables in a subdiagnostic - for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) { - for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) { - if attr_name == "subdiagnostic" { - return quote!(); - } - } - } - use std::sync::atomic::{AtomicUsize, Ordering}; - // We need to make sure that the same diagnostic slug can be used multiple times without - // causing an error, so just have a global counter here. - static COUNTER: AtomicUsize = AtomicUsize::new(0); - let slug = slug.get_ident().unwrap(); - let ident = quote::format_ident!("verify_{slug}_{}", COUNTER.fetch_add(1, Ordering::Relaxed)); - let ref_slug = quote::format_ident!("{slug}_refs"); - let struct_name = &structure.ast().ident; - let variables: Vec<_> = structure - .variants() - .iter() - .flat_map(|v| v.ast().fields.iter().filter_map(|f| f.ident.as_ref().map(|i| i.to_string()))) - .collect(); - // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this - quote! { - #[cfg(test)] - #[test ] - fn #ident() { - let variables = [#(#variables),*]; - for vref in crate::fluent_generated::#ref_slug { - assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug)); - } - } - } -} diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index 14eda017970f..e6d9409a1fa3 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -137,7 +137,8 @@ impl DiagnosticDeriveVariantBuilder { let ast = variant.ast(); let attrs = &ast.attrs; let preamble = attrs.iter().map(|attr| { - self.generate_structure_code_for_attr(attr).unwrap_or_else(|v| v.to_compile_error()) + self.generate_structure_code_for_attr(attr, variant) + .unwrap_or_else(|v| v.to_compile_error()) }); quote! { @@ -155,7 +156,7 @@ impl DiagnosticDeriveVariantBuilder { } // ..and then subdiagnostic additions. for binding in variant.bindings().iter().filter(|bi| !should_generate_arg(bi.ast())) { - body.extend(self.generate_field_attrs_code(binding)); + body.extend(self.generate_field_attrs_code(binding, variant)); } body } @@ -199,6 +200,7 @@ impl DiagnosticDeriveVariantBuilder { fn generate_structure_code_for_attr( &mut self, attr: &Attribute, + variant: &VariantInfo<'_>, ) -> Result { // Always allow documentation comments. if is_doc_comment(attr) { @@ -224,7 +226,7 @@ impl DiagnosticDeriveVariantBuilder { ) .emit(); } - self.message = Some(Message::Inline(message.value())); + self.message = Some(Message::Inline(message.span(), message.value())); } else { // Parse a slug let slug = input.parse::()?; @@ -285,7 +287,7 @@ impl DiagnosticDeriveVariantBuilder { | SubdiagnosticKind::NoteOnce | SubdiagnosticKind::Help | SubdiagnosticKind::HelpOnce - | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug)), + | SubdiagnosticKind::Warn => Ok(self.add_subdiagnostic(&fn_ident, slug, variant)), SubdiagnosticKind::Label | SubdiagnosticKind::Suggestion { .. } => { throw_invalid_attr!(attr, |diag| diag .help("`#[label]` and `#[suggestion]` can only be applied to fields")); @@ -313,7 +315,11 @@ impl DiagnosticDeriveVariantBuilder { } } - fn generate_field_attrs_code(&mut self, binding_info: &BindingInfo<'_>) -> TokenStream { + fn generate_field_attrs_code( + &mut self, + binding_info: &BindingInfo<'_>, + variant: &VariantInfo<'_>, + ) -> TokenStream { let field = binding_info.ast(); let field_binding = &binding_info.binding; @@ -352,6 +358,7 @@ impl DiagnosticDeriveVariantBuilder { attr, FieldInfo { binding: binding_info, ty: inner_ty, span: &field.span() }, binding, + variant ) .unwrap_or_else(|v| v.to_compile_error()); @@ -369,6 +376,7 @@ impl DiagnosticDeriveVariantBuilder { attr: &Attribute, info: FieldInfo<'_>, binding: TokenStream, + variant: &VariantInfo<'_>, ) -> Result { let ident = &attr.path().segments.last().unwrap().ident; let name = ident.to_string(); @@ -407,7 +415,7 @@ impl DiagnosticDeriveVariantBuilder { match subdiag { SubdiagnosticKind::Label => { report_error_if_not_applied_to_span(attr, &info)?; - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) } SubdiagnosticKind::Note | SubdiagnosticKind::NoteOnce @@ -418,11 +426,11 @@ impl DiagnosticDeriveVariantBuilder { if type_matches_path(inner, &["rustc_span", "Span"]) || type_matches_path(inner, &["rustc_span", "MultiSpan"]) { - Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug)) + Ok(self.add_spanned_subdiagnostic(binding, &fn_ident, slug, variant)) } else if type_is_unit(inner) || (matches!(info.ty, FieldInnerTy::Plain(_)) && type_is_bool(inner)) { - Ok(self.add_subdiagnostic(&fn_ident, slug)) + Ok(self.add_subdiagnostic(&fn_ident, slug, variant)) } else { report_type_error(attr, "`Span`, `MultiSpan`, `bool` or `()`")? } @@ -448,7 +456,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } - let message = slug.diag_message(); + let message = slug.diag_message(variant); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -476,9 +484,10 @@ impl DiagnosticDeriveVariantBuilder { field_binding: TokenStream, kind: &Ident, message: Message, + variant: &VariantInfo<'_>, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); - let message = message.diag_message(); + let message = message.diag_message(variant); quote! { diag.#fn_name( #field_binding, @@ -489,8 +498,13 @@ impl DiagnosticDeriveVariantBuilder { /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug /// and `fluent_attr_identifier`. - fn add_subdiagnostic(&self, kind: &Ident, message: Message) -> TokenStream { - let message = message.diag_message(); + fn add_subdiagnostic( + &self, + kind: &Ident, + message: Message, + variant: &VariantInfo<'_>, + ) -> TokenStream { + let message = message.diag_message(variant); quote! { diag.#kind(#message); } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index cfce252fbdd8..153abecf8937 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -1,28 +1,133 @@ -use proc_macro2::TokenStream; +use fluent_bundle::FluentResource; +use fluent_syntax::ast::{Expression, InlineExpression, Pattern, PatternElement}; +use proc_macro2::{Span, TokenStream}; use quote::quote; use syn::Path; +use synstructure::{Structure, VariantInfo}; +use crate::diagnostics::error::span_err; + +#[derive(Clone)] pub(crate) enum Message { Slug(Path), - Inline(String), + Inline(Span, String), } impl Message { - pub(crate) fn slug(&self) -> Option<&Path> { - match self { - Message::Slug(slug) => Some(slug), - Message::Inline(_) => None, - } - } - - pub(crate) fn diag_message(&self) -> TokenStream { + pub(crate) fn diag_message(&self, variant: &VariantInfo<'_>) -> TokenStream { match self { Message::Slug(slug) => { quote! { crate::fluent_generated::#slug } } - Message::Inline(message) => { + Message::Inline(message_span, message) => { + verify_fluent_message(*message_span, &message, variant); quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } } } } + + /// Generates a `#[test]` that verifies that all referenced variables + /// exist on this structure. + pub(crate) fn generate_test(&self, structure: &Structure<'_>) -> TokenStream { + match self { + Message::Slug(slug) => { + // FIXME: We can't identify variables in a subdiagnostic + for field in structure.variants().iter().flat_map(|v| v.ast().fields.iter()) { + for attr_name in field.attrs.iter().filter_map(|at| at.path().get_ident()) { + if attr_name == "subdiagnostic" { + return quote!(); + } + } + } + use std::sync::atomic::{AtomicUsize, Ordering}; + // We need to make sure that the same diagnostic slug can be used multiple times without + // causing an error, so just have a global counter here. + static COUNTER: AtomicUsize = AtomicUsize::new(0); + let slug = slug.get_ident().unwrap(); + let ident = quote::format_ident!( + "verify_{slug}_{}", + COUNTER.fetch_add(1, Ordering::Relaxed) + ); + let ref_slug = quote::format_ident!("{slug}_refs"); + let struct_name = &structure.ast().ident; + let variables: Vec<_> = structure + .variants() + .iter() + .flat_map(|v| { + v.ast() + .fields + .iter() + .filter_map(|f| f.ident.as_ref().map(|i| i.to_string())) + }) + .collect(); + // tidy errors on `#[test]` outside of test files, so we use `#[test ]` to work around this + quote! { + #[cfg(test)] + #[test ] + fn #ident() { + let variables = [#(#variables),*]; + for vref in crate::fluent_generated::#ref_slug { + assert!(variables.contains(vref), "{}: variable `{vref}` not found ({})", stringify!(#struct_name), stringify!(#slug)); + } + } + } + } + Message::Inline(..) => { + // We don't generate a test for inline diagnostics, we can verify these at compile-time! + // This verification is done in the `diag_message` function above + quote! {} + } + } + } +} + +fn verify_fluent_message(msg_span: Span, message: &str, variant: &VariantInfo<'_>) { + // Parse the fluent message + const GENERATED_MSG_ID: &str = "generated_msg"; + let resource = FluentResource::try_new(format!("{GENERATED_MSG_ID} = {message}\n")).unwrap(); + assert_eq!(resource.entries().count(), 1); + let Some(fluent_syntax::ast::Entry::Message(message)) = resource.get_entry(0) else { + panic!("Did not parse into a message") + }; + + // Check if all variables are used + let fields: Vec = variant + .bindings() + .iter() + .flat_map(|b| b.ast().ident.as_ref()) + .map(|id| id.to_string()) + .collect(); + for variable in variable_references(&message) { + if !fields.iter().any(|f| f == variable) { + span_err(msg_span.unwrap(), format!("Variable `{variable}` not found in diagnostic ")) + .help(format!("Available fields: {:?}", fields.join(", "))) + .emit(); + } + // assert!(, ); + } +} + +fn variable_references<'a>(msg: &fluent_syntax::ast::Message<&'a str>) -> Vec<&'a str> { + let mut refs = vec![]; + if let Some(Pattern { elements }) = &msg.value { + for elt in elements { + if let PatternElement::Placeable { + expression: Expression::Inline(InlineExpression::VariableReference { id }), + } = elt + { + refs.push(id.name); + } + } + } + for attr in &msg.attributes { + for elt in &attr.value.elements { + if let PatternElement::Placeable { + expression: Expression::Inline(InlineExpression::VariableReference { id }), + } = elt + { + refs.push(id.name); + } + } + } + refs } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index 61a234f96d12..adc968dacd5c 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -75,7 +75,7 @@ impl SubdiagnosticDerive { has_subdiagnostic: false, is_enum, }; - builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) + builder.into_tokens(variant).unwrap_or_else(|v| v.to_compile_error()) }); quote! { @@ -497,7 +497,10 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } - pub(crate) fn into_tokens(&mut self) -> Result { + pub(crate) fn into_tokens( + &mut self, + variant: &VariantInfo<'_>, + ) -> Result { let kind_slugs = self.identify_kind()?; let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); @@ -535,7 +538,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { let message = format_ident!("__message"); - let message_stream = slug.diag_message(); + let message_stream = slug.diag_message(variant); calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); diff --git a/compiler/rustc_macros/src/diagnostics/utils.rs b/compiler/rustc_macros/src/diagnostics/utils.rs index 0718448a0513..a5265a847a9c 100644 --- a/compiler/rustc_macros/src/diagnostics/utils.rs +++ b/compiler/rustc_macros/src/diagnostics/utils.rs @@ -708,7 +708,7 @@ impl SubdiagnosticVariant { } if !input.is_empty() { input.parse::()?; } if is_first { - slug = Some(Message::Inline(message.value())); + slug = Some(Message::Inline(message.span(), message.value())); is_first = false; } else { span_err(message.span().unwrap(), "a diagnostic message must be the first argument to the attribute").emit(); diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs index 7d9af0522f63..babe3813e40b 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.rs @@ -762,3 +762,16 @@ struct SuggestionOnVec { //~^ ERROR `#[suggestion(...)]` is not a valid attribute sub: Vec, } + +#[derive(Diagnostic)] +#[diag("exists: {$sub}")] +struct VariableExists { + sub: String, +} + +#[derive(Diagnostic)] +#[diag("does not exist: {$nosub}")] +//~^ ERROR Variable `nosub` not found in diagnostic +struct VariableDoesNotExist { + sub: String, +} diff --git a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr index bec07a425762..2ba307940280 100644 --- a/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr +++ b/tests/ui-fulldeps/session-diagnostic/diagnostic-derive-inline.stderr @@ -508,6 +508,14 @@ LL | #[suggestion("with a suggestion", code = "")] = help: to show a suggestion consisting of multiple parts, use a `Subdiagnostic` annotated with `#[multipart_suggestion(...)]` = help: to show a variable set of suggestions, use a `Vec` of `Subdiagnostic`s annotated with `#[suggestion(...)]` +error: derive(Diagnostic): Variable `nosub` not found in diagnostic + --> $DIR/diagnostic-derive-inline.rs:773:8 + | +LL | #[diag("does not exist: {$nosub}")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: Available fields: "sub" + error: cannot find attribute `nonsense` in this scope --> $DIR/diagnostic-derive-inline.rs:61:3 | @@ -622,6 +630,6 @@ note: required by a bound in `Diag::<'a, G>::arg` = note: in this macro invocation = note: this error originates in the macro `with_fn` (in Nightly builds, run with -Z macro-backtrace for more info) -error: aborting due to 79 previous errors +error: aborting due to 80 previous errors For more information about this error, try `rustc --explain E0277`. From 16b79b3310619ea5a31cc73e6b501102e6e55f3b Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 1 Feb 2026 13:58:01 +0100 Subject: [PATCH 213/319] fix: Fix upvar analysis of nested closures --- .../crates/hir-def/src/expr_store.rs | 4 +- .../hir-ty/src/tests/closure_captures.rs | 82 ++++++++++++++----- .../src/handlers/mutability_errors.rs | 30 +++---- .../src/handlers/unused_variables.rs | 42 +++++----- 4 files changed, 92 insertions(+), 66 deletions(-) diff --git a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs index edbfd42d1314..1ce4c881e7ea 100644 --- a/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs +++ b/src/tools/rust-analyzer/crates/hir-def/src/expr_store.rs @@ -474,8 +474,8 @@ impl ExpressionStore { match expr_only.binding_owners.get(&binding) { Some(it) => { // We assign expression ids in a way that outer closures will receive - // a lower id - it.into_raw() < relative_to.into_raw() + // a higher id (allocated after their body is collected) + it.into_raw() > relative_to.into_raw() } None => true, } diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs index 8408c0a7bfcd..f089120cd7b8 100644 --- a/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs +++ b/src/tools/rust-analyzer/crates/hir-ty/src/tests/closure_captures.rs @@ -135,7 +135,7 @@ fn check_closure_captures(#[rust_analyzer::rust_fixture] ra_fixture: &str, expec fn deref_in_let() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let b = *a; }; @@ -149,7 +149,7 @@ fn main() { fn deref_then_ref_pattern() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let &mut ref b = a; }; @@ -159,7 +159,7 @@ fn main() { ); check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let &mut ref mut b = a; }; @@ -173,7 +173,7 @@ fn main() { fn unique_borrow() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { *a = false; }; @@ -187,7 +187,7 @@ fn main() { fn deref_ref_mut() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let ref mut b = *a; }; @@ -201,7 +201,7 @@ fn main() { fn let_else_not_consuming() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let a = &mut true; let closure = || { let _ = *a else { return; }; }; @@ -215,7 +215,7 @@ fn main() { fn consume() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let a = NonCopy; @@ -230,7 +230,7 @@ fn main() { fn ref_to_upvar() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = NonCopy; @@ -248,7 +248,7 @@ fn main() { fn field() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct Foo { a: i32, b: i32 } fn main() { let a = Foo { a: 0, b: 0 }; @@ -263,7 +263,7 @@ fn main() { fn fields_different_mode() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; struct Foo { a: i32, b: i32, c: NonCopy, d: bool } fn main() { @@ -286,7 +286,7 @@ fn main() { fn autoref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct Foo; impl Foo { fn imm(&self) {} @@ -308,7 +308,7 @@ fn main() { fn captures_priority() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = &mut true; @@ -336,7 +336,7 @@ fn main() { fn let_underscore() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = true; let closure = || { let _ = a; }; @@ -350,7 +350,7 @@ fn main() { fn match_wildcard() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct NonCopy; fn main() { let mut a = NonCopy; @@ -375,7 +375,7 @@ fn main() { fn multiple_bindings() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { let (b | b) = a; }; @@ -389,7 +389,7 @@ fn main() { fn multiple_usages() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { @@ -410,7 +410,7 @@ fn main() { fn ref_then_deref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = false; let mut closure = || { let b = *&mut a; }; @@ -424,7 +424,7 @@ fn main() { fn ref_of_ref() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn fn main() { let mut a = &false; let closure = || { let b = &a; }; @@ -446,7 +446,7 @@ fn main() { fn multiple_capture_usages() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct A { a: i32, b: bool } fn main() { let mut a = A { a: 123, b: false }; @@ -465,7 +465,7 @@ fn main() { fn let_binding_is_a_ref_capture_in_ref_binding() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn struct S; fn main() { let mut s = S; @@ -489,7 +489,7 @@ fn main() { fn let_binding_is_a_value_capture_in_binding() { check_closure_captures( r#" -//- minicore:copy, option +//- minicore:copy, fn, option struct Box(i32); fn main() { let b = Some(Box(0)); @@ -508,7 +508,7 @@ fn main() { fn alias_needs_to_be_normalized() { check_closure_captures( r#" -//- minicore:copy +//- minicore:copy, fn trait Trait { type Associated; } @@ -528,3 +528,41 @@ fn main() { expect!["220..257;174..175;245..250 ByRef(Shared) c.b.x &'? i32"], ); } + +#[test] +fn nested_ref_captures_from_outer() { + check_closure_captures( + r#" +//- minicore:copy, fn +fn f() { + let a = 1; + let a_closure = || { + let b_closure = || { + { a }; + }; + }; +} +"#, + expect![[r#" + 44..113;17..18;92..93 ByRef(Shared) a &'? i32 + 73..106;17..18;92..93 ByRef(Shared) a &'? i32"#]], + ); +} + +#[test] +fn nested_ref_captures() { + check_closure_captures( + r#" +//- minicore:copy, fn +fn f() { + let a_closure = || { + let b = 2; + let b_closure = || { + { b }; + }; + }; +} +"#, + expect!["77..110;46..47;96..97 ByRef(Shared) b &'? i32"], + ); +} diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs index e3cfbdfb515f..18280a4addec 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/mutability_errors.rs @@ -1,5 +1,3 @@ -#![expect(unused, reason = "diagnostics is temporarily disabled due to too many false positives")] - use hir::db::ExpandDatabase; use ide_db::source_change::SourceChange; use ide_db::text_edit::TextEdit; @@ -90,17 +88,16 @@ pub(crate) fn unused_mut(ctx: &DiagnosticsContext<'_>, d: &hir::UnusedMut) -> Op )]) })(); let ast = d.local.primary_source(ctx.sema.db).syntax_ptr(); - // Some( - // Diagnostic::new_with_syntax_node_ptr( - // ctx, - // DiagnosticCode::RustcLint("unused_mut"), - // "variable does not need to be mutable", - // ast, - // ) - // // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. - // .with_fixes(fixes), - // ) - None + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_mut"), + "variable does not need to be mutable", + ast, + ) + // Not supporting `#[allow(unused_mut)]` in proc macros leads to false positive, hence not stable. + .with_fixes(fixes), + ) } pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option { @@ -108,7 +105,6 @@ pub(super) fn token(parent: &SyntaxNode, kind: SyntaxKind) -> Option u8) -> u8 { } "#, ); - // FIXME: There should be no "unused variable" here, and there should be a mutability error, - // but our MIR infra is horribly broken and due to the order in which expressions are lowered - // there is no `StorageLive` for `x` in the closure (in fact, `x` should not even be a variable - // of the closure, the environment should be, but as I said, our MIR infra is horribly broken). check_diagnostics( r#" //- minicore: copy, fn @@ -1011,8 +1003,8 @@ fn f() { || { || { let x = 2; - // ^ 💡 warn: unused variable || { || { x = 5; } } + //^^^^^ 💡 error: cannot mutate immutable variable `x` } } }; diff --git a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs index ff558d067061..52a2f44fd0f8 100644 --- a/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs +++ b/src/tools/rust-analyzer/crates/ide-diagnostics/src/handlers/unused_variables.rs @@ -1,5 +1,3 @@ -#![expect(unused, reason = "diagnostics is temporarily disabled due to too many false positives")] - use hir::Name; use ide_db::text_edit::TextEdit; use ide_db::{ @@ -42,26 +40,25 @@ pub(crate) fn unused_variables( .and_then(syntax::ast::RecordPatField::cast) .is_some_and(|field| field.colon_token().is_none()); let var_name = d.local.name(ctx.sema.db); - // Some( - // Diagnostic::new_with_syntax_node_ptr( - // ctx, - // DiagnosticCode::RustcLint("unused_variables"), - // "unused variable", - // ast, - // ) - // .with_fixes(name_range.and_then(|it| { - // fixes( - // ctx.sema.db, - // var_name, - // it.range, - // diagnostic_range, - // ast.file_id.is_macro(), - // is_shorthand_field, - // ctx.edition, - // ) - // })), - // ) - None + Some( + Diagnostic::new_with_syntax_node_ptr( + ctx, + DiagnosticCode::RustcLint("unused_variables"), + "unused variable", + ast, + ) + .with_fixes(name_range.and_then(|it| { + fixes( + ctx.sema.db, + var_name, + it.range, + diagnostic_range, + ast.file_id.is_macro(), + is_shorthand_field, + ctx.edition, + ) + })), + ) } fn fixes( @@ -94,7 +91,6 @@ fn fixes( } #[cfg(test)] -#[cfg(false)] // Diagnostic temporarily disabled mod tests { use crate::tests::{check_diagnostics, check_fix}; From 2bab7a02f2caf77f53637838ef3cbf67bb718f6f Mon Sep 17 00:00:00 2001 From: yukang Date: Sat, 31 Jan 2026 02:06:47 +0000 Subject: [PATCH 214/319] Handle unbalanced delimiters gracefully in make_attr_token_stream --- compiler/rustc_ast/src/tokenstream.rs | 8 +- .../ui/macros/tokenstream-ice-issue-149954.rs | 22 ++++ .../tokenstream-ice-issue-149954.stderr | 110 ++++++++++++++++++ 3 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 tests/ui/macros/tokenstream-ice-issue-149954.rs create mode 100644 tests/ui/macros/tokenstream-ice-issue-149954.stderr diff --git a/compiler/rustc_ast/src/tokenstream.rs b/compiler/rustc_ast/src/tokenstream.rs index e346a56bcf40..8d8e8ffc562f 100644 --- a/compiler/rustc_ast/src/tokenstream.rs +++ b/compiler/rustc_ast/src/tokenstream.rs @@ -354,7 +354,13 @@ fn make_attr_token_stream( FrameData { open_delim_sp: Some((delim, span, spacing)), inner: vec![] }, )); } else if let Some(delim) = kind.close_delim() { - let frame_data = mem::replace(&mut stack_top, stack_rest.pop().unwrap()); + // If there's no matching opening delimiter, the token stream is malformed, + // likely due to a improper delimiter positions in the source code. + // It's not delimiter mismatch, and lexer can not detect it, so we just ignore it here. + let Some(frame) = stack_rest.pop() else { + return AttrTokenStream::new(stack_top.inner); + }; + let frame_data = mem::replace(&mut stack_top, frame); let (open_delim, open_sp, open_spacing) = frame_data.open_delim_sp.unwrap(); assert!( open_delim.eq_ignoring_invisible_origin(&delim), diff --git a/tests/ui/macros/tokenstream-ice-issue-149954.rs b/tests/ui/macros/tokenstream-ice-issue-149954.rs new file mode 100644 index 000000000000..958a86cbde8b --- /dev/null +++ b/tests/ui/macros/tokenstream-ice-issue-149954.rs @@ -0,0 +1,22 @@ +// Regression test for ICE https://github.com/rust-lang/rust/issues/149954 +//@ edition: 2024 + +enum A { + A + const A: A = { //~ ERROR expected one of `(`, `,`, `=`, `{`, or `}`, found keyword `const` + #[derive(Debug)] + struct A + where + A: A<{ struct A> ; enum A } + //~^ ERROR malformed `cfg` attribute input + //~| ERROR malformed `cfg` attribute input + //~| ERROR expected trait, found struct `A` + //~| ERROR expected trait, found type parameter `A` + //~| ERROR expected trait, found struct `A` + //~| ERROR expected trait, found type parameter `A` + //~| ERROR expected one of `<`, `where`, or `{`, found `}` + //~| ERROR expected one of `<`, `where`, or `{`, found `}` + //~| ERROR expected one of `,`, `>`, or `}`, found `` + } + >; +}; //~ ERROR `main` function not found in crate diff --git a/tests/ui/macros/tokenstream-ice-issue-149954.stderr b/tests/ui/macros/tokenstream-ice-issue-149954.stderr new file mode 100644 index 000000000000..750f3efcc612 --- /dev/null +++ b/tests/ui/macros/tokenstream-ice-issue-149954.stderr @@ -0,0 +1,110 @@ +error: expected one of `(`, `,`, `=`, `{`, or `}`, found keyword `const` + --> $DIR/tokenstream-ice-issue-149954.rs:6:5 + | +LL | A + | - expected one of `(`, `,`, `=`, `{`, or `}` +LL | const A: A = { + | ^^^^^ unexpected token + | + = help: enum variants can be `Variant`, `Variant = `, `Variant(Type, ..., TypeN)` or `Variant { fields: Types }` + +error: expected one of `<`, `where`, or `{`, found `}` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | - ^ expected one of `<`, `where`, or `{` + | | + | while parsing this enum + +error: expected one of `<`, `where`, or `{`, found `}` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | - ^ expected one of `<`, `where`, or `{` + | | + | while parsing this enum + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: expected one of `,`, `>`, or `}`, found `` + --> $DIR/tokenstream-ice-issue-149954.rs:10:60 + | +LL | A: A<{ struct A> ; enum A } + | ^ expected one of `,`, `>`, or `}` + | +help: you might have meant to end the type parameters here + | +LL | A: A<{ struct A> ; enum A }> + | + + +error[E0539]: malformed `cfg` attribute input + --> $DIR/tokenstream-ice-issue-149954.rs:10:36 + | +LL | A: A<{ struct A> ; enum A } + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + +error[E0539]: malformed `cfg` attribute input + --> $DIR/tokenstream-ice-issue-149954.rs:10:36 + | +LL | A: A<{ struct A> ; enum A } + | ^^^^^^ + | | + | expected this to be a list + | help: must be of the form: `#[cfg(predicate)]` + | + = note: for more information, visit + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0404]: expected trait, found struct `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:16 + | +LL | A: A<{ struct A> ; enum A } + | ________________^ +... | +LL | | >; + | |_________^ not a trait + +error[E0404]: expected trait, found type parameter `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:32 + | +LL | A: A<{ struct A> ; enum A } + | - ^^^^^^^^^^^^^^^^ not a trait + | | + | found this type parameter + +error[E0404]: expected trait, found struct `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:16 + | +LL | A: A<{ struct A> ; enum A } + | ________________^ +... | +LL | | >; + | |_________^ not a trait + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0404]: expected trait, found type parameter `A` + --> $DIR/tokenstream-ice-issue-149954.rs:10:32 + | +LL | A: A<{ struct A> ; enum A } + | - ^^^^^^^^^^^^^^^^ not a trait + | | + | found this type parameter + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error[E0601]: `main` function not found in crate `tokenstream_ice_issue_149954` + --> $DIR/tokenstream-ice-issue-149954.rs:22:3 + | +LL | }; + | ^ consider adding a `main` function to `$DIR/tokenstream-ice-issue-149954.rs` + +error: aborting due to 11 previous errors + +Some errors have detailed explanations: E0404, E0539, E0601. +For more information about an error, try `rustc --explain E0404`. From 55e3d2206a21aeb291cbaf6535579e2994d98d4a Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 1 Feb 2026 15:05:57 +0100 Subject: [PATCH 215/319] Improve new proc-macro methods name --- library/proc_macro/src/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index 2a75c4489095..18ab470f89d6 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -1445,9 +1445,9 @@ impl Literal { }) } - /// Returns the unescaped char value if the current literal is a char. + /// Returns the unescaped character value if the current literal is a byte character literal. #[unstable(feature = "proc_macro_value", issue = "136652")] - pub fn byte_value(&self) -> Result { + pub fn byte_character_value(&self) -> Result { self.0.symbol.with(|symbol| match self.0.kind { bridge::LitKind::Char => { unescape_byte(symbol).map_err(ConversionErrorKind::FailedToUnescape) @@ -1456,9 +1456,9 @@ impl Literal { }) } - /// Returns the unescaped char value if the current literal is a char. + /// Returns the unescaped character value if the current literal is a character literal. #[unstable(feature = "proc_macro_value", issue = "136652")] - pub fn char_value(&self) -> Result { + pub fn character_value(&self) -> Result { self.0.symbol.with(|symbol| match self.0.kind { bridge::LitKind::Char => { unescape_char(symbol).map_err(ConversionErrorKind::FailedToUnescape) From 990c55801ec129a1781c1b3915ff74bb85f9cdf1 Mon Sep 17 00:00:00 2001 From: mu001999 Date: Sun, 1 Feb 2026 19:17:28 +0800 Subject: [PATCH 216/319] Include assoc const projections in CFI trait object --- .../cfi/typeid/itanium_cxx_abi/transform.rs | 24 +++++++++---------- .../assoc-const-projection-issue-151878.rs | 20 ++++++++++++++++ 2 files changed, 32 insertions(+), 12 deletions(-) create mode 100644 tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs diff --git a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs index 9cea681fcb57..971ac9348fc4 100644 --- a/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs +++ b/compiler/rustc_sanitizers/src/cfi/typeid/itanium_cxx_abi/transform.rs @@ -243,24 +243,24 @@ fn trait_object_ty<'tcx>(tcx: TyCtxt<'tcx>, poly_trait_ref: ty::PolyTraitRef<'tc .flat_map(|super_poly_trait_ref| { tcx.associated_items(super_poly_trait_ref.def_id()) .in_definition_order() - .filter(|item| item.is_type()) + .filter(|item| item.is_type() || item.is_const()) .filter(|item| !tcx.generics_require_sized_self(item.def_id)) - .map(move |assoc_ty| { + .map(move |assoc_item| { super_poly_trait_ref.map_bound(|super_trait_ref| { - let alias_ty = - ty::AliasTy::new_from_args(tcx, assoc_ty.def_id, super_trait_ref.args); - let resolved = tcx.normalize_erasing_regions( - ty::TypingEnv::fully_monomorphized(), - alias_ty.to_ty(tcx), + let projection_term = ty::AliasTerm::new_from_args( + tcx, + assoc_item.def_id, + super_trait_ref.args, ); - debug!("Resolved {:?} -> {resolved}", alias_ty.to_ty(tcx)); + let term = tcx.normalize_erasing_regions( + ty::TypingEnv::fully_monomorphized(), + projection_term.to_term(tcx), + ); + debug!("Projection {:?} -> {term}", projection_term.to_term(tcx),); ty::ExistentialPredicate::Projection( ty::ExistentialProjection::erase_self_ty( tcx, - ty::ProjectionPredicate { - projection_term: alias_ty.into(), - term: resolved.into(), - }, + ty::ProjectionPredicate { projection_term, term }, ), ) }) diff --git a/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs b/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs new file mode 100644 index 000000000000..50530e4f0db0 --- /dev/null +++ b/tests/ui/sanitizer/cfi/assoc-const-projection-issue-151878.rs @@ -0,0 +1,20 @@ +//@ compile-flags: -Zsanitizer=cfi -Cunsafe-allow-abi-mismatch=sanitizer -Ccodegen-units=1 -Clto +//@ needs-rustc-debug-assertions +//@ needs-sanitizer-cfi +//@ build-pass +//@ no-prefer-dynamic + +#![feature(min_generic_const_args)] +#![expect(incomplete_features)] + +trait Trait { + #[type_const] + const N: usize = 0; + fn process(&self, _: [u8; Self::N]) {} +} + +impl Trait for () {} + +fn main() { + let _x: &dyn Trait = &(); +} From 2292d53b7b5dc3989167e753d3ac4422b321b426 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sun, 1 Feb 2026 21:41:43 +0530 Subject: [PATCH 217/319] Add codegen test for SLP vectorization --- tests/codegen-llvm/slp-vectorization-mul3.rs | 23 ++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/codegen-llvm/slp-vectorization-mul3.rs diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs new file mode 100644 index 000000000000..a414192e6a1a --- /dev/null +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -0,0 +1,23 @@ +// compile-flags: -O +#![crate_type = "lib"] + +// CHECK-LABEL: mul3 +// CHECK: %[[C_BPP:.*]] = phi <4 x i8> +// CHECK: %[[V:.*]] = load <4 x i8>, ptr +// CHECK: %[[ADD:.*]] = add <4 x i8> %[[V]], %[[C_BPP]] +// CHECK: store {{<4 x i8>|i32}} {{.*}}, ptr + +pub fn mul3(previous: &[u8], current: &mut [u8]) { + let mut c_bpp = [0u8; 4]; + + for (chunk, b_bpp) in current.chunks_exact_mut(4).zip(previous.chunks_exact(4)) { + let new_chunk = [ + chunk[0].wrapping_add(c_bpp[0]), + chunk[1].wrapping_add(c_bpp[1]), + chunk[2].wrapping_add(c_bpp[2]), + chunk[3].wrapping_add(c_bpp[3]), + ]; + chunk.copy_from_slice(&new_chunk); + c_bpp.copy_from_slice(b_bpp); + } +} \ No newline at end of file From 0a60bd653d3bd42752e4e454567ae5fcb96a2b49 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sun, 1 Feb 2026 22:09:05 +0530 Subject: [PATCH 218/319] fix: remove trailing newline for tidy --- tests/codegen-llvm/slp-vectorization-mul3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index a414192e6a1a..7a69be5cafb7 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -20,4 +20,4 @@ pub fn mul3(previous: &[u8], current: &mut [u8]) { chunk.copy_from_slice(&new_chunk); c_bpp.copy_from_slice(b_bpp); } -} \ No newline at end of file +} From 1c396d24ddf97d556d41487234f6216b67461b7c Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Sun, 1 Feb 2026 22:14:13 +0530 Subject: [PATCH 219/319] Restrict test to x86_64 per reviewer feedback --- tests/codegen-llvm/slp-vectorization-mul3.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index 7a69be5cafb7..bb4965c46bc0 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,4 +1,6 @@ -// compile-flags: -O +//@ only-x86_64 +//@ compile-flags: -O + #![crate_type = "lib"] // CHECK-LABEL: mul3 From caaee92855a64ae1f1e6cf93ce7ad29f1bd12241 Mon Sep 17 00:00:00 2001 From: Joshua Rayton Date: Tue, 23 Sep 2025 15:31:58 +0100 Subject: [PATCH 220/319] more float constants: sqrt(5), 1/sqrt(5) --- library/core/src/num/f128.rs | 11 +++++++++++ library/core/src/num/f16.rs | 10 ++++++++++ library/core/src/num/f32.rs | 8 ++++++++ library/core/src/num/f64.rs | 8 ++++++++ 4 files changed, 37 insertions(+) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index cc142fab8e82..4630fd729c6a 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -108,6 +108,17 @@ pub mod consts { pub const FRAC_1_SQRT_3: f128 = 0.577350269189625764509148780501957455647601751270126876018602_f128; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const SQRT_5: f128 = 2.23606797749978969640917366873127623544061835961152572427089_f128; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f128", issue = "116909")] + pub const FRAC_1_SQRT_5: f128 = + 0.447213595499957939281834733746255247088123671922305144854179_f128; + /// Euler's number (e) #[unstable(feature = "f128", issue = "116909")] pub const E: f128 = 2.71828182845904523536028747135266249775724709369995957496697_f128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index e97a44e991f6..21ba8f1f2828 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -103,6 +103,16 @@ pub mod consts { // Also, #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f16 = 0.577350269189625764509148780501957456_f16; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const SQRT_5: f16 = 2.23606797749978969640917366873127623_f16; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + // Also, #[unstable(feature = "f16", issue = "116909")] + pub const FRAC_1_SQRT_5: f16 = 0.44721359549995793928183473374625524_f16; + /// Euler's number (e) #[unstable(feature = "f16", issue = "116909")] pub const E: f16 = 2.71828182845904523536028747135266250_f16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 3d8249631037..c4ec774f5956 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -356,6 +356,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f32 = 0.577350269189625764509148780501957456_f32; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f32 = 2.23606797749978969640917366873127623_f32; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f32 = 0.44721359549995793928183473374625524_f32; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f32 = 2.71828182845904523536028747135266250_f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index 566a6a7ec947..0bb881323577 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -356,6 +356,14 @@ pub mod consts { #[unstable(feature = "more_float_constants", issue = "146939")] pub const FRAC_1_SQRT_3: f64 = 0.577350269189625764509148780501957456_f64; + /// sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const SQRT_5: f64 = 2.23606797749978969640917366873127623_f64; + + /// 1/sqrt(5) + #[unstable(feature = "more_float_constants", issue = "146939")] + pub const FRAC_1_SQRT_5: f64 = 0.44721359549995793928183473374625524_f64; + /// Euler's number (e) #[stable(feature = "rust1", since = "1.0.0")] pub const E: f64 = 2.71828182845904523536028747135266250_f64; From f490f6420bbc7055e212e96c1745b44436a017e3 Mon Sep 17 00:00:00 2001 From: Maarten Craeynest Date: Sun, 1 Feb 2026 21:51:27 +0100 Subject: [PATCH 221/319] Add regression test for issue-136514: Negative literal in unsigned range --- .../range-negative-literal-unsigned-type.rs | 21 +++ ...ange-negative-literal-unsigned-type.stderr | 122 ++++++++++++++++++ 2 files changed, 143 insertions(+) create mode 100644 tests/ui/range/range-negative-literal-unsigned-type.rs create mode 100644 tests/ui/range/range-negative-literal-unsigned-type.stderr diff --git a/tests/ui/range/range-negative-literal-unsigned-type.rs b/tests/ui/range/range-negative-literal-unsigned-type.rs new file mode 100644 index 000000000000..b6152abb340e --- /dev/null +++ b/tests/ui/range/range-negative-literal-unsigned-type.rs @@ -0,0 +1,21 @@ +// Regression tests for: https://github.com/rust-lang/rust/issues/136514 + +#![allow(unreachable_patterns)] +fn main() { + match 0u8 { + -1..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -0..=0 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -256..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -255..=2 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + 0..=-1 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + -2..=-1 => {} + //~^ ERROR the trait bound `u8: Neg` is not satisfied + //~| ERROR the trait bound `u8: Neg` is not satisfied + _ => {} + } +} diff --git a/tests/ui/range/range-negative-literal-unsigned-type.stderr b/tests/ui/range/range-negative-literal-unsigned-type.stderr new file mode 100644 index 000000000000..7ca14c3d7790 --- /dev/null +++ b/tests/ui/range/range-negative-literal-unsigned-type.stderr @@ -0,0 +1,122 @@ +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:6:9 + | +LL | -1..=2 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:8:9 + | +LL | -0..=0 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:10:9 + | +LL | -256..=2 => {} + | ^^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:12:9 + | +LL | -255..=2 => {} + | ^^^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:14:13 + | +LL | 0..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:16:9 + | +LL | -2..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error[E0277]: the trait bound `u8: Neg` is not satisfied + --> $DIR/range-negative-literal-unsigned-type.rs:16:14 + | +LL | -2..=-1 => {} + | ^^ the trait `Neg` is not implemented for `u8` + | + = help: the following other types implement trait `Neg`: + &f128 + &f16 + &f32 + &f64 + &i128 + &i16 + &i32 + &i64 + and 12 others + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0277`. From cd1c7736614dcde9fe19961c83d935adedf98662 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Jan 2026 14:45:35 +1100 Subject: [PATCH 222/319] Remove `lift_query_info`. It doesn't use `self`, which means it doesn't need to be part of the `QueryContext` trait; we can just call `extract` directly where necessary. --- compiler/rustc_query_impl/src/plumbing.rs | 12 +----------- compiler/rustc_query_system/src/query/job.rs | 9 +++------ compiler/rustc_query_system/src/query/mod.rs | 6 ++---- compiler/rustc_query_system/src/query/plumbing.rs | 10 +++++----- 4 files changed, 11 insertions(+), 26 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..da11623d77b7 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -104,13 +104,6 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { if complete { Ok(jobs) } else { Err(jobs) } } - fn lift_query_info( - self, - info: &QueryStackDeferred<'tcx>, - ) -> rustc_query_system::query::QueryStackFrameExtra { - info.extract() - } - // Interactions with on_disk_cache fn load_side_effect( self, @@ -174,10 +167,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { self.tcx.sess.dcx().emit_fatal(QueryOverflow { span: info.job.span, - note: QueryOverflowNote { - desc: self.lift_query_info(&info.query.info).description, - depth, - }, + note: QueryOverflowNote { desc: info.query.info.extract().description, depth }, suggested_limit, crate_name: self.tcx.crate_name(LOCAL_CRATE), }); diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 177bcd63cbc6..3affb23d3022 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -27,11 +27,8 @@ pub struct QueryInfo { } impl<'tcx> QueryInfo> { - pub(crate) fn lift>( - &self, - qcx: Qcx, - ) -> QueryInfo { - QueryInfo { span: self.span, query: self.query.lift(qcx) } + pub(crate) fn lift(&self) -> QueryInfo { + QueryInfo { span: self.span, query: self.query.lift() } } } @@ -628,7 +625,7 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( let Some(query_info) = query_map.get(&query) else { break; }; - let query_extra = qcx.lift_query_info(&query_info.query.info); + let query_extra = query_info.query.info.extract(); if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. dcx.struct_failure_note(format!( diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 63202429679d..e40dcf2daf35 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -70,9 +70,9 @@ impl<'tcx> QueryStackFrame> { Self { info, def_id, dep_kind, hash, def_id_for_ty_in_cycle } } - fn lift>(&self, qcx: Qcx) -> QueryStackFrame { + fn lift(&self) -> QueryStackFrame { QueryStackFrame { - info: qcx.lift_query_info(&self.info), + info: self.info.extract(), dep_kind: self.dep_kind, hash: self.hash, def_id: self.def_id, @@ -168,8 +168,6 @@ pub trait QueryContext<'tcx>: HasDepContext { fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>>; - fn lift_query_info(self, info: &QueryStackDeferred<'tcx>) -> QueryStackFrameExtra; - /// Load a side effect associated to the node in the previous session. fn load_side_effect( self, diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 98bbd3ebc4a0..04b62daa86b4 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -253,10 +253,10 @@ pub struct CycleError { } impl<'tcx> CycleError> { - fn lift>(&self, qcx: Qcx) -> CycleError { + fn lift(&self) -> CycleError { CycleError { - usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift(qcx))), - cycle: self.cycle.iter().map(|info| info.lift(qcx)).collect(), + usage: self.usage.as_ref().map(|(span, frame)| (*span, frame.lift())), + cycle: self.cycle.iter().map(|info| info.lift()).collect(), } } } @@ -297,7 +297,7 @@ where let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); - (mk_cycle(query, qcx, error.lift(qcx)), None) + (mk_cycle(query, qcx, error.lift()), None) } #[inline(always)] @@ -345,7 +345,7 @@ where (v, Some(index)) } - Err(cycle) => (mk_cycle(query, qcx, cycle.lift(qcx)), None), + Err(cycle) => (mk_cycle(query, qcx, cycle.lift()), None), } } From 4ff360e997eaf57005454eaa8dc6eff2b820d425 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Jan 2026 14:50:45 +1100 Subject: [PATCH 223/319] Rename some query-related things. Various `QueryStackFrame` variables are called `query`; `frame` is a better name. And various `QueryInfo` variables are called `frame`; `info` is a better name. This eliminates some confusing `query.query()` occurrences, which is a good sign, and some `frame.query` occurrences become `info.frame`. --- compiler/rustc_middle/src/values.rs | 34 +++++++------- compiler/rustc_query_impl/src/plumbing.rs | 6 +-- compiler/rustc_query_system/src/query/job.rs | 44 +++++++++---------- .../rustc_query_system/src/query/plumbing.rs | 10 ++--- 4 files changed, 47 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index bc73d36216ef..fd08b5a972c6 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -50,9 +50,9 @@ impl<'tcx> Value> for ty::Binder<'_, ty::FnSig<'_>> { ) -> Self { let err = Ty::new_error(tcx, guar); - let arity = if let Some(frame) = cycle_error.cycle.get(0) - && frame.query.dep_kind == dep_kinds::fn_sig - && let Some(def_id) = frame.query.def_id + let arity = if let Some(info) = cycle_error.cycle.get(0) + && info.frame.dep_kind == dep_kinds::fn_sig + && let Some(def_id) = info.frame.def_id && let Some(node) = tcx.hir_get_if_local(def_id) && let Some(sig) = node.fn_sig() { @@ -85,10 +85,10 @@ impl<'tcx> Value> for Representability { let mut item_and_field_ids = Vec::new(); let mut representable_ids = FxHashSet::default(); for info in &cycle_error.cycle { - if info.query.dep_kind == dep_kinds::representability - && let Some(field_id) = info.query.def_id + if info.frame.dep_kind == dep_kinds::representability + && let Some(field_id) = info.frame.def_id && let Some(field_id) = field_id.as_local() - && let Some(DefKind::Field) = info.query.info.def_kind + && let Some(DefKind::Field) = info.frame.info.def_kind { let parent_id = tcx.parent(field_id.to_def_id()); let item_id = match tcx.def_kind(parent_id) { @@ -99,8 +99,8 @@ impl<'tcx> Value> for Representability { } } for info in &cycle_error.cycle { - if info.query.dep_kind == dep_kinds::representability_adt_ty - && let Some(def_id) = info.query.def_id_for_ty_in_cycle + if info.frame.dep_kind == dep_kinds::representability_adt_ty + && let Some(def_id) = info.frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && !item_and_field_ids.iter().any(|&(id, _)| id == def_id) { @@ -141,9 +141,9 @@ impl<'tcx> Value> for &[ty::Variance] { search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if let Some(frame) = cycle.get(0) - && frame.query.dep_kind == dep_kinds::variances_of - && let Some(def_id) = frame.query.def_id + if let Some(info) = cycle.get(0) + && info.frame.dep_kind == dep_kinds::variances_of + && let Some(def_id) = info.frame.def_id { let n = tcx.generics_of(def_id).own_params.len(); ControlFlow::Break(vec![ty::Bivariant; n].leak()) @@ -189,8 +189,8 @@ impl<'tcx, T> Value> for Result> let diag = search_for_cycle_permutation( &cycle_error.cycle, |cycle| { - if cycle[0].query.dep_kind == dep_kinds::layout_of - && let Some(def_id) = cycle[0].query.def_id_for_ty_in_cycle + if cycle[0].frame.dep_kind == dep_kinds::layout_of + && let Some(def_id) = cycle[0].frame.def_id_for_ty_in_cycle && let Some(def_id) = def_id.as_local() && let def_kind = tcx.def_kind(def_id) && matches!(def_kind, DefKind::Closure) @@ -213,18 +213,18 @@ impl<'tcx, T> Value> for Result> tcx.def_kind_descr_article(def_kind, def_id.to_def_id()), tcx.def_kind_descr(def_kind, def_id.to_def_id()), ); - for (i, frame) in cycle.iter().enumerate() { - if frame.query.dep_kind != dep_kinds::layout_of { + for (i, info) in cycle.iter().enumerate() { + if info.frame.dep_kind != dep_kinds::layout_of { continue; } - let Some(frame_def_id) = frame.query.def_id_for_ty_in_cycle else { + let Some(frame_def_id) = info.frame.def_id_for_ty_in_cycle else { continue; }; let Some(frame_coroutine_kind) = tcx.coroutine_kind(frame_def_id) else { continue; }; let frame_span = - frame.query.info.default_span(cycle[(i + 1) % cycle.len()].span); + info.frame.info.default_span(cycle[(i + 1) % cycle.len()].span); if frame_span.is_dummy() { continue; } diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index da11623d77b7..b7ce8587695b 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -167,7 +167,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { self.tcx.sess.dcx().emit_fatal(QueryOverflow { span: info.job.span, - note: QueryOverflowNote { desc: info.query.info.extract().description, depth }, + note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, suggested_limit, crate_name: self.tcx.crate_name(LOCAL_CRATE), }); @@ -728,14 +728,14 @@ macro_rules! define_queries { qmap: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { - let make_query = |tcx, key| { + let make_frame = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) }; let res = tcx.query_system.states.$name.collect_active_jobs( tcx, - make_query, + make_frame, qmap, require_complete, ); diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 3affb23d3022..82b23b022c37 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -23,12 +23,12 @@ use crate::query::{QueryContext, QueryStackFrame}; pub struct QueryInfo { /// The span corresponding to the reason for which this query was required. pub span: Span, - pub query: QueryStackFrame, + pub frame: QueryStackFrame, } impl<'tcx> QueryInfo> { pub(crate) fn lift(&self) -> QueryInfo { - QueryInfo { span: self.span, query: self.query.lift() } + QueryInfo { span: self.span, frame: self.frame.lift() } } } @@ -39,8 +39,8 @@ pub type QueryMap<'tcx> = FxHashMap>; pub struct QueryJobId(pub NonZero); impl QueryJobId { - fn query<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { - map.get(&self).unwrap().query.clone() + fn frame<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> QueryStackFrame> { + map.get(&self).unwrap().frame.clone() } fn span<'a, 'tcx>(self, map: &'a QueryMap<'tcx>) -> Span { @@ -58,7 +58,7 @@ impl QueryJobId { #[derive(Clone, Debug)] pub struct QueryJobInfo<'tcx> { - pub query: QueryStackFrame>, + pub frame: QueryStackFrame>, pub job: QueryJob<'tcx>, } @@ -122,7 +122,7 @@ impl QueryJobId { while let Some(job) = current_job { let info = query_map.get(&job).unwrap(); - cycle.push(QueryInfo { span: info.job.span, query: info.query.clone() }); + cycle.push(QueryInfo { span: info.job.span, frame: info.frame.clone() }); if job == *self { cycle.reverse(); @@ -137,7 +137,7 @@ impl QueryJobId { .job .parent .as_ref() - .map(|parent| (info.job.span, parent.query(&query_map))); + .map(|parent| (info.job.span, parent.frame(&query_map))); return CycleError { usage, cycle }; } @@ -155,13 +155,13 @@ impl QueryJobId { ) -> (QueryJobInfo<'tcx>, usize) { let mut depth = 1; let info = query_map.get(&self).unwrap(); - let dep_kind = info.query.dep_kind; + let dep_kind = info.frame.dep_kind; let mut current_id = info.job.parent; let mut last_layout = (info.clone(), depth); while let Some(id) = current_id { let info = query_map.get(&id).unwrap(); - if info.query.dep_kind == dep_kind { + if info.frame.dep_kind == dep_kind { depth += 1; last_layout = (info.clone(), depth); } @@ -386,7 +386,7 @@ where .iter() .min_by_key(|v| { let (span, query) = f(v); - let hash = query.query(query_map).hash; + let hash = query.frame(query_map).hash; // Prefer entry points which have valid spans for nicer error messages // We add an integer to the tuple ensuring that entry points // with valid spans are picked first @@ -470,14 +470,14 @@ fn remove_cycle<'tcx>( stack.rotate_left(pos); } - let usage = usage.as_ref().map(|(span, query)| (*span, query.query(query_map))); + let usage = usage.as_ref().map(|(span, query)| (*span, query.frame(query_map))); // Create the cycle error let error = CycleError { usage, cycle: stack .iter() - .map(|&(s, ref q)| QueryInfo { span: s, query: q.query(query_map) }) + .map(|&(s, ref q)| QueryInfo { span: s, frame: q.frame(query_map) }) .collect(), }; @@ -556,7 +556,7 @@ pub fn report_cycle<'a>( ) -> Diag<'a> { assert!(!stack.is_empty()); - let span = stack[0].query.info.default_span(stack[1 % stack.len()].span); + let span = stack[0].frame.info.default_span(stack[1 % stack.len()].span); let mut cycle_stack = Vec::new(); @@ -564,9 +564,9 @@ pub fn report_cycle<'a>( let stack_count = if stack.len() == 1 { StackCount::Single } else { StackCount::Multiple }; for i in 1..stack.len() { - let query = &stack[i].query; - let span = query.info.default_span(stack[(i + 1) % stack.len()].span); - cycle_stack.push(CycleStack { span, desc: query.info.description.to_owned() }); + let frame = &stack[i].frame; + let span = frame.info.default_span(stack[(i + 1) % stack.len()].span); + cycle_stack.push(CycleStack { span, desc: frame.info.description.to_owned() }); } let mut cycle_usage = None; @@ -578,9 +578,9 @@ pub fn report_cycle<'a>( } let alias = - if stack.iter().all(|entry| matches!(entry.query.info.def_kind, Some(DefKind::TyAlias))) { + if stack.iter().all(|entry| matches!(entry.frame.info.def_kind, Some(DefKind::TyAlias))) { Some(crate::error::Alias::Ty) - } else if stack.iter().all(|entry| entry.query.info.def_kind == Some(DefKind::TraitAlias)) { + } else if stack.iter().all(|entry| entry.frame.info.def_kind == Some(DefKind::TraitAlias)) { Some(crate::error::Alias::Trait) } else { None @@ -589,7 +589,7 @@ pub fn report_cycle<'a>( let cycle_diag = crate::error::Cycle { span, cycle_stack, - stack_bottom: stack[0].query.info.description.to_owned(), + stack_bottom: stack[0].frame.info.description.to_owned(), alias, cycle_usage, stack_count, @@ -625,12 +625,12 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( let Some(query_info) = query_map.get(&query) else { break; }; - let query_extra = query_info.query.info.extract(); + let query_extra = query_info.frame.info.extract(); if Some(count_printed) < limit_frames || limit_frames.is_none() { // Only print to stderr as many stack frames as `num_frames` when present. dcx.struct_failure_note(format!( "#{} [{:?}] {}", - count_printed, query_info.query.dep_kind, query_extra.description + count_printed, query_info.frame.dep_kind, query_extra.description )) .with_span(query_info.job.span) .emit(); @@ -642,7 +642,7 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( file, "#{} [{}] {}", count_total, - qcx.dep_context().dep_kind_vtable(query_info.query.dep_kind).name, + qcx.dep_context().dep_kind_vtable(query_info.frame.dep_kind).name, query_extra.description ); } diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 04b62daa86b4..25f96315cd13 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -82,7 +82,7 @@ where pub fn collect_active_jobs( &self, qcx: Qcx, - make_query: fn(Qcx, K) -> QueryStackFrame>, + make_frame: fn(Qcx, K) -> QueryStackFrame>, jobs: &mut QueryMap<'tcx>, require_complete: bool, ) -> Option<()> { @@ -108,11 +108,11 @@ where } } - // Call `make_query` while we're not holding a `self.active` lock as `make_query` may call + // Call `make_frame` while we're not holding a `self.active` lock as `make_frame` may call // queries leading to a deadlock. for (key, job) in active { - let query = make_query(qcx, key); - jobs.insert(job.id, QueryJobInfo { query, job }); + let frame = make_frame(qcx, key); + jobs.insert(job.id, QueryJobInfo { frame, job }); } Some(()) @@ -170,7 +170,7 @@ where } CycleErrorHandling::Stash => { let guar = if let Some(root) = cycle_error.cycle.first() - && let Some(span) = root.query.info.span + && let Some(span) = root.frame.info.span { error.stash(span, StashKey::Cycle).unwrap() } else { From 8e2c9c69c6279c0a63833163d622e4a55df07b3f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 30 Jan 2026 15:14:41 +1100 Subject: [PATCH 224/319] Eliminate some `'a` lifetimes. Putting `+ 'tcx` on the `QueryDispatcher` trait lets a few other places be simplified. --- compiler/rustc_query_impl/src/lib.rs | 11 ++++------- compiler/rustc_query_system/src/query/dispatcher.rs | 6 +++--- compiler/rustc_query_system/src/query/plumbing.rs | 10 +++++----- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 4e79d0842da2..6e119bf92e2d 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -86,10 +86,7 @@ where } #[inline(always)] - fn query_state<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a QueryState<'tcx, Self::Key> - where - QueryCtxt<'tcx>: 'a, - { + fn query_state(self, qcx: QueryCtxt<'tcx>) -> &'tcx QueryState<'tcx, Self::Key> { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { @@ -100,7 +97,7 @@ where } #[inline(always)] - fn query_cache<'a>(self, qcx: QueryCtxt<'tcx>) -> &'a Self::Cache { + fn query_cache(self, qcx: QueryCtxt<'tcx>) -> &'tcx Self::Cache { // Safety: // This is just manually doing the subfield referencing through pointer math. unsafe { @@ -216,12 +213,12 @@ trait QueryDispatcherUnerased<'tcx> { ) -> Self::UnerasedValue; } -pub fn query_system<'a>( +pub fn query_system<'tcx>( local_providers: Providers, extern_providers: ExternProviders, on_disk_cache: Option, incremental: bool, -) -> QuerySystem<'a> { +) -> QuerySystem<'tcx> { QuerySystem { states: Default::default(), arenas: Default::default(), diff --git a/compiler/rustc_query_system/src/query/dispatcher.rs b/compiler/rustc_query_system/src/query/dispatcher.rs index ac6c38dd7db5..bcd3d0322d07 100644 --- a/compiler/rustc_query_system/src/query/dispatcher.rs +++ b/compiler/rustc_query_system/src/query/dispatcher.rs @@ -25,7 +25,7 @@ type DepContextOf<'tcx, This: QueryDispatcher<'tcx>> = /// Those types are not visible from this `rustc_query_system` crate. /// /// "Dispatcher" should be understood as a near-synonym of "vtable". -pub trait QueryDispatcher<'tcx>: Copy { +pub trait QueryDispatcher<'tcx>: Copy + 'tcx { fn name(self) -> &'static str; /// Query context used by this dispatcher, i.e. `rustc_query_impl::QueryCtxt`. @@ -41,10 +41,10 @@ pub trait QueryDispatcher<'tcx>: Copy { fn format_value(self) -> fn(&Self::Value) -> String; // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_state<'a>(self, tcx: Self::Qcx) -> &'a QueryState<'tcx, Self::Key>; + fn query_state(self, tcx: Self::Qcx) -> &'tcx QueryState<'tcx, Self::Key>; // Don't use this method to access query results, instead use the methods on TyCtxt - fn query_cache<'a>(self, tcx: Self::Qcx) -> &'a Self::Cache; + fn query_cache(self, tcx: Self::Qcx) -> &'tcx Self::Cache; fn will_cache_on_disk_for_key(self, tcx: DepContextOf<'tcx, Self>, key: &Self::Key) -> bool; diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 25f96315cd13..472839116107 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -127,11 +127,11 @@ impl<'tcx, K> Default for QueryState<'tcx, K> { /// A type representing the responsibility to execute the job in the `job` field. /// This will poison the relevant query if dropped. -struct JobOwner<'a, 'tcx, K> +struct JobOwner<'tcx, K> where K: Eq + Hash + Copy, { - state: &'a QueryState<'tcx, K>, + state: &'tcx QueryState<'tcx, K>, key: K, } @@ -181,7 +181,7 @@ where } } -impl<'a, 'tcx, K> JobOwner<'a, 'tcx, K> +impl<'tcx, K> JobOwner<'tcx, K> where K: Eq + Hash + Copy, { @@ -218,7 +218,7 @@ where } } -impl<'a, 'tcx, K> Drop for JobOwner<'a, 'tcx, K> +impl<'tcx, K> Drop for JobOwner<'tcx, K> where K: Eq + Hash + Copy, { @@ -422,7 +422,7 @@ where fn execute_job<'tcx, Q, const INCR: bool>( query: Q, qcx: Q::Qcx, - state: &QueryState<'tcx, Q::Key>, + state: &'tcx QueryState<'tcx, Q::Key>, key: Q::Key, key_hash: u64, id: QueryJobId, From c6afd45ac16e0a665ed6feb2d45cf2a4e2769643 Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Mon, 2 Feb 2026 10:41:03 +1100 Subject: [PATCH 225/319] Move `depth_limit_error` out of `QueryContext` trait. It's defined and used in `rustc_query_impl`; `rustc_query_system` doesn't need it. So it can just be an inherent method on `QueryCtxt`. --- compiler/rustc_query_impl/src/plumbing.rs | 34 ++++++++++---------- compiler/rustc_query_system/src/query/mod.rs | 2 -- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index b7ce8587695b..0002e5950a33 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -48,6 +48,23 @@ impl<'tcx> QueryCtxt<'tcx> { pub fn new(tcx: TyCtxt<'tcx>) -> Self { QueryCtxt { tcx } } + + fn depth_limit_error(self, job: QueryJobId) { + let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); + let (info, depth) = job.find_dep_kind_root(query_map); + + let suggested_limit = match self.tcx.recursion_limit() { + Limit(0) => Limit(2), + limit => limit * 2, + }; + + self.tcx.sess.dcx().emit_fatal(QueryOverflow { + span: info.job.span, + note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, + suggested_limit, + crate_name: self.tcx.crate_name(LOCAL_CRATE), + }); + } } impl<'tcx> HasDepContext for QueryCtxt<'tcx> { @@ -155,23 +172,6 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { tls::enter_context(&new_icx, compute) }) } - - fn depth_limit_error(self, job: QueryJobId) { - let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); - let (info, depth) = job.find_dep_kind_root(query_map); - - let suggested_limit = match self.tcx.recursion_limit() { - Limit(0) => Limit(2), - limit => limit * 2, - }; - - self.tcx.sess.dcx().emit_fatal(QueryOverflow { - span: info.job.span, - note: QueryOverflowNote { desc: info.frame.info.extract().description, depth }, - suggested_limit, - crate_name: self.tcx.crate_name(LOCAL_CRATE), - }); - } } pub(super) fn try_mark_green<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &dep_graph::DepNode) -> bool { diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index e40dcf2daf35..54e5fa4d7229 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -181,6 +181,4 @@ pub trait QueryContext<'tcx>: HasDepContext { /// new query job while it executes. fn start_query(self, token: QueryJobId, depth_limit: bool, compute: impl FnOnce() -> R) -> R; - - fn depth_limit_error(self, job: QueryJobId); } From f3b7a1a9d8e0beb8998f5b621b26d4942c423123 Mon Sep 17 00:00:00 2001 From: Carl Lundin Date: Sun, 1 Feb 2026 17:14:41 -0800 Subject: [PATCH 226/319] Update documentation for `Result::ok()` The term of "discard" is misleading. An error is not discarded but converted to an `Option::None`. --- library/core/src/result.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 52f3d43dfd6d..5f438d72ac13 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -690,7 +690,7 @@ impl Result { /// Converts from `Result` to [`Option`]. /// /// Converts `self` into an [`Option`], consuming `self`, - /// and discarding the error, if any. + /// and converting the error to `None`, if any. /// /// # Examples /// From c64f9a0fc46fecb8ccb15d1ab1fcfd83ce94b5e5 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 07:38:14 +0530 Subject: [PATCH 227/319] Add backlink to issue --- tests/codegen-llvm/slp-vectorization-mul3.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index bb4965c46bc0..b6edc212c82d 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,3 +1,4 @@ +//! Regression test for #142519 //@ only-x86_64 //@ compile-flags: -O From 0cbbe56d84ef44715e4e12496d904848c0863968 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 2 Feb 2026 13:03:10 +1100 Subject: [PATCH 228/319] Work around rustfmt giving up on a large expression --- compiler/rustc_interface/src/util.rs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index b3889849430a..249368fd1194 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -231,7 +231,12 @@ pub(crate) fn run_in_thread_pool_with_globals< .name("rustc query cycle handler".to_string()) .spawn(move || { let on_panic = defer(|| { - eprintln!("internal compiler error: query cycle handler thread panicked, aborting process"); + // Split this long string so that it doesn't cause rustfmt to + // give up on the entire builder expression. + // + const MESSAGE: &str = "\ +internal compiler error: query cycle handler thread panicked, aborting process"; + eprintln!("{MESSAGE}"); // We need to abort here as we failed to resolve the deadlock, // otherwise the compiler could just hang, process::abort(); @@ -244,11 +249,16 @@ pub(crate) fn run_in_thread_pool_with_globals< tls::with(|tcx| { // Accessing session globals is sound as they outlive `GlobalCtxt`. // They are needed to hash query keys containing spans or symbols. - let query_map = rustc_span::set_session_globals_then(unsafe { &*(session_globals as *const SessionGlobals) }, || { - // Ensure there was no errors collecting all active jobs. - // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs(false).expect("failed to collect active queries in deadlock handler") - }); + let query_map = rustc_span::set_session_globals_then( + unsafe { &*(session_globals as *const SessionGlobals) }, + || { + // Ensure there were no errors collecting all active jobs. + // We need the complete map to ensure we find a cycle to break. + QueryCtxt::new(tcx).collect_active_jobs(false).expect( + "failed to collect active queries in deadlock handler", + ) + }, + ); break_query_cycles(query_map, ®istry); }) }) From 59868c13947753220a6727e50c2c596d2f513b29 Mon Sep 17 00:00:00 2001 From: Nicholas Bishop Date: Fri, 23 Jan 2026 13:43:20 -0500 Subject: [PATCH 229/319] Fix uninitialized UEFI globals in tests Export globals via a `doc(hidden)` module. In test code, use the globals from `realstd` so that they are properly initialized. --- library/std/src/lib.rs | 1 + library/std/src/os/uefi/env.rs | 24 ++++++++++++++++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index b213fa749177..dcde208fac77 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -260,6 +260,7 @@ all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) )] +#![cfg_attr(all(test, target_os = "uefi"), feature(uefi_std))] #![cfg_attr(target_family = "wasm", feature(stdarch_wasm_atomic_wait))] #![cfg_attr(target_arch = "wasm64", feature(simd_wasm64))] // diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index ab5406e605c6..82e3fc9775cb 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -4,13 +4,25 @@ use crate::ffi::c_void; use crate::ptr::NonNull; -use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::Ordering; -static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); -// Flag to check if BootServices are still valid. -// Start with assuming that they are not available -static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +#[doc(hidden)] +#[cfg(not(test))] +pub mod globals { + use crate::ffi::c_void; + use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; + + pub static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + pub static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); + // Flag to check if BootServices are still valid. + // Start with assuming that they are not available + pub static BOOT_SERVICES_FLAG: Atomic = AtomicBool::new(false); +} + +#[cfg(not(test))] +use globals::*; +#[cfg(test)] +use realstd::os::uefi::env::globals::*; /// Initializes the global System Table and Image Handle pointers. /// From 09de0fd848251aed26149bd5938b1e05ef35dc82 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Fri, 30 Jan 2026 12:41:04 +1100 Subject: [PATCH 230/319] Use `#![feature(adt_const_params)]` for static query flags --- compiler/rustc_query_impl/src/lib.rs | 40 ++++++++++++++--------- compiler/rustc_query_impl/src/plumbing.rs | 10 ++++-- 2 files changed, 31 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index 4e79d0842da2..8dd220e1ae0f 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -2,10 +2,13 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![feature(adt_const_params)] #![feature(min_specialization)] #![feature(rustc_attrs)] // tidy-alphabetical-end +use std::marker::ConstParamTy; + use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; @@ -35,29 +38,34 @@ pub use crate::plumbing::{QueryCtxt, query_key_hash_verify_all}; mod profiling_support; pub use self::profiling_support::alloc_self_profile_query_strings; +#[derive(ConstParamTy)] // Allow this struct to be used for const-generic values. +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct QueryFlags { + /// True if this query has the `anon` modifier. + is_anon: bool, + /// True if this query has the `depth_limit` modifier. + is_depth_limit: bool, + /// True if this query has the `feedable` modifier. + is_feedable: bool, +} + /// Combines a [`QueryVTable`] with some additional compile-time booleans /// to implement [`QueryDispatcher`], for use by code in [`rustc_query_system`]. /// /// Baking these boolean flags into the type gives a modest but measurable /// improvement to compiler perf and compiler code size; see /// . -struct SemiDynamicQueryDispatcher< - 'tcx, - C: QueryCache, - const ANON: bool, - const DEPTH_LIMIT: bool, - const FEEDABLE: bool, -> { +struct SemiDynamicQueryDispatcher<'tcx, C: QueryCache, const FLAGS: QueryFlags> { vtable: &'tcx QueryVTable<'tcx, C>, } // Manually implement Copy/Clone, because deriving would put trait bounds on the cache type. -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Copy - for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Copy + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { } -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> Clone - for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> Clone + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> { fn clone(&self) -> Self { *self @@ -65,8 +73,8 @@ impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDA } // This is `impl QueryDispatcher for SemiDynamicQueryDispatcher`. -impl<'tcx, C: QueryCache, const ANON: bool, const DEPTH_LIMIT: bool, const FEEDABLE: bool> - QueryDispatcher<'tcx> for SemiDynamicQueryDispatcher<'tcx, C, ANON, DEPTH_LIMIT, FEEDABLE> +impl<'tcx, C: QueryCache, const FLAGS: QueryFlags> QueryDispatcher<'tcx> + for SemiDynamicQueryDispatcher<'tcx, C, FLAGS> where for<'a> C::Key: HashStable>, { @@ -158,7 +166,7 @@ where #[inline(always)] fn anon(self) -> bool { - ANON + FLAGS.is_anon } #[inline(always)] @@ -168,12 +176,12 @@ where #[inline(always)] fn depth_limit(self) -> bool { - DEPTH_LIMIT + FLAGS.is_depth_limit } #[inline(always)] fn feedable(self) -> bool { - FEEDABLE + FLAGS.is_feedable } #[inline(always)] diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 2d4e10a0380c..48aed0bad4b8 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -708,14 +708,18 @@ macro_rules! define_queries { data: PhantomData<&'tcx ()> } + const FLAGS: QueryFlags = QueryFlags { + is_anon: is_anon!([$($modifiers)*]), + is_depth_limit: depth_limit!([$($modifiers)*]), + is_feedable: feedable!([$($modifiers)*]), + }; + impl<'tcx> QueryDispatcherUnerased<'tcx> for QueryType<'tcx> { type UnerasedValue = queries::$name::Value<'tcx>; type Dispatcher = SemiDynamicQueryDispatcher< 'tcx, queries::$name::Storage<'tcx>, - { is_anon!([$($modifiers)*]) }, - { depth_limit!([$($modifiers)*]) }, - { feedable!([$($modifiers)*]) }, + FLAGS, >; const NAME: &'static &'static str = &stringify!($name); From 95ac5673cedf8b0ba543aac2ccb67b47f60662e1 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 15:38:26 +0530 Subject: [PATCH 231/319] Fix SLP vectorization test CHECK patterns --- tests/codegen-llvm/slp-vectorization-mul3.rs | 29 ++++++++++---------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index b6edc212c82d..1c40c6b28f3f 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -4,23 +4,22 @@ #![crate_type = "lib"] -// CHECK-LABEL: mul3 -// CHECK: %[[C_BPP:.*]] = phi <4 x i8> -// CHECK: %[[V:.*]] = load <4 x i8>, ptr -// CHECK: %[[ADD:.*]] = add <4 x i8> %[[V]], %[[C_BPP]] -// CHECK: store {{<4 x i8>|i32}} {{.*}}, ptr +// CHECK-LABEL: @mul3 +// CHECK: phi <4 x i8> +// CHECK: load <4 x i8> +// CHECK: add <4 x i8> +// CHECK: store <4 x i8> -pub fn mul3(previous: &[u8], current: &mut [u8]) { +#[no_mangle] +pub fn mul3(previous: &[[u8; 4]], current: &mut [[u8; 4]]) { let mut c_bpp = [0u8; 4]; - for (chunk, b_bpp) in current.chunks_exact_mut(4).zip(previous.chunks_exact(4)) { - let new_chunk = [ - chunk[0].wrapping_add(c_bpp[0]), - chunk[1].wrapping_add(c_bpp[1]), - chunk[2].wrapping_add(c_bpp[2]), - chunk[3].wrapping_add(c_bpp[3]), - ]; - chunk.copy_from_slice(&new_chunk); - c_bpp.copy_from_slice(b_bpp); + for i in 0..previous.len() { + current[i][0] = current[i][0].wrapping_add(c_bpp[0]); + current[i][1] = current[i][1].wrapping_add(c_bpp[1]); + current[i][2] = current[i][2].wrapping_add(c_bpp[2]); + current[i][3] = current[i][3].wrapping_add(c_bpp[3]); + + c_bpp = previous[i]; } } From 0830a5a9286e351a009e8eaa1fa86691e3a3b62e Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 15:44:50 +0530 Subject: [PATCH 232/319] fix: add min-llvm-version --- tests/codegen-llvm/slp-vectorization-mul3.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index 1c40c6b28f3f..2987493e0371 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,6 +1,8 @@ //! Regression test for #142519 //@ only-x86_64 //@ compile-flags: -O +//@ min-llvm-version: 18.0 + #![crate_type = "lib"] From c725637dc24fbe47704e92b7e8a8875f60ccd2be Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 2 Feb 2026 11:22:33 +0100 Subject: [PATCH 233/319] explain why we dont skip some of this work when there are field projections --- compiler/rustc_middle/src/mir/statement.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/compiler/rustc_middle/src/mir/statement.rs b/compiler/rustc_middle/src/mir/statement.rs index 393e9c59c355..973ceccc67f9 100644 --- a/compiler/rustc_middle/src/mir/statement.rs +++ b/compiler/rustc_middle/src/mir/statement.rs @@ -441,6 +441,8 @@ impl<'tcx> Place<'tcx> { where D: ?Sized + HasLocalDecls<'tcx>, { + // If there's a field projection element in `projection`, we *could* skip everything + // before that, but on 2026-01-31 a perf experiment showed no benefit from doing so. PlaceTy::from_ty(local_decls.local_decls()[local].ty).multi_projection_ty(tcx, projection) } From 4956604c7db5fb272214331c9db82608fbbbe185 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Sat, 31 Jan 2026 21:34:36 +0200 Subject: [PATCH 234/319] external-rustc-drivers.md: some improvements --- .../rustc-driver/external-rustc-drivers.md | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md index 1049d7a82ddd..2d409ad201b1 100644 --- a/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md +++ b/src/doc/rustc-dev-guide/src/rustc-driver/external-rustc-drivers.md @@ -42,29 +42,28 @@ For custom-built toolchains or environments not using rustup, additional configu #### Troubleshooting Steps -1. **Check LLVM installation**: Verify LLVM is installed and accessible -2. **Configure library paths**: You may need to set environment variables: - ```text +1. Verify LLVM is installed and accessible +2. Ensure that library paths are set: + ```sh export LD_LIBRARY_PATH=/path/to/llvm/lib:$LD_LIBRARY_PATH ``` -3. **Check version compatibility**: Ensure your LLVM version is compatible with your Rust toolchain +3. Ensure your LLVM version is compatible with your Rust toolchain -### Configuring `rust-analyzer` for Out-of-Tree Projects +### Configuring `rust-analyzer` for out-of-tree projects When developing out-of-tree projects that use `rustc_private` crates, you can configure `rust-analyzer` to recognize these crates. #### Configuration Steps -1. **Set rust-analyzer configuration** - Configure `rust-analyzer.rustc.source` to `"discover"` in your editor settings. +1. Configure `rust-analyzer.rustc.source` to `"discover"` in your editor settings. For VS Code, add to `rust_analyzer_settings.json`: ```json { "rust-analyzer.rustc.source": "discover" } ``` -2. **Add metadata to Cargo.toml** - Add the following to the `Cargo.toml` of every crate that uses `rustc_private`: + +2. Add the following to the `Cargo.toml` of every crate that uses `rustc_private`: ```toml [package.metadata.rust-analyzer] rustc_private = true @@ -74,4 +73,6 @@ This configuration allows `rust-analyzer` to properly recognize and provide IDE ### Additional Resources -- [GitHub Issue #137421](https://github.com/rust-lang/rust/issues/137421): Explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed +- [GitHub Issue #137421] explains that `rustc_private` linker failures often occur because `llvm-tools` is not installed + +[GitHub Issue #137421]: https://github.com/rust-lang/rust/issues/137421 From 61769452234deaf0e035789009ab807dd1ca93d4 Mon Sep 17 00:00:00 2001 From: ritik chahar Date: Mon, 2 Feb 2026 16:05:08 +0530 Subject: [PATCH 235/319] fix: remove space for tidy and only for x86_64 --- tests/codegen-llvm/slp-vectorization-mul3.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index 2987493e0371..d7c44c8db71a 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -3,7 +3,6 @@ //@ compile-flags: -O //@ min-llvm-version: 18.0 - #![crate_type = "lib"] // CHECK-LABEL: @mul3 From 5aba6b1635935237a146d759e67bc7d3e8bdc73b Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Mon, 2 Feb 2026 09:38:07 +0000 Subject: [PATCH 236/319] Fix missing unused_variables lint when using a match guard Within a binding pattern match guard, only real reads of a bound local impact its liveness analysis - not the fake read that is injected. --- compiler/rustc_mir_transform/src/liveness.rs | 9 ++++++--- .../clippy/clippy_lints/src/time_subtraction.rs | 2 +- tests/ui/lint/unused/match_with_guard.rs | 10 ++++++++++ tests/ui/lint/unused/match_with_guard.stderr | 15 +++++++++++++++ 4 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 tests/ui/lint/unused/match_with_guard.rs create mode 100644 tests/ui/lint/unused/match_with_guard.stderr diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index cf977be4c3df..9bafee9f31c3 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1243,9 +1243,12 @@ struct TransferFunction<'a, 'tcx> { impl<'tcx> Visitor<'tcx> for TransferFunction<'_, 'tcx> { fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) { match statement.kind { - // `ForLet(None)` fake read erroneously marks the just-assigned local as live. - // This defeats the purpose of the analysis for `let` bindings. - StatementKind::FakeRead(box (FakeReadCause::ForLet(None), _)) => return, + // `ForLet(None)` and `ForGuardBinding` fake reads erroneously mark the just-assigned + // locals as live. This defeats the purpose of the analysis for such bindings. + StatementKind::FakeRead(box ( + FakeReadCause::ForLet(None) | FakeReadCause::ForGuardBinding, + _, + )) => return, // Handle self-assignment by restricting the read/write they do. StatementKind::Assign(box (ref dest, ref rvalue)) if self.self_assignment.contains(&location) => diff --git a/src/tools/clippy/clippy_lints/src/time_subtraction.rs b/src/tools/clippy/clippy_lints/src/time_subtraction.rs index 3ba59aefea06..ca8378ba7c6a 100644 --- a/src/tools/clippy/clippy_lints/src/time_subtraction.rs +++ b/src/tools/clippy/clippy_lints/src/time_subtraction.rs @@ -85,7 +85,7 @@ impl LateLintPass<'_> for UncheckedTimeSubtraction { fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) { let (lhs, rhs) = match expr.kind { ExprKind::Binary(op, lhs, rhs) if matches!(op.node, BinOpKind::Sub,) => (lhs, rhs), - ExprKind::MethodCall(fn_name, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { + ExprKind::MethodCall(_, lhs, [rhs], _) if cx.ty_based_def(expr).is_diag_item(cx, sym::sub) => { (lhs, rhs) }, _ => return, diff --git a/tests/ui/lint/unused/match_with_guard.rs b/tests/ui/lint/unused/match_with_guard.rs new file mode 100644 index 000000000000..61e4321f6735 --- /dev/null +++ b/tests/ui/lint/unused/match_with_guard.rs @@ -0,0 +1,10 @@ +//! The mere presence of a match guard should not deem bound variables "used". +//! Regression test for https://github.com/rust-lang/rust/issues/151983 +//@ check-pass +#![warn(unused)] +fn main() { + match Some(42) { + Some(unused) if true => (), //~WARN unused variable: `unused` + _ => (), + } +} diff --git a/tests/ui/lint/unused/match_with_guard.stderr b/tests/ui/lint/unused/match_with_guard.stderr new file mode 100644 index 000000000000..6a509e568b68 --- /dev/null +++ b/tests/ui/lint/unused/match_with_guard.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `unused` + --> $DIR/match_with_guard.rs:7:14 + | +LL | Some(unused) if true => (), + | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` + | +note: the lint level is defined here + --> $DIR/match_with_guard.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + From 8476e893e72e1e3617fb50a06a01548d6177d192 Mon Sep 17 00:00:00 2001 From: Ritik Chahar <126174435+chahar-ritik@users.noreply.github.com> Date: Mon, 2 Feb 2026 16:47:09 +0530 Subject: [PATCH 237/319] Update min-llvm-version: 22 Co-authored-by: Nikita Popov --- tests/codegen-llvm/slp-vectorization-mul3.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/codegen-llvm/slp-vectorization-mul3.rs b/tests/codegen-llvm/slp-vectorization-mul3.rs index d7c44c8db71a..38c0949b8271 100644 --- a/tests/codegen-llvm/slp-vectorization-mul3.rs +++ b/tests/codegen-llvm/slp-vectorization-mul3.rs @@ -1,7 +1,7 @@ //! Regression test for #142519 //@ only-x86_64 //@ compile-flags: -O -//@ min-llvm-version: 18.0 +//@ min-llvm-version: 22 #![crate_type = "lib"] From c0393cf8dbfc91f4719e6be9895b5c8a983ef8ec Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Fri, 24 Oct 2025 15:29:33 +0300 Subject: [PATCH 238/319] resolve: Report more early resolution ambiguities for imports The new ambiguities are reported when the import's visibility is ambiguous and may depend on the resolution/expansion order. --- compiler/rustc_lint_defs/src/builtin.rs | 50 +++++++++++++++++++ compiler/rustc_middle/src/ty/mod.rs | 6 +++ compiler/rustc_resolve/src/diagnostics.rs | 17 +++++-- compiler/rustc_resolve/src/errors.rs | 9 +++- compiler/rustc_resolve/src/ident.rs | 34 ++++++++++--- compiler/rustc_resolve/src/imports.rs | 6 ++- compiler/rustc_resolve/src/lib.rs | 4 ++ .../ambiguous-import-visibility-macro.rs | 19 +++++++ .../ambiguous-import-visibility-macro.stderr | 29 +++++++++++ .../ambiguous-import-visibility-module.rs | 24 +++++++++ .../ambiguous-import-visibility-module.stderr | 29 +++++++++++ .../ui/imports/ambiguous-import-visibility.rs | 14 ++++++ .../ambiguous-import-visibility.stderr | 25 ++++++++++ 13 files changed, 254 insertions(+), 12 deletions(-) create mode 100644 tests/ui/imports/ambiguous-import-visibility-macro.rs create mode 100644 tests/ui/imports/ambiguous-import-visibility-macro.stderr create mode 100644 tests/ui/imports/ambiguous-import-visibility-module.rs create mode 100644 tests/ui/imports/ambiguous-import-visibility-module.stderr create mode 100644 tests/ui/imports/ambiguous-import-visibility.rs create mode 100644 tests/ui/imports/ambiguous-import-visibility.stderr diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index ff108031badc..d1d5d0a56ead 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -20,6 +20,7 @@ declare_lint_pass! { AMBIGUOUS_GLOB_IMPORTED_TRAITS, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_GLOB_REEXPORTS, + AMBIGUOUS_IMPORT_VISIBILITIES, AMBIGUOUS_PANIC_IMPORTS, ARITHMETIC_OVERFLOW, ASM_SUB_REGISTER, @@ -4564,6 +4565,55 @@ declare_lint! { }; } +declare_lint! { + /// The `ambiguous_import_visibilities` lint detects imports that should report ambiguity + /// errors, but previously didn't do that due to rustc bugs. + /// + /// ### Example + /// + /// ```rust,compile_fail + /// #![deny(unknown_lints)] + /// #![deny(ambiguous_import_visibilities)] + /// mod reexport { + /// mod m { + /// pub struct S {} + /// } + /// + /// macro_rules! mac { + /// () => { use m::S; } + /// } + /// + /// pub use m::*; + /// mac!(); + /// + /// pub use S as Z; // ambiguous visibility + /// } + /// + /// fn main() { + /// reexport::Z {}; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// Previous versions of Rust compile it successfully because it + /// fetched the glob import's visibility for `pub use S as Z` import, and ignored the private + /// `use m::S` import that appeared later. + /// + /// This is a [future-incompatible] lint to transition this to a + /// hard error in the future. + /// + /// [future-incompatible]: ../index.md#future-incompatible-lints + pub AMBIGUOUS_IMPORT_VISIBILITIES, + Warn, + "detects certain glob imports that require reporting an ambiguity error", + @future_incompatible = FutureIncompatibleInfo { + reason: fcw!(FutureReleaseError #149145), + }; +} + declare_lint! { /// The `refining_impl_trait_reachable` lint detects `impl Trait` return /// types in method signatures that are refined by a publically reachable diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 4e33b8ebb6de..0220531b09fa 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -408,6 +408,12 @@ impl> Visibility { } } +impl + Copy> Visibility { + pub fn min(self, vis: Visibility, tcx: TyCtxt<'_>) -> Visibility { + if self.is_at_least(vis, tcx) { vis } else { self } + } +} + impl Visibility { pub fn expect_local(self) -> Visibility { self.map_id(|id| id.expect_local()) diff --git a/compiler/rustc_resolve/src/diagnostics.rs b/compiler/rustc_resolve/src/diagnostics.rs index 6884ed1891a3..f09b98715799 100644 --- a/compiler/rustc_resolve/src/diagnostics.rs +++ b/compiler/rustc_resolve/src/diagnostics.rs @@ -24,8 +24,8 @@ use rustc_middle::ty::TyCtxt; use rustc_session::Session; use rustc_session::lint::BuiltinLintDiag; use rustc_session::lint::builtin::{ - ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_PANIC_IMPORTS, - MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, + ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, AMBIGUOUS_GLOB_IMPORTS, AMBIGUOUS_IMPORT_VISIBILITIES, + AMBIGUOUS_PANIC_IMPORTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, }; use rustc_session::utils::was_invoked_from_cargo; use rustc_span::edit_distance::find_best_match_for_name; @@ -145,6 +145,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { }; let lint = match ambiguity_warning { + _ if ambiguity_error.ambig_vis.is_some() => AMBIGUOUS_IMPORT_VISIBILITIES, AmbiguityWarning::GlobImport => AMBIGUOUS_GLOB_IMPORTS, AmbiguityWarning::PanicImport => AMBIGUOUS_PANIC_IMPORTS, }; @@ -1995,7 +1996,8 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { } fn ambiguity_diagnostic(&self, ambiguity_error: &AmbiguityError<'ra>) -> errors::Ambiguity { - let AmbiguityError { kind, ident, b1, b2, scope1, scope2, .. } = *ambiguity_error; + let AmbiguityError { kind, ambig_vis, ident, b1, b2, scope1, scope2, .. } = + *ambiguity_error; let extern_prelude_ambiguity = || { // Note: b1 may come from a module scope, as an extern crate item in module. matches!(scope2, Scope::ExternPreludeFlags) @@ -2074,9 +2076,18 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { None }; + let ambig_vis = ambig_vis.map(|(vis1, vis2)| { + format!( + "{} or {}", + vis1.to_string(CRATE_DEF_ID, self.tcx), + vis2.to_string(CRATE_DEF_ID, self.tcx) + ) + }); + errors::Ambiguity { ident, help, + ambig_vis, kind: kind.descr(), b1_note, b1_help_msgs, diff --git a/compiler/rustc_resolve/src/errors.rs b/compiler/rustc_resolve/src/errors.rs index 205f2c6aa539..3e5446403052 100644 --- a/compiler/rustc_resolve/src/errors.rs +++ b/compiler/rustc_resolve/src/errors.rs @@ -1463,6 +1463,7 @@ pub(crate) struct UnknownDiagnosticAttributeTypoSugg { // FIXME: Make this properly translatable. pub(crate) struct Ambiguity { pub ident: Ident, + pub ambig_vis: Option, pub kind: &'static str, pub help: Option<&'static [&'static str]>, pub b1_note: Spanned, @@ -1473,8 +1474,12 @@ pub(crate) struct Ambiguity { impl Ambiguity { fn decorate<'a>(self, diag: &mut Diag<'a, impl EmissionGuarantee>) { - diag.primary_message(format!("`{}` is ambiguous", self.ident)); - diag.span_label(self.ident.span, "ambiguous name"); + if let Some(ambig_vis) = self.ambig_vis { + diag.primary_message(format!("ambiguous import visibility: {ambig_vis}")); + } else { + diag.primary_message(format!("`{}` is ambiguous", self.ident)); + diag.span_label(self.ident.span, "ambiguous name"); + } diag.note(format!("ambiguous because of {}", self.kind)); diag.span_note(self.b1_note.span, self.b1_note.node); if let Some(help) = self.help { diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 0e73c349c8cd..d4d373d82064 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -5,6 +5,7 @@ use Namespace::*; use rustc_ast::{self as ast, NodeId}; use rustc_errors::ErrorGuaranteed; use rustc_hir::def::{DefKind, MacroKinds, Namespace, NonMacroAttrKind, PartialRes, PerNS}; +use rustc_middle::ty::Visibility; use rustc_middle::{bug, span_bug}; use rustc_session::lint::builtin::PROC_MACRO_DERIVE_RESOLUTION_FALLBACK; use rustc_session::parse::feature_err; @@ -485,9 +486,12 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // We do not need to report them if we are either in speculative resolution, // or in late resolution when everything is already imported and expanded // and no ambiguities exist. - if matches!(finalize, None | Some(Finalize { stage: Stage::Late, .. })) { - return ControlFlow::Break(Ok(decl)); - } + let import_vis = match finalize { + None | Some(Finalize { stage: Stage::Late, .. }) => { + return ControlFlow::Break(Ok(decl)); + } + Some(Finalize { import_vis, .. }) => import_vis, + }; if let Some(&(innermost_decl, _)) = innermost_results.first() { // Found another solution, if the first one was "weak", report an error. @@ -500,6 +504,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { decl, scope, &innermost_results, + import_vis, ) { // No need to search for more potential ambiguities, one is enough. return ControlFlow::Break(Ok(innermost_decl)); @@ -779,12 +784,22 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { decl: Decl<'ra>, scope: Scope<'ra>, innermost_results: &[(Decl<'ra>, Scope<'ra>)], + import_vis: Option, ) -> bool { let (innermost_decl, innermost_scope) = innermost_results[0]; let (res, innermost_res) = (decl.res(), innermost_decl.res()); - if res == innermost_res { + let ambig_vis = if res != innermost_res { + None + } else if let Some(import_vis) = import_vis + && let min = + (|d: Decl<'_>| d.vis().min(import_vis.to_def_id(), self.tcx).expect_local()) + && let (min1, min2) = (min(decl), min(innermost_decl)) + && min1 != min2 + { + Some((min1, min2)) + } else { return false; - } + }; // FIXME: Use `scope` instead of `res` to detect built-in attrs and derive helpers, // it will exclude imports, make slightly more code legal, and will require lang approval. @@ -871,10 +886,17 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { || (self.is_specific_builtin_macro(res, sym::core_panic) && self.is_specific_builtin_macro(innermost_res, sym::std_panic))); - let warning = is_issue_147319_hack.then_some(AmbiguityWarning::PanicImport); + let warning = if ambig_vis.is_some() { + Some(AmbiguityWarning::GlobImport) + } else if is_issue_147319_hack { + Some(AmbiguityWarning::PanicImport) + } else { + None + }; self.ambiguity_errors.push(AmbiguityError { kind, + ambig_vis, ident: ident.orig(orig_ident_span), b1: innermost_decl, b2: decl, diff --git a/compiler/rustc_resolve/src/imports.rs b/compiler/rustc_resolve/src/imports.rs index 057340028085..78ad139cff79 100644 --- a/compiler/rustc_resolve/src/imports.rs +++ b/compiler/rustc_resolve/src/imports.rs @@ -1189,7 +1189,11 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { ident, ns, &import.parent_scope, - Some(Finalize { report_private: false, ..finalize }), + Some(Finalize { + report_private: false, + import_vis: Some(import.vis), + ..finalize + }), bindings[ns].get().decl(), Some(import), ); diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 75bd2c62f9b4..3a65598584ae 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -969,6 +969,7 @@ enum AmbiguityWarning { struct AmbiguityError<'ra> { kind: AmbiguityKind, + ambig_vis: Option<(Visibility, Visibility)>, ident: Ident, b1: Decl<'ra>, b2: Decl<'ra>, @@ -2087,6 +2088,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { if let Some(b2) = used_decl.ambiguity.get() { let ambiguity_error = AmbiguityError { kind: AmbiguityKind::GlobVsGlob, + ambig_vis: None, ident, b1: used_decl, b2, @@ -2556,6 +2558,8 @@ struct Finalize { used: Used = Used::Other, /// Finalizing early or late resolution. stage: Stage = Stage::Early, + /// Nominal visibility of the import item, in case we are resolving an import's final segment. + import_vis: Option = None, } impl Finalize { diff --git a/tests/ui/imports/ambiguous-import-visibility-macro.rs b/tests/ui/imports/ambiguous-import-visibility-macro.rs new file mode 100644 index 000000000000..e1861cc5d4e0 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-macro.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +macro_rules! globbing{ + () => { + pub use same_res_ambigious_extern_macro::*; + } +} + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +fn main() {} diff --git a/tests/ui/imports/ambiguous-import-visibility-macro.stderr b/tests/ui/imports/ambiguous-import-visibility-macro.stderr new file mode 100644 index 000000000000..ed6eb6f893af --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-macro.stderr @@ -0,0 +1,29 @@ +warning: ambiguous import visibility: pub(crate) or pub + --> $DIR/ambiguous-import-visibility-macro.rs:17:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + = note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution +note: `RustEmbed` could refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility-macro.rs:7:17 + | +LL | pub use same_res_ambigious_extern_macro::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +... +LL | globbing! {} // this imports the same `RustEmbed` macro with `pub` visibility + | ------------ in this macro invocation + = help: consider adding an explicit import of `RustEmbed` to disambiguate + = help: or use `crate::RustEmbed` to refer to this derive macro unambiguously +note: `RustEmbed` could also refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility-macro.rs:11:1 + | +LL | #[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility + | ^^^^^^^^^^^^ + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + = note: this warning originates in the macro `globbing` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/imports/ambiguous-import-visibility-module.rs b/tests/ui/imports/ambiguous-import-visibility-module.rs new file mode 100644 index 000000000000..35c6da8b21a4 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-module.rs @@ -0,0 +1,24 @@ +//@ check-pass +//@ edition:2018.. + +mod reexport { + mod m { + pub struct S {} + } + + macro_rules! mac { + () => { + use m::S; + }; + } + + pub use m::*; + mac!(); + + pub use S as Z; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +} + +fn main() { + reexport::Z {}; +} diff --git a/tests/ui/imports/ambiguous-import-visibility-module.stderr b/tests/ui/imports/ambiguous-import-visibility-module.stderr new file mode 100644 index 000000000000..a97070c20a62 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility-module.stderr @@ -0,0 +1,29 @@ +warning: ambiguous import visibility: pub or pub(in crate::reexport) + --> $DIR/ambiguous-import-visibility-module.rs:18:13 + | +LL | pub use S as Z; + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + = note: ambiguous because of a conflict between a macro-expanded name and a less macro-expanded name from outer scope during import or macro resolution +note: `S` could refer to the struct imported here + --> $DIR/ambiguous-import-visibility-module.rs:11:17 + | +LL | use m::S; + | ^^^^ +... +LL | mac!(); + | ------ in this macro invocation + = help: use `self::S` to refer to this struct unambiguously +note: `S` could also refer to the struct imported here + --> $DIR/ambiguous-import-visibility-module.rs:15:13 + | +LL | pub use m::*; + | ^^^^ + = help: use `self::S` to refer to this struct unambiguously + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + = note: this warning originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info) + +warning: 1 warning emitted + diff --git a/tests/ui/imports/ambiguous-import-visibility.rs b/tests/ui/imports/ambiguous-import-visibility.rs new file mode 100644 index 000000000000..4cb8b763fbc9 --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility.rs @@ -0,0 +1,14 @@ +//@ check-pass +//@ edition:2018 +//@ proc-macro: same-res-ambigious-extern-macro.rs + +#[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility +extern crate same_res_ambigious_extern_macro; +// this imports the same `RustEmbed` macro with `pub` visibility +pub use same_res_ambigious_extern_macro::*; + +pub trait RustEmbed {} + +pub use RustEmbed as Embed; //~ WARN ambiguous import visibility + //~| WARN this was previously accepted +fn main() {} diff --git a/tests/ui/imports/ambiguous-import-visibility.stderr b/tests/ui/imports/ambiguous-import-visibility.stderr new file mode 100644 index 000000000000..30cddca4697d --- /dev/null +++ b/tests/ui/imports/ambiguous-import-visibility.stderr @@ -0,0 +1,25 @@ +warning: ambiguous import visibility: pub(crate) or pub + --> $DIR/ambiguous-import-visibility.rs:12:9 + | +LL | pub use RustEmbed as Embed; + | ^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #149145 + = note: ambiguous because of a conflict between a name from a glob import and an outer scope during import or macro resolution +note: `RustEmbed` could refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility.rs:8:9 + | +LL | pub use same_res_ambigious_extern_macro::*; + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + = help: consider adding an explicit import of `RustEmbed` to disambiguate + = help: or use `crate::RustEmbed` to refer to this derive macro unambiguously +note: `RustEmbed` could also refer to the derive macro imported here + --> $DIR/ambiguous-import-visibility.rs:5:1 + | +LL | #[macro_use] // this imports the `RustEmbed` macro with `pub(crate)` visibility + | ^^^^^^^^^^^^ + = note: `#[warn(ambiguous_import_visibilities)]` (part of `#[warn(future_incompatible)]`) on by default + +warning: 1 warning emitted + From 629ff9b4aa1f0216b26b44958d114332a793cf19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jana=20D=C3=B6nszelmann?= Date: Mon, 2 Feb 2026 12:48:42 +0100 Subject: [PATCH 239/319] Update tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs Co-authored-by: lcnr --- tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs index 0a948989f981..a81e18559a39 100644 --- a/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs +++ b/tests/ui/traits/next-solver/generalize/relate-alias-in-lub.rs @@ -7,7 +7,7 @@ // The change we tried to make there caused relating a type variable with an alias inside lub, // In 5bd20bbd0ba6c0285664e55a1ffc677d7487c98b, we moved around code // that adds an alias-relate predicate to be earlier, from one shared codepath into several -// distinct code paths. However, we forgot one codepath, through lub, causing an ICE in serde. +// distinct code paths. However, we forgot the codepath in `LatticeOp`, causing an ICE in serde. // In the end we dropped said commit, but the reproducer is still a useful as test. use std::marker::PhantomData; From 82a530c61c56df0fb89e1d3d70565ec3b69fbf32 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 2 Feb 2026 11:53:30 +0000 Subject: [PATCH 240/319] Port --- .../src/attributes/rustc_internal.rs | 8 ++++++++ compiler/rustc_attr_parsing/src/context.rs | 16 +++++++++------- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_hir_analysis/src/collect/dump.rs | 3 +-- compiler/rustc_passes/src/check_attr.rs | 2 +- 6 files changed, 23 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 38f728fa9f55..250bceecbd65 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -307,6 +307,14 @@ impl NoArgsAttributeParser for RustcHasIncoherentInherentImplsParse const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHasIncoherentInherentImpls; } +pub(crate) struct RustcHiddenTypeOfOpaquesParser; + +impl NoArgsAttributeParser for RustcHiddenTypeOfOpaquesParser { + const PATH: &[Symbol] = &[sym::rustc_hidden_type_of_opaques]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcHiddenTypeOfOpaques; +} pub(crate) struct RustcNounwindParser; impl NoArgsAttributeParser for RustcNounwindParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0cabc0895053..51da6def086c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -75,13 +75,14 @@ use crate::attributes::rustc_dump::{ RustcDumpVtable, }; use crate::attributes::rustc_internal::{ - RustcHasIncoherentInherentImplsParser, RustcLayoutParser, RustcLayoutScalarValidRangeEndParser, - RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, - RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, - RustcLintUntrackedQueryInformationParser, RustcMainParser, RustcMustImplementOneOfParser, - RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, - RustcNonConstTraitMethodParser, RustcNounwindParser, RustcObjectLifetimeDefaultParser, - RustcOffloadKernelParser, RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, + RustcHasIncoherentInherentImplsParser, RustcHiddenTypeOfOpaquesParser, RustcLayoutParser, + RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, + RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, + RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, + RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, + RustcNoImplicitAutorefsParser, RustcNonConstTraitMethodParser, RustcNounwindParser, + RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, + RustcSimdMonomorphizeLaneLimitParser, }; use crate::attributes::semantics::MayDangleParser; use crate::attributes::stability::{ @@ -299,6 +300,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 92dda79b0920..3104a08d7547 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1057,6 +1057,9 @@ pub enum AttributeKind { /// Represents `#[rustc_has_incoherent_inherent_impls]` RustcHasIncoherentInherentImpls, + /// Represents `#[rustc_hidden_type_of_opaques]` + RustcHiddenTypeOfOpaques, + /// Represents `#[rustc_layout]` RustcLayout(ThinVec), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 816ed07c1dc4..f03f55f85b39 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -111,6 +111,7 @@ impl AttributeKind { RustcDumpVtable(..) => No, RustcDynIncompatibleTrait(..) => No, RustcHasIncoherentInherentImpls => Yes, + RustcHiddenTypeOfOpaques => No, RustcLayout(..) => No, RustcLayoutScalarValidRangeEnd(..) => Yes, RustcLayoutScalarValidRangeStart(..) => Yes, diff --git a/compiler/rustc_hir_analysis/src/collect/dump.rs b/compiler/rustc_hir_analysis/src/collect/dump.rs index 2dfd4ab6111f..bbf912cd4bde 100644 --- a/compiler/rustc_hir_analysis/src/collect/dump.rs +++ b/compiler/rustc_hir_analysis/src/collect/dump.rs @@ -7,10 +7,9 @@ use rustc_middle::ty::{self, TyCtxt, TypeVisitableExt}; use rustc_span::sym; pub(crate) fn opaque_hidden_types(tcx: TyCtxt<'_>) { - if !tcx.has_attr(CRATE_DEF_ID, sym::rustc_hidden_type_of_opaques) { + if !find_attr!(tcx.get_all_attrs(CRATE_DEF_ID), AttributeKind::RustcHiddenTypeOfOpaques) { return; } - for id in tcx.hir_crate_items(()).opaques() { if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8cf68b280850..e413780d6c4f 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -299,6 +299,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcDumpVtable(..) | AttributeKind::RustcDynIncompatibleTrait(..) | AttributeKind::RustcHasIncoherentInherentImpls + | AttributeKind::RustcHiddenTypeOfOpaques | AttributeKind::RustcLayout(..) | AttributeKind::RustcLayoutScalarValidRangeEnd(..) | AttributeKind::RustcLayoutScalarValidRangeStart(..) @@ -390,7 +391,6 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::rustc_capture_analysis | sym::rustc_regions | sym::rustc_strict_coherence - | sym::rustc_hidden_type_of_opaques | sym::rustc_mir | sym::rustc_effective_visibility | sym::rustc_outlives From fd023cb122ffacff4bd14f27d9751ffce1bd7878 Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 2 Feb 2026 12:57:35 +0100 Subject: [PATCH 241/319] Add uv to the list of possible python runners I'm one of the rare cases that does have uv, but doesn't have python installed globally --- x | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/x b/x index 4fce0be219e7..a8acbd4d5ac6 100755 --- a/x +++ b/x @@ -29,16 +29,19 @@ xpy=$(dirname "$(realpath "$0")")/x.py # On MacOS, `py` tries to install "Developer command line tools". Try `python3` first. # NOTE: running `bash -c ./x` from Windows doesn't set OSTYPE. case ${OSTYPE:-} in - cygwin*|msys*) SEARCH="py python3 python python2";; - *) SEARCH="python3 python py python2";; + cygwin*|msys*) SEARCH="py python3 python python2 uv";; + *) SEARCH="python3 python py python2 uv";; esac for SEARCH_PYTHON in $SEARCH; do if python=$(command -v $SEARCH_PYTHON) && [ -x "$python" ]; then - if [ $SEARCH_PYTHON = py ]; then - extra_arg="-3" - else - extra_arg="" - fi + case $SEARCH_PYTHON in + py) + extra_arg="-3";; + uv) + extra_arg="run";; + *) + extra_arg="";; + esac exec "$python" $extra_arg "$xpy" "$@" fi done From 30ae46fab148f8f924d445feddc68b9103204423 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 2 Feb 2026 14:43:33 +0100 Subject: [PATCH 242/319] Use rustc_parse's Recovery instead of a boolean --- compiler/rustc_attr_parsing/src/attributes/cfg.rs | 10 ++++++---- .../src/attributes/cfg_select.rs | 12 +++++++----- compiler/rustc_attr_parsing/src/context.rs | 11 ++++++----- compiler/rustc_attr_parsing/src/parser.rs | 14 ++++++++++---- compiler/rustc_builtin_macros/src/cfg.rs | 5 +++-- compiler/rustc_expand/src/config.rs | 5 ++++- compiler/rustc_expand/src/expand.rs | 6 +++--- 7 files changed, 39 insertions(+), 24 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 3a540d80998d..157a91c249d4 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -10,7 +10,7 @@ use rustc_feature::{ use rustc_hir::attrs::CfgEntry; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, RustcVersion, Target}; -use rustc_parse::parser::{ForceCollect, Parser}; +use rustc_parse::parser::{ForceCollect, Parser, Recovery}; use rustc_parse::{exp, parse_in}; use rustc_session::Session; use rustc_session::config::ExpectedValues; @@ -360,8 +360,10 @@ fn parse_cfg_attr_internal<'a>( ) -> PResult<'a, (CfgEntry, Vec<(ast::AttrItem, Span)>)> { // Parse cfg predicate let pred_start = parser.token.span; - let meta = - MetaItemOrLitParser::parse_single(parser, ShouldEmit::ErrorsAndLints { recover: true })?; + let meta = MetaItemOrLitParser::parse_single( + parser, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + )?; let pred_span = pred_start.with_hi(parser.token.span.hi()); let cfg_predicate = AttributeParser::parse_single_args( @@ -376,7 +378,7 @@ fn parse_cfg_attr_internal<'a>( CRATE_NODE_ID, Target::Crate, features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, &meta, parse_cfg_entry, &CFG_ATTR_TEMPLATE, diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs index ca844758daaa..4005ad2cba11 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg_select.rs @@ -5,7 +5,7 @@ use rustc_feature::{AttributeTemplate, Features}; use rustc_hir::attrs::CfgEntry; use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; -use rustc_parse::parser::Parser; +use rustc_parse::parser::{Parser, Recovery}; use rustc_session::Session; use rustc_span::{ErrorGuaranteed, Span, sym}; @@ -78,9 +78,11 @@ pub fn parse_cfg_select( } } } else { - let meta = - MetaItemOrLitParser::parse_single(p, ShouldEmit::ErrorsAndLints { recover: true }) - .map_err(|diag| diag.emit())?; + let meta = MetaItemOrLitParser::parse_single( + p, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, + ) + .map_err(|diag| diag.emit())?; let cfg_span = meta.span(); let cfg = AttributeParser::parse_single_args( sess, @@ -95,7 +97,7 @@ pub fn parse_cfg_select( // Doesn't matter what the target actually is here. Target::Crate, features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, &meta, parse_cfg_entry, &AttributeTemplate::default(), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0cabc0895053..fa6c9513c59c 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -10,6 +10,7 @@ use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; use rustc_hir::attrs::AttributeKind; use rustc_hir::lints::AttributeLintKind; use rustc_hir::{AttrPath, HirId}; +use rustc_parse::parser::Recovery; use rustc_session::Session; use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; @@ -383,7 +384,7 @@ impl Stage for Late { } fn should_emit(&self) -> ShouldEmit { - ShouldEmit::ErrorsAndLints { recover: true } + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed } } } @@ -770,10 +771,10 @@ pub enum ShouldEmit { ErrorsAndLints { /// Whether [`ArgParser`] will attempt to recover from errors. /// - /// If true, it will attempt to recover from bad input (like an invalid literal). Setting - /// this to false will instead return early, and not raise errors except at the top level - /// (in [`ArgParser::from_attr_args`]). - recover: bool, + /// Whether it is allowed to recover from bad input (like an invalid literal). Setting + /// this to `Forbidden` will instead return early, and not raise errors except at the top + /// level (in [`ArgParser::from_attr_args`]). + recovery: Recovery, }, /// The operation will *not* emit errors and lints. /// diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 7f3c6d28005f..80dab0b85edd 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -15,7 +15,7 @@ use rustc_ast_pretty::pprust; use rustc_errors::{Diag, PResult}; use rustc_hir::{self as hir, AttrPath}; use rustc_parse::exp; -use rustc_parse::parser::{ForceCollect, Parser, PathStyle, token_descr}; +use rustc_parse::parser::{ForceCollect, Parser, PathStyle, Recovery, token_descr}; use rustc_session::errors::create_lit_error; use rustc_session::parse::ParseSess; use rustc_span::{Ident, Span, Symbol, sym}; @@ -121,7 +121,7 @@ impl ArgParser { &args.tokens, args.dspan.entire(), psess, - ShouldEmit::ErrorsAndLints { recover: false }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden }, ) { Ok(p) => return Some(ArgParser::List(p)), Err(e) => { @@ -373,7 +373,10 @@ fn expr_to_lit<'sess>( } Err(err) => { let err = create_lit_error(psess, err, token_lit, expr.span); - if matches!(should_emit, ShouldEmit::ErrorsAndLints { recover: false }) { + if matches!( + should_emit, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } + ) { Err(err) } else { let lit = MetaItemLit { @@ -431,7 +434,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { if !lit.kind.is_unsuffixed() { // Emit error and continue, we can still parse the attribute as if the suffix isn't there let err = self.parser.dcx().create_err(SuffixedLiteralInAttribute { span: lit.span }); - if matches!(self.should_emit, ShouldEmit::ErrorsAndLints { recover: false }) { + if matches!( + self.should_emit, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Forbidden } + ) { return Err(err); } else { self.should_emit.emit_err(err) diff --git a/compiler/rustc_builtin_macros/src/cfg.rs b/compiler/rustc_builtin_macros/src/cfg.rs index 8e925cfe09a2..3ebde949b99b 100644 --- a/compiler/rustc_builtin_macros/src/cfg.rs +++ b/compiler/rustc_builtin_macros/src/cfg.rs @@ -13,6 +13,7 @@ use rustc_expand::base::{DummyResult, ExpandResult, ExtCtxt, MacEager, MacroExpa use rustc_hir::attrs::CfgEntry; use rustc_hir::{AttrPath, Target}; use rustc_parse::exp; +use rustc_parse::parser::Recovery; use rustc_span::{ErrorGuaranteed, Span, sym}; use crate::errors; @@ -42,7 +43,7 @@ fn parse_cfg(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream) -> Result, span: Span, tts: TokenStream) -> Result StripUnconfigured<'a> { fn in_cfg(&self, attrs: &[Attribute]) -> bool { attrs.iter().all(|attr| { !is_cfg(attr) - || self.cfg_true(attr, ShouldEmit::ErrorsAndLints { recover: true }).as_bool() + || self + .cfg_true(attr, ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }) + .as_bool() }) } diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index cfa7725c7400..48e2ee8e9abd 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -26,7 +26,7 @@ use rustc_hir::def::MacroKinds; use rustc_hir::limit::Limit; use rustc_parse::parser::{ AllowConstBlockItems, AttemptLocalParseRecovery, CommaRecoveryMode, ForceCollect, Parser, - RecoverColon, RecoverComma, token_descr, + RecoverColon, RecoverComma, Recovery, token_descr, }; use rustc_session::Session; use rustc_session::lint::builtin::{UNUSED_ATTRIBUTES, UNUSED_DOC_COMMENTS}; @@ -2170,7 +2170,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { call.span(), self.cx.current_expansion.lint_node_id, Some(self.cx.ecfg.features), - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, ); let current_span = if let Some(sp) = span { sp.to(attr.span) } else { attr.span }; @@ -2220,7 +2220,7 @@ impl<'a, 'b> InvocationCollector<'a, 'b> { // Target doesn't matter for `cfg` parsing. Target::Crate, self.cfg().features, - ShouldEmit::ErrorsAndLints { recover: true }, + ShouldEmit::ErrorsAndLints { recovery: Recovery::Allowed }, parse_cfg, &CFG_TEMPLATE, ) else { From aef821794367e03e654acbb2fa8f098102b9ef64 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Mon, 2 Feb 2026 14:55:21 +0100 Subject: [PATCH 243/319] Pass `Recovery` down to rustc_parse's Parser --- compiler/rustc_attr_parsing/src/parser.rs | 4 ++++ .../do_not_recommend/does_not_acccept_args.current.stderr | 4 ++-- .../do_not_recommend/does_not_acccept_args.next.stderr | 4 ++-- .../do_not_recommend/does_not_acccept_args.rs | 2 +- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/parser.rs b/compiler/rustc_attr_parsing/src/parser.rs index 80dab0b85edd..973635f432e8 100644 --- a/compiler/rustc_attr_parsing/src/parser.rs +++ b/compiler/rustc_attr_parsing/src/parser.rs @@ -575,6 +575,10 @@ impl<'a, 'sess> MetaItemListParserContext<'a, 'sess> { should_emit: ShouldEmit, ) -> PResult<'sess, MetaItemListParser> { let mut parser = Parser::new(psess, tokens, None); + if let ShouldEmit::ErrorsAndLints { recovery } = should_emit { + parser = parser.recovery(recovery); + } + let mut this = MetaItemListParserContext { parser: &mut parser, should_emit }; // Presumably, the majority of the time there will only be one attr. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr index 43205bd395fc..075e4bf0384d 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.current.stderr @@ -1,8 +1,8 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/does_not_acccept_args.rs:12:1 | -LL | #[diagnostic::do_not_recommend(not_accepted)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr index 43205bd395fc..075e4bf0384d 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.next.stderr @@ -1,8 +1,8 @@ warning: `#[diagnostic::do_not_recommend]` does not expect any arguments --> $DIR/does_not_acccept_args.rs:12:1 | -LL | #[diagnostic::do_not_recommend(not_accepted)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +LL | #[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs index 918bf5a0113e..943b5a37f938 100644 --- a/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs +++ b/tests/ui/diagnostic_namespace/do_not_recommend/does_not_acccept_args.rs @@ -9,7 +9,7 @@ trait Bar {} trait Baz {} trait Boo {} -#[diagnostic::do_not_recommend(not_accepted)] +#[diagnostic::do_not_recommend(if, crate, do yeet, false, dyn, abstract, gen, not_accepted)] //~^ WARNING `#[diagnostic::do_not_recommend]` does not expect any arguments impl Foo for T where T: Send {} From bdd19d0db39266bd5c22348d15b8215e3a804e59 Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Mon, 2 Feb 2026 15:13:38 +0100 Subject: [PATCH 244/319] clarity that the neon extension is present --- tests/ui/asm/cortex-r82.rs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/ui/asm/cortex-r82.rs b/tests/ui/asm/cortex-r82.rs index a64349b37af7..74313e5cb48c 100644 --- a/tests/ui/asm/cortex-r82.rs +++ b/tests/ui/asm/cortex-r82.rs @@ -22,6 +22,10 @@ use minicore::*; * A `/* */` comment indicates that the extension is being tested in the ISA level codegen test * (`tests/ui/asm/aarch64v8r.rs`) * + * Note that as we use the hard-float `aarch64v8r-unknown-none` target as the base, the neon + * extension is present (`NEON_FPm=1`). This affects which R82-specific extensions are enabled + * (see "when `NEON_FPm == 1`" note in Cortex-R82 Processor Technical Reference Manual) + * * ## References: * * - Arm Cortex-R82 Processor Technical Reference Manual Revision r3p1 (102670_0301_06_en Issue 6) @@ -55,7 +59,7 @@ pub fn mandatory_extensions() { // FEAT_nTLBPA /* FEAT_CRC32 */ /* FEAT_LSE */ - feat_rdm(); + feat_rdm(); // mandatory given that NEON_FPm=1 /* FEAT_HPDS */ /* FEAT_PAN */ // FEAT_HAFDBS @@ -68,10 +72,10 @@ pub fn mandatory_extensions() { /* FEAT_Debugv8p2 */ /* FEAT_ASMv8p2 */ // FEAT_IESB - feat_fp16(); + feat_fp16(); // mandatory given that NEON_FPm=1 // FEAT_PCSRv8p2 - feat_dotprod(); - feat_fhm(); + feat_dotprod(); // mandatory given that NEON_FPm=1 + feat_fhm(); // mandatory given that NEON_FPm=1 feat_dpb2(); /* FEAT_PAuth */ // FEAT_PACQARMA3 @@ -79,9 +83,9 @@ pub fn mandatory_extensions() { // FEAT_FPAC // FEAT_FPACCOMBINE // FEAT_CONSTPACFIELD - feat_jscvt(); + feat_jscvt(); // mandatory given that NEON_FPm=1 /* FEAT_LRCPC */ - feat_fcma(); + feat_fcma(); // mandatory given that NEON_FPm=1 // FEAT_DoPD // FEAT_SEL2 /* FEAT_S2FWB */ From 6490e0ecb5e78932f2e7f5bf5c11726f972eec8c Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 2 Feb 2026 13:57:40 +0100 Subject: [PATCH 245/319] stabilize ptr_as_ref_unchecked --- compiler/rustc_resolve/src/lib.rs | 2 +- library/core/src/ptr/const_ptr.rs | 8 +++--- library/core/src/ptr/docs/as_ref.md | 8 +++--- library/core/src/ptr/mut_ptr.rs | 26 +++++++++---------- .../data_race/mixed_size_read_write_read.rs | 1 - 5 files changed, 22 insertions(+), 23 deletions(-) diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index 5fe1be039a88..cc3c7bc49446 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -8,6 +8,7 @@ // tidy-alphabetical-start #![allow(internal_features)] +#![cfg_attr(bootstrap, feature(ptr_as_ref_unchecked))] #![feature(arbitrary_self_types)] #![feature(assert_matches)] #![feature(box_patterns)] @@ -18,7 +19,6 @@ #![feature(default_field_values)] #![feature(if_let_guard)] #![feature(iter_intersperse)] -#![feature(ptr_as_ref_unchecked)] #![feature(rustc_attrs)] #![feature(trim_prefix_suffix)] #![recursion_limit = "256"] diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 2566d1471ab7..75777144dfb8 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -239,7 +239,7 @@ impl *const T { /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// assert_eq!(val_back, &10); /// } /// ``` @@ -259,6 +259,7 @@ impl *const T { /// /// [`is_null`]: #method.is_null /// [`as_uninit_ref`]: #method.as_uninit_ref + /// [`as_ref_unchecked`]: #method.as_ref_unchecked #[stable(feature = "ptr_as_ref", since = "1.9.0")] #[rustc_const_stable(feature = "const_ptr_is_null", since = "1.84.0")] #[inline] @@ -283,15 +284,14 @@ impl *const T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *const u8 = &10u8 as *const u8; /// /// unsafe { /// assert_eq!(ptr.as_ref_unchecked(), &10); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { diff --git a/library/core/src/ptr/docs/as_ref.md b/library/core/src/ptr/docs/as_ref.md index 0c0d2768c748..2c7d6e149b76 100644 --- a/library/core/src/ptr/docs/as_ref.md +++ b/library/core/src/ptr/docs/as_ref.md @@ -1,6 +1,7 @@ Returns `None` if the pointer is null, or else returns a shared reference to the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_ref`] -must be used instead. +must be used instead. If the value is known to be non-null, [`as_ref_unchecked`] +can be used instead. # Safety @@ -14,6 +15,5 @@ determined to be null or not. See [`is_null`] for more information. # Null-unchecked version -If you are sure the pointer can never be null and are looking for some kind of -`as_ref_unchecked` that returns the `&T` instead of `Option<&T>`, know that you can -dereference the pointer directly. +If you are sure the pointer can never be null, you can use `as_ref_unchecked` which returns +`&mut T` instead of `Option<&mut T>`. diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 20e71bc2a1a5..02e12d56fa65 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -230,7 +230,7 @@ impl *mut T { /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { - /// let val_back = &*ptr; + /// let val_back = ptr.as_ref_unchecked(); /// println!("We got back the value: {val_back}!"); /// } /// ``` @@ -252,7 +252,8 @@ impl *mut T { /// For the mutable counterpart see [`as_mut`]. /// /// [`is_null`]: #method.is_null-1 - /// [`as_uninit_ref`]: pointer#method.as_uninit_ref-1 + /// [`as_uninit_ref`]: #method.as_uninit_ref-1 + /// [`as_ref_unchecked`]: #method.as_ref_unchecked-1 /// [`as_mut`]: #method.as_mut #[stable(feature = "ptr_as_ref", since = "1.9.0")] @@ -281,15 +282,14 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let ptr: *mut u8 = &mut 10u8 as *mut u8; /// /// unsafe { /// println!("We got back the value: {}!", ptr.as_ref_unchecked()); /// } /// ``` - // FIXME: mention it in the docs for `as_ref` and `as_uninit_ref` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_ref_unchecked<'a>(self) -> &'a T { @@ -531,11 +531,13 @@ impl *mut T { /// Returns `None` if the pointer is null, or else returns a unique reference to /// the value wrapped in `Some`. If the value may be uninitialized, [`as_uninit_mut`] - /// must be used instead. + /// must be used instead. If the value is known to be non-null, [`as_mut_unchecked`] + /// can be used instead. /// /// For the shared counterpart see [`as_ref`]. /// /// [`as_uninit_mut`]: #method.as_uninit_mut + /// [`as_mut_unchecked`]: #method.as_mut_unchecked /// [`as_ref`]: pointer#method.as_ref-1 /// /// # Safety @@ -564,14 +566,13 @@ impl *mut T { /// /// # Null-unchecked version /// - /// If you are sure the pointer can never be null and are looking for some kind of - /// `as_mut_unchecked` that returns the `&mut T` instead of `Option<&mut T>`, know that - /// you can dereference the pointer directly. + /// If you are sure the pointer can never be null, you can use `as_mut_unchecked` which returns + /// `&mut T` instead of `Option<&mut T>`. /// /// ``` /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); - /// let first_value = unsafe { &mut *ptr }; + /// let first_value = unsafe { ptr.as_mut_unchecked() }; /// *first_value = 4; /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". @@ -603,7 +604,6 @@ impl *mut T { /// # Examples /// /// ``` - /// #![feature(ptr_as_ref_unchecked)] /// let mut s = [1, 2, 3]; /// let ptr: *mut u32 = s.as_mut_ptr(); /// let first_value = unsafe { ptr.as_mut_unchecked() }; @@ -611,8 +611,8 @@ impl *mut T { /// # assert_eq!(s, [4, 2, 3]); /// println!("{s:?}"); // It'll print: "[4, 2, 3]". /// ``` - // FIXME: mention it in the docs for `as_mut` and `as_uninit_mut` once stabilized. - #[unstable(feature = "ptr_as_ref_unchecked", issue = "122034")] + #[stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "ptr_as_ref_unchecked", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_mut_unchecked<'a>(self) -> &'a mut T { diff --git a/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs b/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs index c84895799b69..2d3d36c0e64a 100644 --- a/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs +++ b/src/tools/miri/tests/fail/data_race/mixed_size_read_write_read.rs @@ -1,6 +1,5 @@ //@compile-flags: -Zmiri-deterministic-concurrency // A case that is not covered by `mixed_size_read_write`. -#![feature(ptr_as_ref_unchecked)] use std::sync::atomic::*; use std::thread; From 8fa27e03c0e3a080e3b030459fdb69c2ecf4bb4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Mon, 2 Feb 2026 21:12:44 +0200 Subject: [PATCH 246/319] Also exclude proc-macro-srv-cli from stage 0 tests --- src/bootstrap/src/core/build_steps/test.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 844251f2c64d..217fa344699e 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -513,6 +513,7 @@ impl Step for RustAnalyzer { // This builds a proc macro against the bootstrap libproc_macro, which is not ABI // compatible with the ABI proc-macro-srv expects to load. cargo.arg("--exclude=proc-macro-srv"); + cargo.arg("--exclude=proc-macro-srv-cli"); } let mut skip_tests = vec![]; From c7c3266b8a073ad51e6cc1bc65ff344ad33860a6 Mon Sep 17 00:00:00 2001 From: khyperia <953151+khyperia@users.noreply.github.com> Date: Mon, 2 Feb 2026 20:48:14 +0100 Subject: [PATCH 247/319] error on unsized AnonConsts --- compiler/rustc_hir_typeck/src/lib.rs | 8 +++ tests/crashes/137582.rs | 16 ----- .../unsized-anon-const-err-1.rs | 22 +++++++ .../unsized-anon-const-err-1.stderr | 44 +++++++++++++ .../unsized-anon-const-err-2.rs | 21 ++++++ .../unsized-anon-const-err-2.stderr | 66 +++++++++++++++++++ .../unsized-anon-const-func-err.rs | 16 +++++ .../unsized-anon-const-func-err.stderr | 38 +++++++++++ .../unsized-anon-const-struct-err.rs | 16 +++++ .../unsized-anon-const-struct-err.stderr | 38 +++++++++++ 10 files changed, 269 insertions(+), 16 deletions(-) delete mode 100644 tests/crashes/137582.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs create mode 100644 tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index 39c28c4f4e99..6e126b3013c9 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -200,6 +200,14 @@ fn typeck_with_inspect<'tcx>( let wf_code = ObligationCauseCode::WellFormed(Some(WellFormedLoc::Ty(def_id))); fcx.register_wf_obligation(expected_type.into(), body.value.span, wf_code); + if let hir::Node::AnonConst(_) = node { + fcx.require_type_is_sized( + expected_type, + body.value.span, + ObligationCauseCode::SizedConstOrStatic, + ); + } + fcx.check_expr_coercible_to_type(body.value, expected_type, None); fcx.write_ty(id, expected_type); diff --git a/tests/crashes/137582.rs b/tests/crashes/137582.rs deleted file mode 100644 index e21b6c9578b7..000000000000 --- a/tests/crashes/137582.rs +++ /dev/null @@ -1,16 +0,0 @@ -//@ known-bug: #137582 -#![feature(adt_const_params)] - -mod lib { - pub type Matrix = [&'static u32]; - - const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; - - pub struct Walk { - _p: (), - } - - impl Walk {} -} - -fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs new file mode 100644 index 000000000000..17910d52d3d7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.rs @@ -0,0 +1,22 @@ +// regression test for issue #137582, where constant evaluating an unsized AnonConst would ICE + +#![feature(adt_const_params)] + +mod lib { + pub type Matrix = [&'static u32]; + + const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + //~^ ERROR the size for values of type `[&'static u32]` cannot be known at compilation time + //~| ERROR mismatched types + //~| ERROR mismatched types + + pub struct Walk { + //~^ ERROR use of unstable library feature `unsized_const_params` + _p: (), + } + + impl Walk {} + //~^ ERROR the size for values of type `[&'static u32]` cannot be known at compilation time +} + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr new file mode 100644 index 000000000000..daea55efbbc7 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-1.stderr @@ -0,0 +1,44 @@ +error[E0277]: the size for values of type `[&'static u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-1.rs:8:25 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[&'static u32]` + = note: statics and constants must have a statically known size + +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-err-1.rs:13:43 + | +LL | pub struct Walk { + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[&'static u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[&'static u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-1.rs:18:46 + | +LL | impl Walk {} + | ^^^^^^^^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[&'static u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-err-1.rs:8:35 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^ expected `&u32`, found `[{integer}; 4]` + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-err-1.rs:8:34 + | +LL | const EMPTY_MATRIX: Matrix = [[0; 4]; 4]; + | ^^^^^^^^^^^ expected `[&u32]`, found `[&u32; 4]` + +error: aborting due to 5 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs new file mode 100644 index 000000000000..81b1ec6a7719 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.rs @@ -0,0 +1,21 @@ +// regression test for issue #151591, where constant evaluating an unsized AnonConst would ICE + +#![feature(adt_const_params)] +#![feature(unsized_const_params)] +//~^ WARN the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + +#[derive(Clone)] +struct S; + +const A: [u8]; +//~^ ERROR free constant item without body +//~| ERROR the size for values of type `[u8]` cannot be known at compilation time + +impl Copy for S {} +//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR the const parameter `N` is not constrained by the impl trait, self type, or predicates +impl Copy for S {} +//~^ ERROR the size for values of type `[u8]` cannot be known at compilation time +//~| ERROR the const parameter `M` is not constrained by the impl trait, self type, or predicates + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr new file mode 100644 index 000000000000..d538bb0af09a --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-err-2.stderr @@ -0,0 +1,66 @@ +error: free constant item without body + --> $DIR/unsized-anon-const-err-2.rs:10:1 + | +LL | const A: [u8]; + | ^^^^^^^^^^^^^- + | | + | help: provide a definition for the constant: `= ;` + +warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/unsized-anon-const-err-2.rs:4:12 + | +LL | #![feature(unsized_const_params)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 for more information + = note: `#[warn(incomplete_features)]` on by default + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:10:10 + | +LL | const A: [u8]; + | ^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:14:31 + | +LL | impl Copy for S {} + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0277]: the size for values of type `[u8]` cannot be known at compilation time + --> $DIR/unsized-anon-const-err-2.rs:17:33 + | +LL | impl Copy for S {} + | ^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u8]` + = note: statics and constants must have a statically known size + +error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates + --> $DIR/unsized-anon-const-err-2.rs:14:6 + | +LL | impl Copy for S {} + | ^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error[E0207]: the const parameter `M` is not constrained by the impl trait, self type, or predicates + --> $DIR/unsized-anon-const-err-2.rs:17:6 + | +LL | impl Copy for S {} + | ^^^^^^^^^^^^^^ unconstrained const parameter + | + = note: expressions using a const parameter must map each value to a distinct output value + = note: proving the result of expressions other than the parameter are unique is not supported + +error: aborting due to 6 previous errors; 1 warning emitted + +Some errors have detailed explanations: E0207, E0277. +For more information about an error, try `rustc --explain E0207`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs new file mode 100644 index 000000000000..a299cc29fcc5 --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.rs @@ -0,0 +1,16 @@ +// manually reduced reproduction of issue #137582, where constant evaluating an unsized AnonConst +// would ICE + +#![feature(adt_const_params)] + +fn func() {} +//~^ ERROR use of unstable library feature `unsized_const_params` + +const VALUE: [u32] = [0; 4]; +//~^ ERROR mismatched types +//~| ERROR the size for values of type `[u32]` cannot be known at compilation time + +fn main() { + func::(); + //~^ ERROR the size for values of type `[u32]` cannot be known at compilation time +} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr new file mode 100644 index 000000000000..14a41e87e4aa --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-func-err.stderr @@ -0,0 +1,38 @@ +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-func-err.rs:6:9 + | +LL | fn func() {} + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-func-err.rs:9:14 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-func-err.rs:9:22 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^^ expected `[u32]`, found `[u32; 4]` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-func-err.rs:14:12 + | +LL | func::(); + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs new file mode 100644 index 000000000000..35407d02f6ef --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.rs @@ -0,0 +1,16 @@ +// manually reduced reproduction of issue #137582, where constant evaluating an unsized AnonConst +// would ICE + +#![feature(adt_const_params)] + +const VALUE: [u32] = [0; 4]; +//~^ ERROR the size for values of type `[u32]` cannot be known at compilation time +//~| ERROR mismatched types + +struct SomeStruct {} +//~^ ERROR use of unstable library feature `unsized_const_params` + +impl SomeStruct {} +//~^ ERROR the size for values of type `[u32]` cannot be known at compilation time + +fn main() {} diff --git a/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr new file mode 100644 index 000000000000..d9a13976d68a --- /dev/null +++ b/tests/ui/const-generics/adt_const_params/unsized-anon-const-struct-err.stderr @@ -0,0 +1,38 @@ +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-struct-err.rs:6:14 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0658]: use of unstable library feature `unsized_const_params` + --> $DIR/unsized-anon-const-struct-err.rs:10:19 + | +LL | struct SomeStruct {} + | ^^^^^^^^^^^^^^ + | + = help: add `#![feature(unsized_const_params)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: required for `[u32]` to implement `ConstParamTy_` + +error[E0277]: the size for values of type `[u32]` cannot be known at compilation time + --> $DIR/unsized-anon-const-struct-err.rs:13:17 + | +LL | impl SomeStruct {} + | ^^^^^ doesn't have a size known at compile-time + | + = help: the trait `Sized` is not implemented for `[u32]` + = note: statics and constants must have a statically known size + +error[E0308]: mismatched types + --> $DIR/unsized-anon-const-struct-err.rs:6:22 + | +LL | const VALUE: [u32] = [0; 4]; + | ^^^^^^ expected `[u32]`, found `[u32; 4]` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0277, E0308, E0658. +For more information about an error, try `rustc --explain E0277`. From 06fe81964e924a2cd0f94fbe3859615a5cf66329 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 22:26:46 +0200 Subject: [PATCH 248/319] use sentence case for titles This makes things consistent --- src/doc/rustc-dev-guide/src/external-repos.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/external-repos.md b/src/doc/rustc-dev-guide/src/external-repos.md index 2e32fcfe78c1..7ae1c881be8f 100644 --- a/src/doc/rustc-dev-guide/src/external-repos.md +++ b/src/doc/rustc-dev-guide/src/external-repos.md @@ -1,4 +1,4 @@ -# Using External Repositories +# Using external repositories The `rust-lang/rust` git repository depends on several other repos in the `rust-lang` organization. There are three main ways we use dependencies: @@ -12,7 +12,7 @@ As a general rule: changes - Use submodules for tools that are independent of the compiler -## External Dependencies (subtrees) +## External dependencies (subtrees) The following external projects are managed using some form of a `subtree`: @@ -153,7 +153,7 @@ Now you're done, the `src/tools/clippy` directory behaves as if Clippy were part of the rustc monorepo, so no one but you (or others that synchronize subtrees) actually needs to use `git subtree`. -## External Dependencies (submodules) +## External dependencies (submodules) Building Rust will also use external git repositories tracked using [git submodules]. The complete list may be found in the [`.gitmodules`] file. From 6b1d4059cbd650617c298079b6c488cb6b68086f Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Mon, 2 Feb 2026 21:14:30 +0000 Subject: [PATCH 249/319] Port rustc_preserve_ub_checks to attr parser. --- .../rustc_attr_parsing/src/attributes/crate_level.rs | 9 +++++++++ compiler/rustc_attr_parsing/src/context.rs | 4 +++- compiler/rustc_hir/src/attrs/data_structures.rs | 3 +++ compiler/rustc_hir/src/attrs/encode_cross_crate.rs | 1 + compiler/rustc_mir_transform/src/instsimplify.rs | 6 +++--- compiler/rustc_passes/src/check_attr.rs | 4 ++-- 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs index a367e699fcb9..557dfe09853b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/crate_level.rs +++ b/compiler/rustc_attr_parsing/src/attributes/crate_level.rs @@ -274,3 +274,12 @@ impl NoArgsAttributeParser for NoBuiltinsParser { const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::NoBuiltins; } + +pub(crate) struct RustcPreserveUbChecksParser; + +impl NoArgsAttributeParser for RustcPreserveUbChecksParser { + const PATH: &[Symbol] = &[sym::rustc_preserve_ub_checks]; + const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]); + const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcPreserveUbChecks; +} diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 0cabc0895053..736eb704b737 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -31,7 +31,8 @@ use crate::attributes::crate_level::{ CrateNameParser, CrateTypeParser, MoveSizeLimitParser, NeedsPanicRuntimeParser, NoBuiltinsParser, NoCoreParser, NoMainParser, NoStdParser, PanicRuntimeParser, PatternComplexityLimitParser, ProfilerRuntimeParser, RecursionLimitParser, - RustcCoherenceIsCoreParser, TypeLengthLimitParser, WindowsSubsystemParser, + RustcCoherenceIsCoreParser, RustcPreserveUbChecksParser, TypeLengthLimitParser, + WindowsSubsystemParser, }; use crate::attributes::debugger::DebuggerViualizerParser; use crate::attributes::deprecation::DeprecationParser; @@ -309,6 +310,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 92dda79b0920..13e36a0ed8e6 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -1123,6 +1123,9 @@ pub enum AttributeKind { /// Represents `#[rustc_pass_indirectly_in_non_rustic_abis]` RustcPassIndirectlyInNonRusticAbis(Span), + /// Represents `#[rustc_preserve_ub_checks]` + RustcPreserveUbChecks, + /// Represents `#[rustc_pub_transparent]` (used by the `repr_transparent_external_private_fields` lint). RustcPubTransparent(Span), diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 816ed07c1dc4..4ffabf82dceb 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -133,6 +133,7 @@ impl AttributeKind { RustcParenSugar(..) => No, RustcPassByValue(..) => Yes, RustcPassIndirectlyInNonRusticAbis(..) => No, + RustcPreserveUbChecks => No, RustcPubTransparent(..) => Yes, RustcReallocator => No, RustcScalableVector { .. } => Yes, diff --git a/compiler/rustc_mir_transform/src/instsimplify.rs b/compiler/rustc_mir_transform/src/instsimplify.rs index fa9ceb018dd5..84d642ea4ebc 100644 --- a/compiler/rustc_mir_transform/src/instsimplify.rs +++ b/compiler/rustc_mir_transform/src/instsimplify.rs @@ -1,8 +1,8 @@ //! Performs various peephole optimizations. use rustc_abi::ExternAbi; -use rustc_ast::attr; -use rustc_hir::LangItem; +use rustc_hir::attrs::AttributeKind; +use rustc_hir::{LangItem, find_attr}; use rustc_middle::bug; use rustc_middle::mir::visit::MutVisitor; use rustc_middle::mir::*; @@ -31,7 +31,7 @@ impl<'tcx> crate::MirPass<'tcx> for InstSimplify { fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { let preserve_ub_checks = - attr::contains_name(tcx.hir_krate_attrs(), sym::rustc_preserve_ub_checks); + find_attr!(tcx.hir_krate_attrs(), AttributeKind::RustcPreserveUbChecks); if !preserve_ub_checks { SimplifyUbCheck { tcx }.visit_body(body); } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8cf68b280850..f93d420fb06d 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -318,6 +318,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcParenSugar(..) | AttributeKind::RustcPassByValue (..) | AttributeKind::RustcPassIndirectlyInNonRusticAbis(..) + | AttributeKind::RustcPreserveUbChecks | AttributeKind::RustcReallocator | AttributeKind::RustcScalableVector { .. } | AttributeKind::RustcShouldNotBeCalledOnConstItems(..) @@ -406,8 +407,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | sym::register_tool | sym::rustc_no_implicit_bounds | sym::test_runner - | sym::reexport_test_harness_main - | sym::rustc_preserve_ub_checks, + | sym::reexport_test_harness_main, .. ] => {} [name, rest@..] => { From adb3861f98f5991703160e7d8a5e67de3bcef903 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:18:00 +0200 Subject: [PATCH 250/319] sembr src/profiling/wpa-profiling.md --- .../src/profiling/wpa-profiling.md | 57 +++++++++++-------- 1 file changed, 33 insertions(+), 24 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index 2267f9b490f7..eb829242e02f 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -3,13 +3,13 @@ ## Introducing WPR and WPA High-level performance analysis (including memory usage) can be performed with the Windows -Performance Recorder (WPR) and Windows Performance Analyzer (WPA). As the names suggest, WPR is for -recording system statistics (in the form of event trace log a.k.a. ETL files), while WPA is for -analyzing these ETL files. +Performance Recorder (WPR) and Windows Performance Analyzer (WPA). +As the names suggest, WPR is for recording system statistics (in the form of event trace log a.k.a. +ETL files), while WPA is for analyzing these ETL files. WPR collects system wide statistics, so it won't just record things relevant to rustc but also -everything else that's running on the machine. During analysis, we can filter to just the things we -find interesting. +everything else that's running on the machine. +During analysis, we can filter to just the things we find interesting. These tools are quite powerful but also require a bit of learning before we can successfully profile the Rust compiler. @@ -21,36 +21,42 @@ specifically designed to make analyzing rustc easier. ### Installing WPR and WPA You can install WPR and WPA as part of the Windows Performance Toolkit which itself is an option as -part of downloading the Windows Assessment and Deployment Kit (ADK). You can download the ADK -installer [here](https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install). Make -sure to select the Windows Performance Toolkit (you don't need to select anything else). +part of downloading the Windows Assessment and Deployment Kit (ADK). +You can download the ADK +installer [here](https://learn.microsoft.com/en-us/windows-hardware/get-started/adk-install). +Make sure to select the Windows Performance Toolkit (you don't need to select anything else). ## Recording -In order to perform system analysis, you'll first need to record your system with WPR. Open WPR and -at the bottom of the window select the "profiles" of the things you want to record. For looking +In order to perform system analysis, you'll first need to record your system with WPR. +Open WPR and at the bottom of the window select the "profiles" of the things you want to record. +For looking into memory usage of the rustc bootstrap process, we'll want to select the following items: * CPU usage * VirtualAlloc usage You might be tempted to record "Heap usage" as well, but this records every single heap allocation -and can be very, very expensive. For high-level analysis, it might be best to leave that turned -off. +and can be very, very expensive. +For high-level analysis, it might be best to leave that turned off. -Now we need to get our setup ready to record. For memory usage analysis, it is best to record the -stage 2 compiler build with a stage 1 compiler build with debug symbols. Having symbols in the +Now we need to get our setup ready to record. +For memory usage analysis, it is best to record the +stage 2 compiler build with a stage 1 compiler build with debug symbols. +Having symbols in the compiler we're using to build rustc will aid our analysis greatly by allowing WPA to resolve Rust -symbols correctly. Unfortunately, the stage 0 compiler does not have symbols turned on which is why +symbols correctly. +Unfortunately, the stage 0 compiler does not have symbols turned on which is why we'll need to build a stage 1 compiler and then a stage 2 compiler ourselves. -To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. This tells -rustc to generate debug information which includes stack frames when bootstrapping. +To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. +This tells rustc to generate debug information which includes stack frames when bootstrapping. Now you can build the stage 1 compiler: `x build --stage 1 -i library` or however else you want to build the stage 1 compiler. -Now that the stage 1 compiler is built, we can record the stage 2 build. Go back to WPR, click the +Now that the stage 1 compiler is built, we can record the stage 2 build. +Go back to WPR, click the "start" button and build the stage 2 compiler (e.g., `x build --stage=2 -i library`). When this process finishes, stop the recording. @@ -61,8 +67,10 @@ appears. ## Analysis -Now that our ETL file is open in WPA, we can analyze the results. First, we'll want to apply the -pre-made "profile" which will put WPA into a state conducive to analyzing rustc bootstrap. Download +Now that our ETL file is open in WPA, we can analyze the results. +First, we'll want to apply the +pre-made "profile" which will put WPA into a state conducive to analyzing rustc bootstrap. +Download the profile [here](https://github.com/wesleywiser/rustc-bootstrap-wpa-analysis/releases/download/1/rustc.generic.wpaProfile). Select the "Profiles" menu at the top, then "apply" and then choose the downloaded profile. @@ -71,8 +79,9 @@ You should see something resembling the following: ![WPA with profile applied](../img/wpa-initial-memory.png) Next, we will need to tell WPA to load and process debug symbols so that it can properly demangle -the Rust stack traces. To do this, click "Trace" and then choose "Load Symbols". This step can take -a while. +the Rust stack traces. +To do this, click "Trace" and then choose "Load Symbols". +This step can take a while. Once WPA has loaded symbols for rustc, we can expand the rustc.exe node and begin drilling down into the stack with the largest allocations. @@ -81,8 +90,8 @@ To do that, we'll expand the `[Root]` node in the "Commit Stack" column and cont until we find interesting stack frames. > Tip: After selecting the node you want to expand, press the right arrow key. This will expand the -node and put the selection on the next largest node in the expanded set. You can continue pressing -the right arrow key until you reach an interesting frame. +node and put the selection on the next largest node in the expanded set. +You can continue pressing the right arrow key until you reach an interesting frame. ![WPA with expanded stack](../img/wpa-stack.png) From f59931384687c4a9bf14fca54f5af4f0e5083150 Mon Sep 17 00:00:00 2001 From: Oscar Bray Date: Mon, 2 Feb 2026 21:19:50 +0000 Subject: [PATCH 251/319] Make parser naming consistent for dump parsers. --- .../src/attributes/rustc_dump.rs | 20 +++++++++---------- compiler/rustc_attr_parsing/src/context.rs | 14 ++++++------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs index 53120dece916..71a8fb0dd47d 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_dump.rs @@ -7,36 +7,36 @@ use crate::attributes::{NoArgsAttributeParser, OnDuplicate}; use crate::context::Stage; use crate::target_checking::AllowedTargets; -pub(crate) struct RustcDumpUserArgs; +pub(crate) struct RustcDumpUserArgsParser; -impl NoArgsAttributeParser for RustcDumpUserArgs { +impl NoArgsAttributeParser for RustcDumpUserArgsParser { const PATH: &[Symbol] = &[sym::rustc_dump_user_args]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpUserArgs; } -pub(crate) struct RustcDumpDefParents; +pub(crate) struct RustcDumpDefParentsParser; -impl NoArgsAttributeParser for RustcDumpDefParents { +impl NoArgsAttributeParser for RustcDumpDefParentsParser { const PATH: &[Symbol] = &[sym::rustc_dump_def_parents]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Fn)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpDefParents; } -pub(crate) struct RustcDumpItemBounds; +pub(crate) struct RustcDumpItemBoundsParser; -impl NoArgsAttributeParser for RustcDumpItemBounds { +impl NoArgsAttributeParser for RustcDumpItemBoundsParser { const PATH: &[Symbol] = &[sym::rustc_dump_item_bounds]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::AssocTy)]); const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpItemBounds; } -pub(crate) struct RustcDumpPredicates; +pub(crate) struct RustcDumpPredicatesParser; -impl NoArgsAttributeParser for RustcDumpPredicates { +impl NoArgsAttributeParser for RustcDumpPredicatesParser { const PATH: &[Symbol] = &[sym::rustc_dump_predicates]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ @@ -49,9 +49,9 @@ impl NoArgsAttributeParser for RustcDumpPredicates { const CREATE: fn(Span) -> AttributeKind = |_| AttributeKind::RustcDumpPredicates; } -pub(crate) struct RustcDumpVtable; +pub(crate) struct RustcDumpVtableParser; -impl NoArgsAttributeParser for RustcDumpVtable { +impl NoArgsAttributeParser for RustcDumpVtableParser { const PATH: &[Symbol] = &[sym::rustc_dump_vtable]; const ON_DUPLICATE: OnDuplicate = OnDuplicate::Error; const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 736eb704b737..451638725c52 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -72,8 +72,8 @@ use crate::attributes::rustc_allocator::{ RustcDeallocatorParser, RustcReallocatorParser, }; use crate::attributes::rustc_dump::{ - RustcDumpDefParents, RustcDumpItemBounds, RustcDumpPredicates, RustcDumpUserArgs, - RustcDumpVtable, + RustcDumpDefParentsParser, RustcDumpItemBoundsParser, RustcDumpPredicatesParser, + RustcDumpUserArgsParser, RustcDumpVtableParser, }; use crate::attributes::rustc_internal::{ RustcHasIncoherentInherentImplsParser, RustcLayoutParser, RustcLayoutScalarValidRangeEndParser, @@ -294,11 +294,11 @@ attribute_parsers!( Single>, Single>, Single>, - Single>, - Single>, - Single>, - Single>, - Single>, + Single>, + Single>, + Single>, + Single>, + Single>, Single>, Single>, Single>, From 957f28c9b8c8fc1513c61657be836ceafc2eafe7 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:25:33 +0200 Subject: [PATCH 252/319] add some pauses --- src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index eb829242e02f..6d885d303629 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -46,8 +46,9 @@ stage 2 compiler build with a stage 1 compiler build with debug symbols. Having symbols in the compiler we're using to build rustc will aid our analysis greatly by allowing WPA to resolve Rust symbols correctly. -Unfortunately, the stage 0 compiler does not have symbols turned on which is why -we'll need to build a stage 1 compiler and then a stage 2 compiler ourselves. +Unfortunately, the stage 0 compiler does not have symbols turned on, +which is why we'll need to build a stage 1 compiler, +and then a stage 2 compiler ourselves. To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. This tells rustc to generate debug information which includes stack frames when bootstrapping. From f285d5d0601d0e08db06b6beda18925337829ada Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:27:22 +0200 Subject: [PATCH 253/319] provide the full path --- src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md index 6d885d303629..a0a5bee63e9e 100644 --- a/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling/wpa-profiling.md @@ -50,7 +50,7 @@ Unfortunately, the stage 0 compiler does not have symbols turned on, which is why we'll need to build a stage 1 compiler, and then a stage 2 compiler ourselves. -To do this, make sure you have set `debuginfo-level = 1` in your `bootstrap.toml` file. +To do this, make sure you have set `rust.debuginfo-level = 1` in your `bootstrap.toml` file. This tells rustc to generate debug information which includes stack frames when bootstrapping. Now you can build the stage 1 compiler: `x build --stage 1 -i library` or however From 6169f2e4849f35165793ab3f810b1f9fdea6a33e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:27:41 +0200 Subject: [PATCH 254/319] sembr src/effects.md --- src/doc/rustc-dev-guide/src/effects.md | 78 +++++++++++++------------- 1 file changed, 40 insertions(+), 38 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index 732ba7153116..a78428cf09eb 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -2,16 +2,17 @@ ## The `HostEffect` predicate -[`HostEffectPredicate`]s are a kind of predicate from `~const Tr` or `const Tr` -bounds. It has a trait reference, and a `constness` which could be `Maybe` or -`Const` depending on the bound. Because `~const Tr`, or rather `Maybe` bounds +[`HostEffectPredicate`]s are a kind of predicate from `~const Tr` or `const Tr` bounds. +It has a trait reference, and a `constness` which could be `Maybe` or +`Const` depending on the bound. +Because `~const Tr`, or rather `Maybe` bounds apply differently based on whichever contexts they are in, they have different -behavior than normal bounds. Where normal trait bounds on a function such as +behavior than normal bounds. +Where normal trait bounds on a function such as `T: Tr` are collected within the [`predicates_of`] query to be proven when a function is called and to be assumed within the function, bounds such as `T: ~const Tr` will behave as a normal trait bound and add `T: Tr` to the result -from `predicates_of`, but also adds a `HostEffectPredicate` to the -[`const_conditions`] query. +from `predicates_of`, but also adds a `HostEffectPredicate` to the [`const_conditions`] query. On the other hand, `T: const Tr` bounds do not change meaning across contexts, therefore they will result in `HostEffect(T: Tr, const)` being added to @@ -23,17 +24,17 @@ therefore they will result in `HostEffect(T: Tr, const)` being added to ## The `const_conditions` query -`predicates_of` represents a set of predicates that need to be proven to use an -item. For example, to use `foo` in the example below: +`predicates_of` represents a set of predicates that need to be proven to use an item. +For example, to use `foo` in the example below: ```rust fn foo() where T: Default {} ``` -We must be able to prove that `T` implements `Default`. In a similar vein, +We must be able to prove that `T` implements `Default`. +In a similar vein, `const_conditions` represents a set of predicates that need to be proven to use -an item *in const contexts*. If we adjust the example above to use `const` trait -bounds: +an item *in const contexts*. If we adjust the example above to use `const` trait bounds: ```rust const fn foo() where T: ~const Default {} @@ -45,13 +46,13 @@ prove that `T` has a const implementation of `Default`. ## Enforcement of `const_conditions` -`const_conditions` are currently checked in various places. +`const_conditions` are currently checked in various places. Every call in HIR from a const context (which includes `const fn` and `const` items) will check that `const_conditions` of the function we are calling hold. -This is done in [`FnCtxt::enforce_context_effects`]. Note that we don't check -if the function is only referred to but not called, as the following code needs -to compile: +This is done in [`FnCtxt::enforce_context_effects`]. +Note that we don't check +if the function is only referred to but not called, as the following code needs to compile: ```rust const fn hi() -> T { @@ -61,8 +62,8 @@ const X: fn() -> u32 = hi::; ``` For a trait `impl` to be well-formed, we must be able to prove the -`const_conditions` of the trait from the `impl`'s environment. This is checked -in [`wfcheck::check_impl`]. +`const_conditions` of the trait from the `impl`'s environment. +This is checked in [`wfcheck::check_impl`]. Here's an example: @@ -77,10 +78,11 @@ impl const Foo for () {} ``` Methods of trait impls must not have stricter bounds than the method of the -trait that they are implementing. To check that the methods are compatible, a +trait that they are implementing. +To check that the methods are compatible, a hybrid environment is constructed with the predicates of the `impl` plus the -predicates of the trait method, and we attempt to prove the predicates of the -impl method. We do the same for `const_conditions`: +predicates of the trait method, and we attempt to prove the predicates of the impl method. +We do the same for `const_conditions`: ```rust const trait Foo { @@ -95,10 +97,10 @@ impl Foo for Vec { } ``` -These checks are done in [`compare_method_predicate_entailment`]. A similar -function that does the same check for associated types is called -[`compare_type_predicate_entailment`]. Both of these need to consider -`const_conditions` when in const contexts. +These checks are done in [`compare_method_predicate_entailment`]. +A similar function that does the same check for associated types is called +[`compare_type_predicate_entailment`]. +Both of these need to consider `const_conditions` when in const contexts. In MIR, as part of const checking, `const_conditions` of items that are called are revalidated again in [`Checker::revalidate_conditional_constness`]. @@ -122,11 +124,12 @@ fn foo() -> impl ~const PartialEq { } ``` -Have their bounds represented differently. Unlike `const_conditions` which need +Have their bounds represented differently. +Unlike `const_conditions` which need to be proved for callers, and can be assumed inside the definition (e.g. trait bounds on functions), these bounds need to be proved at definition (at the impl, -or when returning the opaque) but can be assumed for callers. The non-const -equivalent of these bounds are called [`explicit_item_bounds`]. +or when returning the opaque) but can be assumed for callers. +The non-const equivalent of these bounds are called [`explicit_item_bounds`]. These bounds are checked in [`compare_impl_item::check_type_bounds`] for HIR typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and @@ -139,16 +142,14 @@ typeck, [`evaluate_host_effect_from_item_bounds`] in the old solver and ## Proving `HostEffectPredicate`s -`HostEffectPredicate`s are implemented both in the [old solver] and the [new -trait solver]. In general, we can prove a `HostEffect` predicate when either of -these conditions are met: +`HostEffectPredicate`s are implemented both in the [old solver] and the [new trait solver]. +In general, we can prove a `HostEffect` predicate when either of these conditions are met: * The predicate can be assumed from caller bounds; * The type has a `const` `impl` for the trait, *and* that const conditions on -the impl holds, *and* that the `explicit_implied_const_bounds` on the trait -holds; or -* The type has a built-in implementation for the trait in const contexts. For -example, `Fn` may be implemented by function items if their const conditions +the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or +* The type has a built-in implementation for the trait in const contexts. + For example, `Fn` may be implemented by function items if their const conditions are satisfied, or `Destruct` is implemented in const contexts if the type can be dropped at compile time. @@ -161,10 +162,11 @@ To be expanded later. ### The `#[rustc_non_const_trait_method]` attribute -This is intended for internal (standard library) usage only. With this attribute -applied to a trait method, the compiler will not check the default body of this -method for ability to run in compile time. Users of the trait will also not be -allowed to use this trait method in const contexts. This attribute is primarily +This is intended for internal (standard library) usage only. +With this attribute applied to a trait method, the compiler will not check the default body of this +method for ability to run in compile time. +Users of the trait will also not be allowed to use this trait method in const contexts. +This attribute is primarily used for constifying large traits such as `Iterator` without having to make all its methods `const` at the same time. From 5143080050451dc45f30668b788078bdba2f8fdc Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Mon, 2 Feb 2026 23:38:03 +0200 Subject: [PATCH 255/319] some improvements --- src/doc/rustc-dev-guide/src/effects.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/effects.md b/src/doc/rustc-dev-guide/src/effects.md index a78428cf09eb..26530a30d066 100644 --- a/src/doc/rustc-dev-guide/src/effects.md +++ b/src/doc/rustc-dev-guide/src/effects.md @@ -113,7 +113,9 @@ are revalidated again in [`Checker::revalidate_conditional_constness`]. ## `explicit_implied_const_bounds` on associated types and traits -Bounds on associated types, opaque types, and supertraits such as +Bounds on associated types, opaque types, and supertraits such as the following +have their bounds represented differently: + ```rust trait Foo: ~const PartialEq { type X: ~const PartialEq; @@ -124,9 +126,8 @@ fn foo() -> impl ~const PartialEq { } ``` -Have their bounds represented differently. -Unlike `const_conditions` which need -to be proved for callers, and can be assumed inside the definition (e.g. trait +Unlike `const_conditions`, which need to be proved for callers, +and can be assumed inside the definition (e.g. trait bounds on functions), these bounds need to be proved at definition (at the impl, or when returning the opaque) but can be assumed for callers. The non-const equivalent of these bounds are called [`explicit_item_bounds`]. @@ -147,11 +148,11 @@ In general, we can prove a `HostEffect` predicate when either of these condition * The predicate can be assumed from caller bounds; * The type has a `const` `impl` for the trait, *and* that const conditions on -the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or + the impl holds, *and* that the `explicit_implied_const_bounds` on the trait holds; or * The type has a built-in implementation for the trait in const contexts. For example, `Fn` may be implemented by function items if their const conditions -are satisfied, or `Destruct` is implemented in const contexts if the type can -be dropped at compile time. + are satisfied, or `Destruct` is implemented in const contexts if the type can + be dropped at compile time. [old solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_trait_selection/traits/effects.rs.html [new trait solver]: https://doc.rust-lang.org/nightly/nightly-rustc/src/rustc_next_trait_solver/solve/effect_goals.rs.html From a802e7c161a351fea0a35eca5c145361c9974234 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Mon, 2 Feb 2026 22:50:29 +0100 Subject: [PATCH 256/319] Remove `with_no_trimmed_paths` use in query macro --- compiler/rustc_macros/src/query.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 0cac699e5b62..0e2b16d72eb1 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -303,9 +303,7 @@ fn add_query_desc_cached_impl( #[allow(unused_variables)] pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::query::queries::#name::Key<'tcx>) -> String { let (#tcx, #key) = (tcx, key); - ::rustc_middle::ty::print::with_no_trimmed_paths!( - format!(#desc) - ) + format!(#desc) } }; From 06d17c0d3bd16286090f1db445e757875c7821c5 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:10:22 +0200 Subject: [PATCH 257/319] use convenient notation, as seen in bootstrap.example.toml --- .../rustc-dev-guide/src/backend/debugging.md | 7 +++---- .../src/backend/updating-llvm.md | 3 +-- .../src/building/compiler-documenting.md | 3 +-- .../src/building/how-to-build-and-run.md | 13 ++++++------ .../src/building/new-target.md | 12 +++-------- .../src/building/optimized-build.md | 21 +++++++------------ .../rustc-dev-guide/src/building/suggested.md | 2 +- .../rustc-dev-guide/src/compiler-debugging.md | 14 ++++++------- .../src/llvm-coverage-instrumentation.md | 6 ++---- src/doc/rustc-dev-guide/src/profiling.md | 11 +++++----- src/doc/rustc-dev-guide/src/sanitizers.md | 5 ++--- .../rustc-dev-guide/src/tests/compiletest.md | 4 +--- .../rustc-dev-guide/src/tests/directives.md | 2 +- src/doc/rustc-dev-guide/src/tests/docker.md | 2 +- src/doc/rustc-dev-guide/src/tracing.md | 2 +- 15 files changed, 42 insertions(+), 65 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 84efa72f4c46..319154f023cf 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -38,7 +38,7 @@ which means that LLVM assertion failures can show up as compiler crashes (not ICEs but "real" crashes) and other sorts of weird behavior. If you are encountering these, it is a good idea to try using a compiler with LLVM assertions enabled - either an "alt" nightly or a compiler you build yourself -by setting `[llvm] assertions=true` in your bootstrap.toml - and see whether +by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. The rustc build process builds the LLVM tools into @@ -162,12 +162,11 @@ aware of its [internal debug infrastructure][llvm-debug]. This is provided in LLVM Debug builds, which you enable for rustc LLVM builds by changing this setting in the bootstrap.toml: ``` -[llvm] # Indicates whether the LLVM assertions are enabled or not -assertions = true +llvm.assertions = true # Indicates whether the LLVM build is a Release or Debug build -optimize = false +llvm.optimize = false ``` The quick summary is: * Setting `assertions=true` enables coarse-grain debug messaging. diff --git a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md index 70deeebf8bec..5962791f64b1 100644 --- a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md +++ b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md @@ -145,8 +145,7 @@ so let's go through each in detail. This is done by having the following setting in `bootstrap.toml`: ```toml - [llvm] - download-ci-llvm = false + llvm.download-ci-llvm = false ``` 1. Test for regressions across other platforms. LLVM often has at least one bug diff --git a/src/doc/rustc-dev-guide/src/building/compiler-documenting.md b/src/doc/rustc-dev-guide/src/building/compiler-documenting.md index b031462ea15f..026a42907493 100644 --- a/src/doc/rustc-dev-guide/src/building/compiler-documenting.md +++ b/src/doc/rustc-dev-guide/src/building/compiler-documenting.md @@ -39,8 +39,7 @@ like the standard library (std) or the compiler (rustc). To create it by default with `x doc`, modify `bootstrap.toml`: ```toml - [build] - compiler-docs = true + build.compiler-docs = true ``` Note that when enabled, diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index d0a38b12c550..3e44a75ad732 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -10,7 +10,7 @@ case, `./x {check,build} library/std` should still work. In the short-term, you may need to disable `download-rustc` for `./x test library/std`. This can be done either by: 1. `./x test library/std --set rust.download-rustc=false` -2. Or set `rust.download-rustc=false` in `bootstrap.toml`. +2. Or set `rust.download-rustc = false` in `bootstrap.toml`. Unfortunately that will require building the stage 1 compiler. The bootstrap team is working on this, but implementing a maintainable fix is taking some time. @@ -278,9 +278,9 @@ Once you have successfully built `rustc`, you will have created a bunch of files in your `build` directory. In order to actually run the resulting `rustc`, we recommend creating rustup toolchains. The first command listed below creates the stage1 toolchain, which was built in the -steps above, with the name `stage1`. The second command creates the stage2 -toolchain using the stage1 compiler. This will be needed in the future -if running the entire test suite, but will not be built in this page. +steps above, with the name `stage1`. The second command creates the stage2 +toolchain using the stage1 compiler. This will be needed in the future +if running the entire test suite, but will not be built in this page. Building stage2 is done with the same `./x build` command as for stage1, specifying that the stage is 2 instead. @@ -289,7 +289,7 @@ rustup toolchain link stage1 build/host/stage1 rustup toolchain link stage2 build/host/stage2 ``` -Now you can run the `rustc` you built with via the toolchain. If you run with +Now you can run the `rustc` you built with via the toolchain. If you run with `-vV`, you should see a version number ending in `-dev`, indicating a build from your local environment: @@ -342,8 +342,7 @@ If you want to always build for other targets without needing to pass flags to ` you can configure this in the `[build]` section of your `bootstrap.toml` like so: ```toml -[build] -target = ["x86_64-unknown-linux-gnu", "wasm32-wasip1"] +build.target = ["x86_64-unknown-linux-gnu", "wasm32-wasip1"] ``` Note that building for some targets requires having external dependencies installed diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index d0ed787f520a..1be83e4a5a58 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -52,7 +52,7 @@ own preinstalled LLVM, you will need to provide `FileCheck` in some other way. On Debian-based systems, you can install the `llvm-N-tools` package (where `N` is the LLVM version number, e.g. `llvm-8-tools`). Alternately, you can specify the path to `FileCheck` with the `llvm-filecheck` config item in `bootstrap.toml` -or you can disable codegen test with the `codegen-tests` item in `bootstrap.toml`. +or you can disable codegen test with the `rust.codegen-tests` item in `bootstrap.toml`. ## Creating a target specification @@ -118,16 +118,10 @@ After this, run `cargo update -p libc` to update the lockfiles. Beware that if you patch to a local `path` dependency, this will enable warnings for that dependency. Some dependencies are not warning-free, and due -to the `deny-warnings` setting in `bootstrap.toml`, the build may suddenly start to fail. +to the `rust.deny-warnings` setting in `bootstrap.toml`, the build may suddenly start to fail. To work around warnings, you may want to: - Modify the dependency to remove the warnings -- Or for local development purposes, suppress the warnings by setting deny-warnings = false in bootstrap.toml. - -```toml -# bootstrap.toml -[rust] -deny-warnings = false -``` +- Or for local development purposes, suppress the warnings by setting `rust.deny-warnings = false` in bootstrap.toml. [`libc`]: https://crates.io/crates/libc [`cc`]: https://crates.io/crates/cc diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 46def66d1782..d11bfaa4ba00 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -14,8 +14,7 @@ enable (Thin-)LTO when building `rustc`, set the `rust.lto` config option to `"t in `bootstrap.toml`: ```toml -[rust] -lto = "thin" +rust.lto = "thin" ``` > Note that LTO for `rustc` is currently supported and tested only for @@ -35,8 +34,7 @@ want to enable the `jemalloc` allocator, you can set the `rust.jemalloc` option in `bootstrap.toml`: ```toml -[rust] -jemalloc = true +rust.jemalloc = true ``` > Note that this option is currently only supported for Linux and macOS targets. @@ -48,9 +46,8 @@ You can modify the number of codegen units for `rustc` and `libstd` in `bootstra following options: ```toml -[rust] -codegen-units = 1 -codegen-units-std = 1 +rust.codegen-units = 1 +rust.codegen-units-std = 1 ``` ## Instruction set @@ -68,9 +65,8 @@ If you also want to compile LLVM for a specific instruction set, you can set `ll in `bootstrap.toml`: ```toml -[llvm] -cxxflags = "-march=x86-64-v3" -cflags = "-march=x86-64-v3" +llvm.cxxflags = "-march=x86-64-v3" +llvm.cflags = "-march=x86-64-v3" ``` ## Profile-guided optimization @@ -108,9 +104,8 @@ like Python or LLVM. Here is an example of how can `opt-dist` be used locally (outside of CI): 1. Enable metrics in your `bootstrap.toml` file, because `opt-dist` expects it to be enabled: - ```toml - [build] - metrics = true + ```toml + build.metrics = true ``` 2. Build the tool with the following command: ```bash diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index 5eb214fac904..af4dc0ab699c 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -441,7 +441,7 @@ ln -s ./src/tools/nix-dev-shell/envrc-shell ./.envrc # Use nix-shell ### Note Note that when using nix on a not-NixOS distribution, it may be necessary to set -**`patch-binaries-for-nix = true` in `bootstrap.toml`**. Bootstrap tries to detect +**`build.patch-binaries-for-nix = true` in `bootstrap.toml`**. Bootstrap tries to detect whether it's running in nix and enable patching automatically, but this detection can have false negatives. diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 1944bf53378d..7b67c262c456 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -9,18 +9,17 @@ chapter](./backend/debugging.md)). ## Configuring the compiler By default, rustc is built without most debug information. To enable debug info, -set `debug = true` in your bootstrap.toml. +set `rust.debug = true` in your bootstrap.toml. -Setting `debug = true` turns on many different debug options (e.g., `debug-assertions`, +Setting `rust.debug = true` turns on many different debug options (e.g., `debug-assertions`, `debug-logging`, etc.) which can be individually tweaked if you want to, but many people -simply set `debug = true`. +simply set `rust.debug = true`. If you want to use GDB to debug rustc, please set `bootstrap.toml` with options: ```toml -[rust] -debug = true -debuginfo-level = 2 +rust.debug = true +rust.debuginfo-level = 2 ``` > NOTE: @@ -36,8 +35,7 @@ This requires at least GDB v10.2, otherwise you need to disable new symbol-mangling-version in `bootstrap.toml`. ```toml -[rust] -new-symbol-mangling = false +rust.new-symbol-mangling = false ``` > See the comments in `bootstrap.example.toml` for more info. diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index d71e51d5f61b..fbfcc4198de6 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -47,15 +47,13 @@ mandatory. # These assertions can detect malformed coverage mappings in some cases. profile = "codegen" -[build] # IMPORTANT: This tells the build system to build the LLVM profiler runtime. # Without it, the compiler can't produce coverage-instrumented binaries, # and many of the coverage tests will be skipped. -profiler = true +build.profiler = true -[rust] # Enable debug assertions in the compiler. -debug-assertions = true +rust.debug-assertions = true ``` ## Rust symbol mangling diff --git a/src/doc/rustc-dev-guide/src/profiling.md b/src/doc/rustc-dev-guide/src/profiling.md index 519d9b5488cb..06ebb8998342 100644 --- a/src/doc/rustc-dev-guide/src/profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling.md @@ -89,22 +89,21 @@ Since this doesn't seem to work with incremental compilation or `./x check`, you will be compiling rustc _a lot_. I recommend changing a few settings in `bootstrap.toml` to make it bearable: ``` -[rust] # A debug build takes _a third_ as long on my machine, # but compiling more than stage0 rustc becomes unbearably slow. -optimize = false +rust.optimize = false # We can't use incremental anyway, so we disable it for a little speed boost. -incremental = false +rust.incremental = false # We won't be running it, so no point in compiling debug checks. -debug = false +rust.debug = false # Using a single codegen unit gives less output, but is slower to compile. -codegen-units = 0 # num_cpus +rust.codegen-units = 0 # num_cpus ``` The llvm-lines output is affected by several options. -`optimize = false` increases it from 2.1GB to 3.5GB and `codegen-units = 0` to 4.1GB. +`rust.optimize = false` increases it from 2.1GB to 3.5GB and `codegen-units = 0` to 4.1GB. MIR optimizations have little impact. Compared to the default `RUSTFLAGS="-Z mir-opt-level=1"`, level 0 adds 0.3GB and level 2 removes 0.2GB. diff --git a/src/doc/rustc-dev-guide/src/sanitizers.md b/src/doc/rustc-dev-guide/src/sanitizers.md index 673d650e6050..65e1c0c70505 100644 --- a/src/doc/rustc-dev-guide/src/sanitizers.md +++ b/src/doc/rustc-dev-guide/src/sanitizers.md @@ -36,8 +36,7 @@ Highlight of the most important aspects of the implementation: when enabled in `bootstrap.toml`: ```toml - [build] - sanitizers = true + build.sanitizers = true ``` The runtimes are [placed into target libdir][sanitizer-copy]. @@ -84,7 +83,7 @@ Sanitizers are validated by code generation tests in [`tests/ui/sanitizer/`][test-ui] directory. Testing sanitizer functionality requires the sanitizer runtimes (built when -`sanitizer = true` in `bootstrap.toml`) and target providing support for particular a sanitizer. +`build.sanitizer = true` in `bootstrap.toml`) and target providing support for particular a sanitizer. When a sanitizer is unsupported on a given target, sanitizer tests will be ignored. This behaviour is controlled by compiletest `needs-sanitizer-*` directives. diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 5c4dfb6e0dd1..d7372de11ff8 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -570,9 +570,7 @@ Instrumented binaries need to be linked against the LLVM profiler runtime, so is enabled in `bootstrap.toml`: ```toml -# bootstrap.toml -[build] -profiler = true +build.profiler = true ``` This also means that they typically don't run in PR CI jobs, though they do run diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 08371a779e11..7034420e9be0 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -166,7 +166,7 @@ The following directives will check rustc build settings and target settings: - `needs-profiler-runtime` — ignores the test if the profiler runtime was not enabled for the target (`build.profiler = true` in rustc's `bootstrap.toml`) - `needs-sanitizer-support` — ignores if the sanitizer support was not enabled - for the target (`sanitizers = true` in rustc's `bootstrap.toml`) + for the target (`build.sanitizers = true` in rustc's `bootstrap.toml`) - `needs-sanitizer-{address,hwaddress,leak,memory,thread}` — ignores if the corresponding sanitizer is not enabled for the target (AddressSanitizer, hardware-assisted AddressSanitizer, LeakSanitizer, MemorySanitizer or diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index af8e0c36d050..436aa13545eb 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -21,7 +21,7 @@ The [`src/ci/docker/run.sh`] script is used to build a specific Docker image, ru build Rust within the image, and either run tests or prepare a set of archives designed for distribution. The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. All the compiler artifacts will be stored in the `obj` directory. The shell will start out in the `obj`directory. From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. You can run `src/ci/docker/run.sh ` directly. A few important notes regarding the `run.sh` script: -- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). +- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). - `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). - If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. - If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 28c0bcc737ca..323f81dcee04 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -194,7 +194,7 @@ calls to `debug!` and `trace!` are only included in the program if `rust.debug-logging=true` is turned on in bootstrap.toml (it is turned off by default), so if you don't see `DEBUG` logs, especially if you run the compiler with `RUSTC_LOG=rustc rustc some.rs` and only see -`INFO` logs, make sure that `debug-logging=true` is turned on in your bootstrap.toml. +`INFO` logs, make sure that `rust.debug-logging=true` is turned on in your bootstrap.toml. ## Logging etiquette and conventions From ed50cc909ad5e8dc1a8cdf0b16aa805028286ebf Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:10:50 +0200 Subject: [PATCH 258/319] I do not expect there will be any confusion --- src/doc/rustc-dev-guide/src/tests/directives.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index 7034420e9be0..ac76a2e15d9a 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -164,9 +164,9 @@ The following directives will check rustc build settings and target settings: via `--target`, use `needs-llvm-components` instead to ensure the appropriate backend is available. - `needs-profiler-runtime` — ignores the test if the profiler runtime was not - enabled for the target (`build.profiler = true` in rustc's `bootstrap.toml`) + enabled for the target (`build.profiler = true` in `bootstrap.toml`) - `needs-sanitizer-support` — ignores if the sanitizer support was not enabled - for the target (`build.sanitizers = true` in rustc's `bootstrap.toml`) + for the target (`build.sanitizers = true` in `bootstrap.toml`) - `needs-sanitizer-{address,hwaddress,leak,memory,thread}` — ignores if the corresponding sanitizer is not enabled for the target (AddressSanitizer, hardware-assisted AddressSanitizer, LeakSanitizer, MemorySanitizer or From ca24637a66fd274087d2393176cf30bd1c1c4943 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:12:31 +0200 Subject: [PATCH 259/319] sembr src/backend/debugging.md --- .../rustc-dev-guide/src/backend/debugging.md | 93 ++++++++++--------- 1 file changed, 48 insertions(+), 45 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 319154f023cf..2f26181abfca 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -6,16 +6,16 @@ [codegen]: ./codegen.md This section is about debugging compiler bugs in code generation (e.g. why the -compiler generated some piece of code or crashed in LLVM). LLVM is a big -project on its own that probably needs to have its own debugging document (not -that I could find one). But here are some tips that are important in a rustc -context: +compiler generated some piece of code or crashed in LLVM). + LLVM is a big project on its own that probably needs to have its own debugging document (not +that I could find one). +But here are some tips that are important in a rustc context: ### Minimize the example As a general rule, compilers generate lots of information from analyzing code. -Thus, a useful first step is usually to find a minimal example. One way to do -this is to +Thus, a useful first step is usually to find a minimal example. +One way to do this is to 1. create a new crate that reproduces the issue (e.g. adding whatever crate is at fault as a dependency, and using it from there) @@ -35,14 +35,13 @@ For more discussion on methodology for steps 2 and 3 above, there is an The official compilers (including nightlies) have LLVM assertions disabled, which means that LLVM assertion failures can show up as compiler crashes (not -ICEs but "real" crashes) and other sorts of weird behavior. If you are -encountering these, it is a good idea to try using a compiler with LLVM +ICEs but "real" crashes) and other sorts of weird behavior. +If you are encountering these, it is a good idea to try using a compiler with LLVM assertions enabled - either an "alt" nightly or a compiler you build yourself -by setting `llvm.assertions = true` in your bootstrap.toml - and see whether -anything turns up. +by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. -The rustc build process builds the LLVM tools into -`./build//llvm/bin`. They can be called directly. +The rustc build process builds the LLVM tools into `./build//llvm/bin`. +They can be called directly. These tools include: * [`llc`], which compiles bitcode (`.bc` files) to executable code; this can be used to replicate LLVM backend bugs. @@ -55,7 +54,8 @@ These tools include: [`bugpoint`]: https://llvm.org/docs/Bugpoint.html By default, the Rust build system does not check for changes to the LLVM source code or -its build configuration settings. So, if you need to rebuild the LLVM that is linked +its build configuration settings. +So, if you need to rebuild the LLVM that is linked into `rustc`, first delete the file `.llvm-stamp`, which should be located in `build//llvm/`. @@ -66,26 +66,28 @@ disappear), passing `-C codegen-units=1` to rustc will make debugging easier. ### Get your hands on raw LLVM input -For rustc to generate LLVM IR, you need to pass the `--emit=llvm-ir` flag. If +For rustc to generate LLVM IR, you need to pass the `--emit=llvm-ir` flag. +If you are building via cargo, use the `RUSTFLAGS` environment variable (e.g. -`RUSTFLAGS='--emit=llvm-ir'`). This causes rustc to spit out LLVM IR into the -target directory. +`RUSTFLAGS='--emit=llvm-ir'`). +This causes rustc to spit out LLVM IR into the target directory. -`cargo llvm-ir [options] path` spits out the LLVM IR for a particular function -at `path`. (`cargo install cargo-asm` installs `cargo asm` and `cargo -llvm-ir`). `--build-type=debug` emits code for debug builds. There are also -other useful options. Also, debug info in LLVM IR can clutter the output a lot: +`cargo llvm-ir [options] path` spits out the LLVM IR for a particular function at `path`. +(`cargo install cargo-asm` installs `cargo asm` and `cargo llvm-ir`). +`--build-type=debug` emits code for debug builds. +There are also other useful options. +Also, debug info in LLVM IR can clutter the output a lot: `RUSTFLAGS="-C debuginfo=0"` is really useful. `RUSTFLAGS="-C save-temps"` outputs LLVM bitcode at -different stages during compilation, which is sometimes useful. The output LLVM -bitcode will be in `.bc` files in the compiler's output directory, set via the +different stages during compilation, which is sometimes useful. +The output LLVM bitcode will be in `.bc` files in the compiler's output directory, set via the `--out-dir DIR` argument to `rustc`. * If you are hitting an assertion failure or segmentation fault from the LLVM backend when invoking `rustc` itself, it is a good idea to try passing each - of these `.bc` files to the `llc` command, and see if you get the same - failure. (LLVM developers often prefer a bug reduced to a `.bc` file over one + of these `.bc` files to the `llc` command, and see if you get the same failure. + (LLVM developers often prefer a bug reduced to a `.bc` file over one that uses a Rust crate for its minimized reproduction.) * To get human readable versions of the LLVM bitcode, one just needs to convert @@ -112,8 +114,8 @@ llvm-args='-filter-print-funcs=EXACT_FUNCTION_NAME` (e.g. `-C llvm-args='-filter-print-funcs=_ZN11collections3str21_$LT$impl$u20$str$GT$\ 7replace17hbe10ea2e7c809b0bE'`). -That produces a lot of output into standard error, so you'll want to pipe that -to some file. Also, if you are using neither `-filter-print-funcs` nor `-C +That produces a lot of output into standard error, so you'll want to pipe that to some file. +Also, if you are using neither `-filter-print-funcs` nor `-C codegen-units=1`, then, because the multiple codegen units run in parallel, the printouts will mix together and you won't be able to read anything. @@ -125,8 +127,8 @@ printouts will mix together and you won't be able to read anything. * Within LLVM itself, calling `F.getParent()->dump()` at the beginning of `SafeStackLegacyPass::runOnFunction` will dump the whole module, which - may provide better basis for reproduction. (However, you - should be able to get that same dump from the `.bc` files dumped by + may provide better basis for reproduction. + (However, you should be able to get that same dump from the `.bc` files dumped by `-C save-temps`.) If you want just the IR for a specific function (say, you want to see why it @@ -145,10 +147,11 @@ $ ./build/$TRIPLE/llvm/bin/llvm-extract \ If you are seeing incorrect behavior due to an optimization pass, a very handy LLVM option is `-opt-bisect-limit`, which takes an integer denoting the index -value of the highest pass to run. Index values for taken passes are stable +value of the highest pass to run. + Index values for taken passes are stable from run to run; by coupling this with software that automates bisecting the -search space based on the resulting program, an errant pass can be quickly -determined. When an `-opt-bisect-limit` is specified, all runs are displayed +search space based on the resulting program, an errant pass can be quickly determined. + When an `-opt-bisect-limit` is specified, all runs are displayed to standard error, along with their index and output indicating if the pass was run or skipped. Setting the limit to an index of -1 (e.g., `RUSTFLAGS="-C llvm-args=-opt-bisect-limit=-1"`) will show all passes and @@ -189,8 +192,8 @@ specifically the `#t-compiler/wg-llvm` channel. ### Compiler options to know and love The `-C help` and `-Z help` compiler switches will list out a variety -of interesting options you may find useful. Here are a few of the most -common that pertain to LLVM development (some of them are employed in the +of interesting options you may find useful. +Here are a few of the most common that pertain to LLVM development (some of them are employed in the tutorial above): - The `--emit llvm-ir` option emits a `.ll` file with LLVM IR in textual format @@ -200,7 +203,8 @@ tutorial above): e.g. `-C llvm-args=-print-before-all` to print IR before every LLVM pass. - The `-C no-prepopulate-passes` will avoid pre-populate the LLVM pass - manager with a list of passes. This will allow you to view the LLVM + manager with a list of passes. + This will allow you to view the LLVM IR that rustc generates, not the LLVM IR after optimizations. - The `-C passes=val` option allows you to supply a space separated list of extra LLVM passes to run - The `-C save-temps` option saves all temporary output files during compilation @@ -210,18 +214,17 @@ tutorial above): - The `-Z no-parallel-backend` will disable parallel compilation of distinct compilation units - The `-Z llvm-time-trace` option will output a Chrome profiler compatible JSON file which contains details and timings for LLVM passes. -- The `-C llvm-args=-opt-bisect-limit=` option allows for bisecting LLVM - optimizations. +- The `-C llvm-args=-opt-bisect-limit=` option allows for bisecting LLVM optimizations. ### Filing LLVM bug reports When filing an LLVM bug report, you will probably want some sort of minimal -working example that demonstrates the problem. The Godbolt compiler explorer is -really helpful for this. +working example that demonstrates the problem. +The Godbolt compiler explorer is really helpful for this. 1. Once you have some LLVM IR for the problematic code (see above), you can -create a minimal working example with Godbolt. Go to -[llvm.godbolt.org](https://llvm.godbolt.org). +create a minimal working example with Godbolt. +Go to [llvm.godbolt.org](https://llvm.godbolt.org). 2. Choose `LLVM-IR` as programming language. @@ -229,8 +232,7 @@ create a minimal working example with Godbolt. Go to - There are some useful flags: `-mattr` enables target features, `-march=` selects the target, `-mcpu=` selects the CPU, etc. - Commands like `llc -march=help` output all architectures available, which - is useful because sometimes the Rust arch names and the LLVM names do not - match. + is useful because sometimes the Rust arch names and the LLVM names do not match. - If you have compiled rustc yourself somewhere, in the target directory you have binaries for `llc`, `opt`, etc. @@ -238,7 +240,8 @@ create a minimal working example with Godbolt. Go to optimizations transform it. 5. Once you have a godbolt link demonstrating the issue, it is pretty easy to - fill in an LLVM bug. Just visit their [github issues page][llvm-issues]. + fill in an LLVM bug. + Just visit their [github issues page][llvm-issues]. [llvm-issues]: https://github.com/llvm/llvm-project/issues @@ -250,8 +253,8 @@ gotten the fix yet (or perhaps you are familiar enough with LLVM to fix it yours In that case, we can sometimes opt to port the fix for the bug directly to our own LLVM fork, so that rustc can use it more easily. -Our fork of LLVM is maintained in [rust-lang/llvm-project]. Once -you've landed the fix there, you'll also need to land a PR modifying +Our fork of LLVM is maintained in [rust-lang/llvm-project]. +Once you've landed the fix there, you'll also need to land a PR modifying our submodule commits -- ask around on Zulip for help. [rust-lang/llvm-project]: https://github.com/rust-lang/llvm-project/ From 4c958e731b9ee0aa3c21c3e0eca745fb04788d31 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:18:37 +0200 Subject: [PATCH 260/319] this is text with multiple authors --- src/doc/rustc-dev-guide/src/backend/debugging.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 2f26181abfca..f18154662e69 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -7,9 +7,8 @@ This section is about debugging compiler bugs in code generation (e.g. why the compiler generated some piece of code or crashed in LLVM). - LLVM is a big project on its own that probably needs to have its own debugging document (not -that I could find one). -But here are some tips that are important in a rustc context: +LLVM is a big project that probably needs to have its own debugging document, +but following are some tips that are important in a rustc context. ### Minimize the example From b658d1521f4fcc4ead01a85c94f23aa9054cee61 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:20:22 +0200 Subject: [PATCH 261/319] a symlink is now helpfully provided --- src/doc/rustc-dev-guide/src/backend/debugging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index f18154662e69..8f0d35f0c9da 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -39,7 +39,7 @@ If you are encountering these, it is a good idea to try using a compiler with LL assertions enabled - either an "alt" nightly or a compiler you build yourself by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. -The rustc build process builds the LLVM tools into `./build//llvm/bin`. +The rustc build process builds the LLVM tools into `./build/host/llvm/bin`. They can be called directly. These tools include: * [`llc`], which compiles bitcode (`.bc` files) to executable code; this can be used to @@ -56,7 +56,7 @@ By default, the Rust build system does not check for changes to the LLVM source its build configuration settings. So, if you need to rebuild the LLVM that is linked into `rustc`, first delete the file `.llvm-stamp`, which should be located -in `build//llvm/`. +in `build/host/llvm/`. The default rustc compilation pipeline has multiple codegen units, which is hard to replicate manually and means that LLVM is called multiple times in @@ -157,7 +157,7 @@ pass was run or skipped. Setting the limit to an index of -1 (e.g., their corresponding index values. If you want to play with the optimization pipeline, you can use the [`opt`] tool -from `./build//llvm/bin/` with the LLVM IR emitted by rustc. +from `./build/host/llvm/bin/` with the LLVM IR emitted by rustc. When investigating the implementation of LLVM itself, you should be aware of its [internal debug infrastructure][llvm-debug]. From c305590083a85118afca0c136eefb39b875daeb8 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:21:02 +0200 Subject: [PATCH 262/319] more clean --- src/doc/rustc-dev-guide/src/backend/debugging.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index 8f0d35f0c9da..c9d47181bde8 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -39,7 +39,7 @@ If you are encountering these, it is a good idea to try using a compiler with LL assertions enabled - either an "alt" nightly or a compiler you build yourself by setting `llvm.assertions = true` in your bootstrap.toml - and see whether anything turns up. -The rustc build process builds the LLVM tools into `./build/host/llvm/bin`. +The rustc build process builds the LLVM tools into `build/host/llvm/bin`. They can be called directly. These tools include: * [`llc`], which compiles bitcode (`.bc` files) to executable code; this can be used to @@ -101,7 +101,7 @@ you should: ```bash $ rustc +local my-file.rs --emit=llvm-ir -O -C no-prepopulate-passes \ -C codegen-units=1 -$ OPT=./build/$TRIPLE/llvm/bin/opt +$ OPT=build/$TRIPLE/llvm/bin/opt $ $OPT -S -O2 < my-file.ll > my ``` From b55c621216f62f7e0fb8183d0f19fa2f8ccadd31 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:22:44 +0200 Subject: [PATCH 263/319] reflow --- src/doc/rustc-dev-guide/src/backend/debugging.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index c9d47181bde8..d3e59993a86d 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -66,9 +66,8 @@ disappear), passing `-C codegen-units=1` to rustc will make debugging easier. ### Get your hands on raw LLVM input For rustc to generate LLVM IR, you need to pass the `--emit=llvm-ir` flag. -If -you are building via cargo, use the `RUSTFLAGS` environment variable (e.g. -`RUSTFLAGS='--emit=llvm-ir'`). +If you are building via cargo, +use the `RUSTFLAGS` environment variable (e.g. `RUSTFLAGS='--emit=llvm-ir'`). This causes rustc to spit out LLVM IR into the target directory. `cargo llvm-ir [options] path` spits out the LLVM IR for a particular function at `path`. From 616954b0bd1fbd5368b323317f1218f9ba62c6bd Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:25:41 +0200 Subject: [PATCH 264/319] whitespace --- src/doc/rustc-dev-guide/src/backend/debugging.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/debugging.md b/src/doc/rustc-dev-guide/src/backend/debugging.md index d3e59993a86d..eaa9e399a052 100644 --- a/src/doc/rustc-dev-guide/src/backend/debugging.md +++ b/src/doc/rustc-dev-guide/src/backend/debugging.md @@ -146,10 +146,10 @@ $ ./build/$TRIPLE/llvm/bin/llvm-extract \ If you are seeing incorrect behavior due to an optimization pass, a very handy LLVM option is `-opt-bisect-limit`, which takes an integer denoting the index value of the highest pass to run. - Index values for taken passes are stable +Index values for taken passes are stable from run to run; by coupling this with software that automates bisecting the search space based on the resulting program, an errant pass can be quickly determined. - When an `-opt-bisect-limit` is specified, all runs are displayed +When an `-opt-bisect-limit` is specified, all runs are displayed to standard error, along with their index and output indicating if the pass was run or skipped. Setting the limit to an index of -1 (e.g., `RUSTFLAGS="-C llvm-args=-opt-bisect-limit=-1"`) will show all passes and @@ -202,7 +202,7 @@ tutorial above): pass. - The `-C no-prepopulate-passes` will avoid pre-populate the LLVM pass manager with a list of passes. - This will allow you to view the LLVM + This will allow you to view the LLVM IR that rustc generates, not the LLVM IR after optimizations. - The `-C passes=val` option allows you to supply a space separated list of extra LLVM passes to run - The `-C save-temps` option saves all temporary output files during compilation From 9bafb7744aa82f0298a961fa8fb26aed75dde88f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:27:12 +0200 Subject: [PATCH 265/319] sembr src/backend/updating-llvm.md --- .../src/backend/updating-llvm.md | 77 +++++++++---------- 1 file changed, 35 insertions(+), 42 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md index 5962791f64b1..3d0e130b6aaa 100644 --- a/src/doc/rustc-dev-guide/src/backend/updating-llvm.md +++ b/src/doc/rustc-dev-guide/src/backend/updating-llvm.md @@ -3,16 +3,16 @@ Rust supports building against multiple LLVM versions: -* Tip-of-tree for the current LLVM development branch is usually supported - within a few days. PRs for such fixes are tagged with `llvm-main`. +* Tip-of-tree for the current LLVM development branch is usually supported within a few days. + PRs for such fixes are tagged with `llvm-main`. * The latest released major version is always supported. * The one or two preceding major versions are usually supported. By default, Rust uses its own fork in the [rust-lang/llvm-project repository]. This fork is based on a `release/$N.x` branch of the upstream project, where `$N` is either the latest released major version, or the current major version -in release candidate phase. The fork is never based on the `main` development -branch. +in release candidate phase. +The fork is never based on the `main` development branch. Our LLVM fork only accepts: @@ -32,16 +32,15 @@ There are three types of LLVM updates, with different procedures: ## Backports (upstream supported) While the current major LLVM version is supported upstream, fixes should be -backported upstream first, and the release branch then merged back into the -Rust fork. +backported upstream first, and the release branch then merged back into the Rust fork. 1. Make sure the bugfix is in upstream LLVM. -2. If this hasn't happened already, request a backport to the upstream release - branch. If you have LLVM commit access, follow the [backport process]. - Otherwise, open an issue requesting the backport. Continue once the - backport has been approved and merged. -3. Identify the branch that rustc is currently using. The `src/llvm-project` - submodule is always pinned to a branch of the +2. If this hasn't happened already, request a backport to the upstream release branch. + If you have LLVM commit access, follow the [backport process]. + Otherwise, open an issue requesting the backport. + Continue once the backport has been approved and merged. +3. Identify the branch that rustc is currently using. + The `src/llvm-project` submodule is always pinned to a branch of the [rust-lang/llvm-project repository]. 4. Fork the rust-lang/llvm-project repository. 5. Check out the appropriate branch (typically named `rustc/a.b-yyyy-mm-dd`). @@ -51,26 +50,23 @@ Rust fork. 7. Merge the `upstream/release/$N.x` branch. 8. Push this branch to your fork. 9. Send a Pull Request to rust-lang/llvm-project to the same branch as before. - Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR - description. + Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR description. 10. Wait for the PR to be merged. -11. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with - your bugfix. This can be done locally with `git submodule update --remote - src/llvm-project` typically. +11. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with your bugfix. + This can be done locally with `git submodule update --remote src/llvm-project` typically. 12. Wait for PR to be merged. -An example PR: -[#59089](https://github.com/rust-lang/rust/pull/59089) +An example PR: [#59089](https://github.com/rust-lang/rust/pull/59089) ## Backports (upstream not supported) -Upstream LLVM releases are only supported for two to three months after the -GA release. Once upstream backports are no longer accepted, changes should be +Upstream LLVM releases are only supported for two to three months after the GA release. +Once upstream backports are no longer accepted, changes should be cherry-picked directly to our fork. 1. Make sure the bugfix is in upstream LLVM. -2. Identify the branch that rustc is currently using. The `src/llvm-project` - submodule is always pinned to a branch of the +2. Identify the branch that rustc is currently using. + The `src/llvm-project` submodule is always pinned to a branch of the [rust-lang/llvm-project repository]. 3. Fork the rust-lang/llvm-project repository. 4. Check out the appropriate branch (typically named `rustc/a.b-yyyy-mm-dd`). @@ -80,16 +76,13 @@ cherry-picked directly to our fork. 6. Cherry-pick the relevant commit(s) using `git cherry-pick -x`. 7. Push this branch to your fork. 8. Send a Pull Request to rust-lang/llvm-project to the same branch as before. - Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR - description. + Be sure to reference the Rust and/or LLVM issue that you're fixing in the PR description. 9. Wait for the PR to be merged. -10. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with - your bugfix. This can be done locally with `git submodule update --remote - src/llvm-project` typically. +10. Send a PR to rust-lang/rust updating the `src/llvm-project` submodule with your bugfix. + This can be done locally with `git submodule update --remote src/llvm-project` typically. 11. Wait for PR to be merged. -An example PR: -[#59089](https://github.com/rust-lang/rust/pull/59089) +An example PR: [#59089](https://github.com/rust-lang/rust/pull/59089) ## New LLVM Release Updates @@ -110,14 +103,12 @@ so let's go through each in detail. 1. Create a new branch in the [rust-lang/llvm-project repository] from this `release/$N.x` branch, and name it `rustc/a.b-yyyy-mm-dd`, - where `a.b` is the current version number of LLVM in-tree - at the time of the branch, + where `a.b` is the current version number of LLVM in-tree at the time of the branch, and the remaining part is the current date. 1. Apply Rust-specific patches to the llvm-project repository. All features and bugfixes are upstream, - but there's often some weird build-related patches - that don't make sense to upstream. + but there's often some weird build-related patches that don't make sense to upstream. These patches are typically the latest patches in the rust-lang/llvm-project branch that rustc is currently using. @@ -148,9 +139,11 @@ so let's go through each in detail. llvm.download-ci-llvm = false ``` -1. Test for regressions across other platforms. LLVM often has at least one bug +1. Test for regressions across other platforms. + LLVM often has at least one bug for non-tier-1 architectures, so it's good to do some more testing before - sending this to bors! If you're low on resources you can send the PR as-is + sending this to bors! + If you're low on resources you can send the PR as-is now to bors, though, and it'll get tested anyway. Ideally, build LLVM and test it on a few platforms: @@ -167,9 +160,11 @@ so let's go through each in detail. * `./src/ci/docker/run.sh dist-various-2` * `./src/ci/docker/run.sh armhf-gnu` -1. Prepare a PR to `rust-lang/rust`. Work with maintainers of +1. Prepare a PR to `rust-lang/rust`. + Work with maintainers of `rust-lang/llvm-project` to get your commit in a branch of that repository, - and then you can send a PR to `rust-lang/rust`. You'll change at least + and then you can send a PR to `rust-lang/rust`. + You'll change at least `src/llvm-project` and will likely also change [`llvm-wrapper`] as well. @@ -191,14 +186,12 @@ so let's go through each in detail. We will often want to have those bug fixes as well. The merge process for that is to use `git merge` itself to merge LLVM's `release/a.b` branch with the branch created in step 2. - This is typically - done multiple times when necessary while LLVM's release branch is baking. + This is typically done multiple times when necessary while LLVM's release branch is baking. 1. LLVM then announces the release of version `a.b`. 1. After LLVM's official release, - we follow the process of creating a new branch on the - rust-lang/llvm-project repository again, + we follow the process of creating a new branch on the rust-lang/llvm-project repository again, this time with a new date. It is only then that the PR to update Rust to use that version is merged. From 299b429e966c4e8de65be80bb6b003c80a61348e Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:36:10 +0200 Subject: [PATCH 266/319] sembr src/building/how-to-build-and-run.md --- .../src/building/how-to-build-and-run.md | 139 ++++++++++-------- 1 file changed, 76 insertions(+), 63 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 3e44a75ad732..2c127245b810 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -4,22 +4,25 @@ For `profile = "library"` users, or users who use `download-rustc = true | "if-unchanged"`, please be advised that the `./x test library/std` flow where `download-rustc` is active (i.e. no compiler changes) is currently broken. -This is tracked in . Only the `./x test` flow is affected in this +This is tracked in . +Only the `./x test` flow is affected in this case, `./x {check,build} library/std` should still work. -In the short-term, you may need to disable `download-rustc` for `./x test library/std`. This can be done either by: +In the short-term, you may need to disable `download-rustc` for `./x test library/std`. +This can be done either by: 1. `./x test library/std --set rust.download-rustc=false` 2. Or set `rust.download-rustc = false` in `bootstrap.toml`. -Unfortunately that will require building the stage 1 compiler. The bootstrap team is working on this, but +Unfortunately that will require building the stage 1 compiler. +The bootstrap team is working on this, but implementing a maintainable fix is taking some time. -The compiler is built using a tool called `x.py`. You will need to -have Python installed to run it. +The compiler is built using a tool called `x.py`. +You will need to have Python installed to run it. ## Quick Start @@ -28,7 +31,8 @@ For a less in-depth quick-start of getting the compiler running, see [quickstart ## Get the source code -The main repository is [`rust-lang/rust`][repo]. This contains the compiler, +The main repository is [`rust-lang/rust`][repo]. +This contains the compiler, the standard library (including `core`, `alloc`, `test`, `proc_macro`, etc), and a bunch of tools (e.g. `rustdoc`, the bootstrapping infrastructure, etc). @@ -86,8 +90,8 @@ cd rust ## What is `x.py`? -`x.py` is the build tool for the `rust` repository. It can build docs, run tests, and compile the -compiler and standard library. +`x.py` is the build tool for the `rust` repository. +It can build docs, run tests, and compile the compiler and standard library. This chapter focuses on the basics to be productive, but if you want to learn more about `x.py`, [read this chapter][bootstrap]. @@ -103,11 +107,13 @@ Also, using `x` rather than `x.py` is recommended as: (You can find the platform related scripts around the `x.py`, like `x.ps1`) -Notice that this is not absolute. For instance, using Nushell in VSCode on Win10, -typing `x` or `./x` still opens `x.py` in an editor rather than invoking the program. :) +Notice that this is not absolute. +For instance, using Nushell in VSCode on Win10, +typing `x` or `./x` still opens `x.py` in an editor rather than invoking the program. +:) -In the rest of this guide, we use `x` rather than `x.py` directly. The following -command: +In the rest of this guide, we use `x` rather than `x.py` directly. +The following command: ```bash ./x check @@ -164,9 +170,10 @@ Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser #### Running `x.py` slightly more conveniently -There is a binary that wraps `x.py` called `x` in `src/tools/x`. All it does is -run `x.py`, but it can be installed system-wide and run from any subdirectory -of a checkout. It also looks up the appropriate version of `python` to use. +There is a binary that wraps `x.py` called `x` in `src/tools/x`. +All it does is run `x.py`, but it can be installed system-wide and run from any subdirectory +of a checkout. +It also looks up the appropriate version of `python` to use. You can install it with `cargo install --path src/tools/x`. @@ -177,18 +184,20 @@ shell to run the platform related scripts. ## Create a `bootstrap.toml` -To start, run `./x setup` and select the `compiler` defaults. This will do some initialization -and create a `bootstrap.toml` for you with reasonable defaults. If you use a different default (which +To start, run `./x setup` and select the `compiler` defaults. +This will do some initialization and create a `bootstrap.toml` for you with reasonable defaults. +If you use a different default (which you'll likely want to do if you want to contribute to an area of rust other than the compiler, such as rustdoc), make sure to read information about that default (located in `src/bootstrap/defaults`) as the build process may be different for other defaults. -Alternatively, you can write `bootstrap.toml` by hand. See `bootstrap.example.toml` for all the available -settings and explanations of them. See `src/bootstrap/defaults` for common settings to change. +Alternatively, you can write `bootstrap.toml` by hand. +See `bootstrap.example.toml` for all the available settings and explanations of them. +See `src/bootstrap/defaults` for common settings to change. If you have already built `rustc` and you change settings related to LLVM, then you may have to -execute `./x clean --all` for subsequent configuration changes to take effect. Note that `./x -clean` will not cause a rebuild of LLVM. +execute `./x clean --all` for subsequent configuration changes to take effect. +Note that `./x clean` will not cause a rebuild of LLVM. ## Common `x` commands @@ -202,28 +211,28 @@ working on `rustc`, `std`, `rustdoc`, and other tools. | `./x test` | Runs all tests | | `./x fmt` | Formats all code | -As written, these commands are reasonable starting points. However, there are -additional options and arguments for each of them that are worth learning for -serious development work. In particular, `./x build` and `./x test` -provide many ways to compile or test a subset of the code, which can save a lot -of time. +As written, these commands are reasonable starting points. +However, there are additional options and arguments for each of them that are worth learning for +serious development work. +In particular, `./x build` and `./x test` +provide many ways to compile or test a subset of the code, which can save a lot of time. Also, note that `x` supports all kinds of path suffixes for `compiler`, `library`, -and `src/tools` directories. So, you can simply run `x test tidy` instead of -`x test src/tools/tidy`. Or, `x build std` instead of `x build library/std`. +and `src/tools` directories. +So, you can simply run `x test tidy` instead of `x test src/tools/tidy`. +Or, `x build std` instead of `x build library/std`. [rust-analyzer]: suggested.html#configuring-rust-analyzer-for-rustc -See the chapters on -[testing](../tests/running.md) and [rustdoc](../rustdoc.md) for more details. +See the chapters on [testing](../tests/running.md) and [rustdoc](../rustdoc.md) for more details. ### Building the compiler Note that building will require a relatively large amount of storage space. You may want to have upwards of 10 or 15 gigabytes available to build the compiler. -Once you've created a `bootstrap.toml`, you are now ready to run -`x`. There are a lot of options here, but let's start with what is +Once you've created a `bootstrap.toml`, you are now ready to run `x`. +There are a lot of options here, but let's start with what is probably the best "go to" command for building a local compiler: ```console @@ -236,8 +245,7 @@ What this command does is: - Assemble a working stage1 sysroot, containing the stage1 compiler and stage1 standard libraries. This final product (stage1 compiler + libs built using that compiler) -is what you need to build other Rust programs (unless you use `#![no_std]` or -`#![no_core]`). +is what you need to build other Rust programs (unless you use `#![no_std]` or `#![no_core]`). You will probably find that building the stage1 `std` is a bottleneck for you, but fear not, there is a (hacky) workaround... @@ -245,12 +253,12 @@ see [the section on avoiding rebuilds for std][keep-stage]. [keep-stage]: ./suggested.md#faster-rebuilds-with---keep-stage-std -Sometimes you don't need a full build. When doing some kind of -"type-based refactoring", like renaming a method, or changing the +Sometimes you don't need a full build. +When doing some kind of "type-based refactoring", like renaming a method, or changing the signature of some function, you can use `./x check` instead for a much faster build. -Note that this whole command just gives you a subset of the full `rustc` -build. The **full** `rustc` build (what you get with `./x build +Note that this whole command just gives you a subset of the full `rustc` build. +The **full** `rustc` build (what you get with `./x build --stage 2 rustc`) has quite a few more steps: - Build `rustc` with the stage1 compiler. @@ -262,8 +270,8 @@ You almost never need to do this. ### Build specific components If you are working on the standard library, you probably don't need to build -every other default component. Instead, you can build a specific component by -providing its name, like this: +every other default component. +Instead, you can build a specific component by providing its name, like this: ```bash ./x build --stage 1 library @@ -275,11 +283,12 @@ default). ## Creating a rustup toolchain Once you have successfully built `rustc`, you will have created a bunch -of files in your `build` directory. In order to actually run the -resulting `rustc`, we recommend creating rustup toolchains. The first -command listed below creates the stage1 toolchain, which was built in the -steps above, with the name `stage1`. The second command creates the stage2 -toolchain using the stage1 compiler. This will be needed in the future +of files in your `build` directory. +In order to actually run the resulting `rustc`, we recommend creating rustup toolchains. +The first command listed below creates the stage1 toolchain, which was built in the +steps above, with the name `stage1`. +The second command creates the stage2 toolchain using the stage1 compiler. +This will be needed in the future if running the entire test suite, but will not be built in this page. Building stage2 is done with the same `./x build` command as for stage1, specifying that the stage is 2 instead. @@ -289,8 +298,8 @@ rustup toolchain link stage1 build/host/stage1 rustup toolchain link stage2 build/host/stage2 ``` -Now you can run the `rustc` you built with via the toolchain. If you run with -`-vV`, you should see a version number ending in `-dev`, indicating a build from +Now you can run the `rustc` you built with via the toolchain. +If you run with `-vV`, you should see a version number ending in `-dev`, indicating a build from your local environment: ```bash @@ -308,14 +317,18 @@ The rustup toolchain points to the specified toolchain compiled in your `build` so the rustup toolchain will be updated whenever `x build` or `x test` are run for that toolchain/stage. -**Note:** the toolchain we've built does not include `cargo`. In this case, `rustup` will +**Note:** the toolchain we've built does not include `cargo`. + In this case, `rustup` will fall back to using `cargo` from the installed `nightly`, `beta`, or `stable` toolchain -(in that order). If you need to use unstable `cargo` flags, be sure to run -`rustup install nightly` if you haven't already. See the +(in that order). + If you need to use unstable `cargo` flags, be sure to run +`rustup install nightly` if you haven't already. + See the [rustup documentation on custom toolchains](https://rust-lang.github.io/rustup/concepts/toolchains.html#custom-toolchains). **Note:** rust-analyzer and IntelliJ Rust plugin use a component called -`rust-analyzer-proc-macro-srv` to work with proc macros. If you intend to use a +`rust-analyzer-proc-macro-srv` to work with proc macros. +If you intend to use a custom toolchain for a project (e.g. via `rustup override set stage1`) you may want to build this component: @@ -368,16 +381,14 @@ cargo +stage1 build --target wasm32-wasip1 ## Other `x` commands -Here are a few other useful `x` commands. We'll cover some of them in detail -in other sections: +Here are a few other useful `x` commands. +We'll cover some of them in detail in other sections: - Building things: - `./x build` – builds everything using the stage 1 compiler, not just up to `std` - - `./x build --stage 2` – builds everything with the stage 2 compiler including - `rustdoc` -- Running tests (see the [section on running tests](../tests/running.html) for - more details): + - `./x build --stage 2` – builds everything with the stage 2 compiler including `rustdoc` +- Running tests (see the [section on running tests](../tests/running.html) for more details): - `./x test library/std` – runs the unit tests and integration tests from `std` - `./x test tests/ui` – runs the `ui` test suite - `./x test tests/ui/const-generics` - runs all the tests in @@ -389,8 +400,8 @@ in other sections: Sometimes you need to start fresh, but this is normally not the case. If you need to run this then bootstrap is most likely not acting right and -you should file a bug as to what is going wrong. If you do need to clean -everything up then you only need to run one command! +you should file a bug as to what is going wrong. +If you do need to clean everything up then you only need to run one command! ```bash ./x clean @@ -402,15 +413,17 @@ a long time even on fast computers. ## Remarks on disk space Building the compiler (especially if beyond stage 1) can require significant amounts of free disk -space, possibly around 100GB. This is compounded if you have a separate build directory for +space, possibly around 100GB. +This is compounded if you have a separate build directory for rust-analyzer (e.g. `build-rust-analyzer`). This is easy to hit with dev-desktops which have a [set disk quota](https://github.com/rust-lang/simpleinfra/blob/8a59e4faeb75a09b072671c74a7cb70160ebef50/ansible/roles/dev-desktop/defaults/main.yml#L7) -for each user, but this also applies to local development as well. Occasionally, you may need to: +for each user, but this also applies to local development as well. +Occasionally, you may need to: - Remove `build/` directory. - Remove `build-rust-analyzer/` directory (if you have a separate rust-analyzer build directory). -- Uninstall unnecessary toolchains if you use `cargo-bisect-rustc`. You can check which toolchains - are installed with `rustup toolchain list`. +- Uninstall unnecessary toolchains if you use `cargo-bisect-rustc`. + You can check which toolchains are installed with `rustup toolchain list`. [^1]: issue[#1707](https://github.com/rust-lang/rustc-dev-guide/issues/1707) From d4ff9c82273c3322c3cc49fb97731d5d3df5c020 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:44:47 +0200 Subject: [PATCH 267/319] less awkward --- src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 2c127245b810..11c4cac024b0 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -91,7 +91,7 @@ cd rust ## What is `x.py`? `x.py` is the build tool for the `rust` repository. -It can build docs, run tests, and compile the compiler and standard library. +It can build docs, run tests, and build the compiler and standard library. This chapter focuses on the basics to be productive, but if you want to learn more about `x.py`, [read this chapter][bootstrap]. @@ -192,7 +192,7 @@ as rustdoc), make sure to read information about that default (located in `src/b as the build process may be different for other defaults. Alternatively, you can write `bootstrap.toml` by hand. -See `bootstrap.example.toml` for all the available settings and explanations of them. +See `bootstrap.example.toml` for all the available settings and what they do. See `src/bootstrap/defaults` for common settings to change. If you have already built `rustc` and you change settings related to LLVM, then you may have to From e83ee8a2be96f6182703c3d994579ded413d141f Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:45:01 +0200 Subject: [PATCH 268/319] missing pause --- src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md index 11c4cac024b0..fc8fe402bfa9 100644 --- a/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md +++ b/src/doc/rustc-dev-guide/src/building/how-to-build-and-run.md @@ -329,7 +329,7 @@ fall back to using `cargo` from the installed `nightly`, `beta`, or `stable` too **Note:** rust-analyzer and IntelliJ Rust plugin use a component called `rust-analyzer-proc-macro-srv` to work with proc macros. If you intend to use a -custom toolchain for a project (e.g. via `rustup override set stage1`) you may +custom toolchain for a project (e.g. via `rustup override set stage1`), you may want to build this component: ```bash From 222e5216a0cf4c7b1cdaeb82e72947275fcff8d0 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:46:00 +0200 Subject: [PATCH 269/319] sembr src/building/optimized-build.md --- .../src/building/optimized-build.md | 30 +++++++++++-------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index d11bfaa4ba00..4953e2004a4c 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -2,15 +2,16 @@ There are multiple additional build configuration options and techniques that can be used to compile a build of `rustc` that is as optimized as possible (for example when building `rustc` for a Linux -distribution). The status of these configuration options for various Rust targets is tracked [here]. +distribution). +The status of these configuration options for various Rust targets is tracked [here]. This page describes how you can use these approaches when building `rustc` yourself. [here]: https://github.com/rust-lang/rust/issues/103595 ## Link-time optimization -Link-time optimization is a powerful compiler technique that can increase program performance. To -enable (Thin-)LTO when building `rustc`, set the `rust.lto` config option to `"thin"` +Link-time optimization is a powerful compiler technique that can increase program performance. +To enable (Thin-)LTO when building `rustc`, set the `rust.lto` config option to `"thin"` in `bootstrap.toml`: ```toml @@ -29,8 +30,8 @@ Enabling LTO on Linux has [produced] speed-ups by up to 10%. ## Memory allocator -Using a different memory allocator for `rustc` can provide significant performance benefits. If you -want to enable the `jemalloc` allocator, you can set the `rust.jemalloc` option to `true` +Using a different memory allocator for `rustc` can provide significant performance benefits. +If you want to enable the `jemalloc` allocator, you can set the `rust.jemalloc` option to `true` in `bootstrap.toml`: ```toml @@ -53,7 +54,8 @@ rust.codegen-units-std = 1 ## Instruction set By default, `rustc` is compiled for a generic (and conservative) instruction set architecture -(depending on the selected target), to make it support as many CPUs as possible. If you want to +(depending on the selected target), to make it support as many CPUs as possible. +If you want to compile `rustc` for a specific instruction set architecture, you can set the `target_cpu` compiler option in `RUSTFLAGS`: @@ -72,14 +74,16 @@ llvm.cflags = "-march=x86-64-v3" ## Profile-guided optimization Applying profile-guided optimizations (or more generally, feedback-directed optimizations) can -produce a large increase to `rustc` performance, by up to 15% ([1], [2]). However, these techniques +produce a large increase to `rustc` performance, by up to 15% ([1], [2]). +However, these techniques are not simply enabled by a configuration option, but rather they require a complex build workflow that compiles `rustc` multiple times and profiles it on selected benchmarks. There is a tool called `opt-dist` that is used to optimize `rustc` with [PGO] (profile-guided -optimizations) and [BOLT] (a post-link binary optimizer) for builds distributed to end users. You -can examine the tool, which is located in `src/tools/opt-dist`, and build a custom PGO build -workflow based on it, or try to use it directly. Note that the tool is currently quite hardcoded to +optimizations) and [BOLT] (a post-link binary optimizer) for builds distributed to end users. +You can examine the tool, which is located in `src/tools/opt-dist`, and build a custom PGO build +workflow based on it, or try to use it directly. +Note that the tool is currently quite hardcoded to the way we use it in Rust's continuous integration workflows, and it might require some custom changes to make it work in a different environment. @@ -93,9 +97,9 @@ changes to make it work in a different environment. To use the tool, you will need to provide some external dependencies: - A Python3 interpreter (for executing `x.py`). -- Compiled LLVM toolchain, with the `llvm-profdata` binary. Optionally, if you want to use BOLT, - the `llvm-bolt` and - `merge-fdata` binaries have to be available in the toolchain. +- Compiled LLVM toolchain, with the `llvm-profdata` binary. + Optionally, if you want to use BOLT, + the `llvm-bolt` and `merge-fdata` binaries have to be available in the toolchain. These dependencies are provided to `opt-dist` by an implementation of the [`Environment`] struct. It specifies directories where will the PGO/BOLT pipeline take place, and also external dependencies From 670fec6bbd14cada71c85b4af9f92c3b48d37751 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:47:52 +0200 Subject: [PATCH 270/319] reflow --- src/doc/rustc-dev-guide/src/building/new-target.md | 3 +-- src/doc/rustc-dev-guide/src/building/optimized-build.md | 5 ++--- src/doc/rustc-dev-guide/src/building/suggested.md | 6 ++---- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/building/new-target.md b/src/doc/rustc-dev-guide/src/building/new-target.md index 1be83e4a5a58..73174aaa0adf 100644 --- a/src/doc/rustc-dev-guide/src/building/new-target.md +++ b/src/doc/rustc-dev-guide/src/building/new-target.md @@ -72,8 +72,7 @@ somewhat successfully, you can copy the specification into the compiler itself. You will need to add a line to the big table inside of the `supported_targets` macro in the `rustc_target::spec` module. -You will then add a corresponding file for your new target containing a -`target` function. +You will then add a corresponding file for your new target containing a `target` function. Look for existing targets to use as examples. diff --git a/src/doc/rustc-dev-guide/src/building/optimized-build.md b/src/doc/rustc-dev-guide/src/building/optimized-build.md index 4953e2004a4c..0a6b82eba46a 100644 --- a/src/doc/rustc-dev-guide/src/building/optimized-build.md +++ b/src/doc/rustc-dev-guide/src/building/optimized-build.md @@ -55,9 +55,8 @@ rust.codegen-units-std = 1 By default, `rustc` is compiled for a generic (and conservative) instruction set architecture (depending on the selected target), to make it support as many CPUs as possible. -If you want to -compile `rustc` for a specific instruction set architecture, you can set the `target_cpu` compiler -option in `RUSTFLAGS`: +If you want to compile `rustc` for a specific instruction set architecture, +you can set the `target_cpu` compiler option in `RUSTFLAGS`: ```bash RUSTFLAGS="-C target_cpu=x86-64-v3" ./x build ... diff --git a/src/doc/rustc-dev-guide/src/building/suggested.md b/src/doc/rustc-dev-guide/src/building/suggested.md index af4dc0ab699c..3ed4f129d144 100644 --- a/src/doc/rustc-dev-guide/src/building/suggested.md +++ b/src/doc/rustc-dev-guide/src/building/suggested.md @@ -118,8 +118,7 @@ requires extra disk space. Selecting `vscode` in `./x setup editor` will prompt you to create a `.vscode/settings.json` file which will configure Visual Studio code. -The recommended `rust-analyzer` settings live at -[`src/etc/rust_analyzer_settings.json`]. +The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_settings.json`]. If running `./x check` on save is inconvenient, in VS Code you can use a [Build Task] instead: @@ -253,8 +252,7 @@ It can be configured through `.zed/settings.json`, as described [here](https://zed.dev/docs/configuring-languages). Selecting `zed` in `./x setup editor` will prompt you to create a `.zed/settings.json` file which will configure Zed with the recommended configuration. -The recommended `rust-analyzer` settings live -at [`src/etc/rust_analyzer_zed.json`]. +The recommended `rust-analyzer` settings live at [`src/etc/rust_analyzer_zed.json`]. ## Check, check, and check again From a2fa6185513bffff922988979d7ea2a7704767f7 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:49:52 +0200 Subject: [PATCH 271/319] sembr src/compiler-debugging.md --- .../rustc-dev-guide/src/compiler-debugging.md | 60 +++++++++++-------- 1 file changed, 34 insertions(+), 26 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 7b67c262c456..b8f6689b05b2 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -1,14 +1,16 @@ # Debugging the compiler -This chapter contains a few tips to debug the compiler. These tips aim to be -useful no matter what you are working on. Some of the other chapters have +This chapter contains a few tips to debug the compiler. +These tips aim to be useful no matter what you are working on. + Some of the other chapters have advice about specific parts of the compiler (e.g. the [Queries Debugging and Testing chapter](./incrcomp-debugging.html) or the [LLVM Debugging chapter](./backend/debugging.md)). ## Configuring the compiler -By default, rustc is built without most debug information. To enable debug info, +By default, rustc is built without most debug information. +To enable debug info, set `rust.debug = true` in your bootstrap.toml. Setting `rust.debug = true` turns on many different debug options (e.g., `debug-assertions`, @@ -45,16 +47,17 @@ You will need to rebuild the compiler after changing any configuration option. ## Suppressing the ICE file By default, if rustc encounters an Internal Compiler Error (ICE) it will dump the ICE contents to an -ICE file within the current working directory named `rustc-ice--.txt`. If this is -not desirable, you can prevent the ICE file from being created with `RUSTC_ICE=0`. +ICE file within the current working directory named `rustc-ice--.txt`. +If this is not desirable, you can prevent the ICE file from being created with `RUSTC_ICE=0`. ## Getting a backtrace [getting-a-backtrace]: #getting-a-backtrace When you have an ICE (panic in the compiler), you can set -`RUST_BACKTRACE=1` to get the stack trace of the `panic!` like in -normal Rust programs. IIRC backtraces **don't work** on MinGW, -sorry. If you have trouble or the backtraces are full of `unknown`, +`RUST_BACKTRACE=1` to get the stack trace of the `panic!` like in normal Rust programs. +IIRC backtraces **don't work** on MinGW, +sorry. +If you have trouble or the backtraces are full of `unknown`, you might want to find some way to use Linux, Mac, or MSVC on Windows. In the default configuration (without `debug` set to `true`), you don't have line numbers @@ -98,9 +101,10 @@ stack backtrace: ## `-Z` flags -The compiler has a bunch of `-Z *` flags. These are unstable flags that are only -enabled on nightly. Many of them are useful for debugging. To get a full listing -of `-Z` flags, use `-Z help`. +The compiler has a bunch of `-Z *` flags. +These are unstable flags that are only enabled on nightly. +Many of them are useful for debugging. +To get a full listing of `-Z` flags, use `-Z help`. One useful flag is `-Z verbose-internals`, which generally enables printing more info that could be useful for debugging. @@ -112,7 +116,8 @@ Right below you can find elaborate explainers on a selected few. If you want to get a backtrace to the point where the compiler emits an error message, you can pass the `-Z treat-err-as-bug=n`, which will make -the compiler panic on the `nth` error. If you leave off `=n`, the compiler will +the compiler panic on the `nth` error. +If you leave off `=n`, the compiler will assume `1` for `n` and thus panic on the first error it encounters. For example: @@ -188,13 +193,12 @@ Cool, now I have a backtrace for the error! The `-Z eagerly-emit-delayed-bugs` option makes it easy to debug delayed bugs. It turns them into normal errors, i.e. makes them visible. This can be used in -combination with `-Z treat-err-as-bug` to stop at a particular delayed bug and -get a backtrace. +combination with `-Z treat-err-as-bug` to stop at a particular delayed bug and get a backtrace. ### Getting the error creation location -`-Z track-diagnostics` can help figure out where errors are emitted. It uses `#[track_caller]` -for this and prints its location alongside the error: +`-Z track-diagnostics` can help figure out where errors are emitted. +It uses `#[track_caller]` for this and prints its location alongside the error: ``` $ RUST_BACKTRACE=1 rustc +stage1 error.rs -Z track-diagnostics @@ -236,11 +240,11 @@ For details see [the guide section on tracing](./tracing.md) ## Narrowing (Bisecting) Regressions The [cargo-bisect-rustc][bisect] tool can be used as a quick and easy way to -find exactly which PR caused a change in `rustc` behavior. It automatically -downloads `rustc` PR artifacts and tests them against a project you provide -until it finds the regression. You can then look at the PR to get more context -on *why* it was changed. See [this tutorial][bisect-tutorial] on how to use -it. +find exactly which PR caused a change in `rustc` behavior. +It automatically downloads `rustc` PR artifacts and tests them against a project you provide +until it finds the regression. +You can then look at the PR to get more context on *why* it was changed. + See [this tutorial][bisect-tutorial] on how to use it. [bisect]: https://github.com/rust-lang/cargo-bisect-rustc [bisect-tutorial]: https://rust-lang.github.io/cargo-bisect-rustc/tutorial.html @@ -250,8 +254,9 @@ it. The [rustup-toolchain-install-master][rtim] tool by kennytm can be used to download the artifacts produced by Rust's CI for a specific SHA1 -- this basically corresponds to the successful landing of some PR -- and then sets -them up for your local use. This also works for artifacts produced by `@bors -try`. This is helpful when you want to examine the resulting build of a PR +them up for your local use. +This also works for artifacts produced by `@bors try`. +This is helpful when you want to examine the resulting build of a PR without doing the build yourself. [rtim]: https://github.com/kennytm/rustup-toolchain-install-master @@ -259,10 +264,12 @@ without doing the build yourself. ## `#[rustc_*]` TEST attributes The compiler defines a whole lot of internal (perma-unstable) attributes some of which are useful -for debugging by dumping extra compiler-internal information. These are prefixed with `rustc_` and +for debugging by dumping extra compiler-internal information. +These are prefixed with `rustc_` and are gated behind the internal feature `rustc_attrs` (enabled via e.g. `#![feature(rustc_attrs)]`). -For a complete and up to date list, see [`builtin_attrs`]. More specifically, the ones marked `TEST`. +For a complete and up to date list, see [`builtin_attrs`]. +More specifically, the ones marked `TEST`. Here are some notable ones: | Attribute | Description | @@ -311,7 +318,8 @@ $ firefox maybe_init_suffix.pdf # Or your favorite pdf viewer ### Debugging type layouts The internal attribute `#[rustc_layout]` can be used to dump the [`Layout`] of -the type it is attached to. For example: +the type it is attached to. +For example: ```rust #![feature(rustc_attrs)] From faea3136bba95ceefb7a5eb0c96d001bc85bc307 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:53:58 +0200 Subject: [PATCH 272/319] whitespace --- src/doc/rustc-dev-guide/src/compiler-debugging.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index b8f6689b05b2..8efcf07d5237 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -2,7 +2,7 @@ This chapter contains a few tips to debug the compiler. These tips aim to be useful no matter what you are working on. - Some of the other chapters have +Some of the other chapters have advice about specific parts of the compiler (e.g. the [Queries Debugging and Testing chapter](./incrcomp-debugging.html) or the [LLVM Debugging chapter](./backend/debugging.md)). From 8c322fcdb3298e445fabb8c5cea5cae151c3daa3 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:55:09 +0200 Subject: [PATCH 273/319] sembr src/llvm-coverage-instrumentation.md --- .../src/llvm-coverage-instrumentation.md | 22 +++++++++---------- 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index fbfcc4198de6..0dde8ed6c9ff 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -7,13 +7,13 @@ libraries and binaries with additional instructions and data, at compile time. The coverage instrumentation injects calls to the LLVM intrinsic instruction [`llvm.instrprof.increment`][llvm-instrprof-increment] at code branches (based on a MIR-based control flow analysis), and LLVM converts these to -instructions that increment static counters, when executed. The LLVM coverage -instrumentation also requires a [Coverage Map] that encodes source metadata, +instructions that increment static counters, when executed. +The LLVM coverage instrumentation also requires a [Coverage Map] that encodes source metadata, mapping counter IDs--directly and indirectly--to the file locations (with start and end line and column). -Rust libraries, with or without coverage instrumentation, can be linked into -instrumented binaries. When the program is executed and cleanly terminates, +Rust libraries, with or without coverage instrumentation, can be linked into instrumented binaries. +When the program is executed and cleanly terminates, LLVM libraries write the final counter values to a file (`default.profraw` or a custom file set through environment variable `LLVM_PROFILE_FILE`). @@ -22,8 +22,7 @@ files, with corresponding Coverage Maps (from matching binaries that produced them), and generate various reports for analysis, for example: Screenshot of sample `llvm-cov show` result, for function add_quoted_string -
+ src="img/llvm-cov-show-01.png" class="center"/>
Detailed instructions and examples are documented in the [rustc book][rustc-book-instrument-coverage]. @@ -39,8 +38,7 @@ When working on the coverage instrumentation code, it is usually necessary to This allows the compiler to produce instrumented binaries, and makes it possible to run the full coverage test suite. -Enabling debug assertions in the compiler and in LLVM is recommended, but not -mandatory. +Enabling debug assertions in the compiler and in LLVM is recommended, but not mandatory. ```toml # Similar to the "compiler" profile, but also enables debug assertions in LLVM. @@ -60,8 +58,8 @@ rust.debug-assertions = true `-C instrument-coverage` automatically enables Rust symbol mangling `v0` (as if the user specified `-C symbol-mangling-version=v0` option when invoking -`rustc`) to ensure consistent and reversible name mangling. This has two -important benefits: +`rustc`) to ensure consistent and reversible name mangling. +This has two important benefits: 1. LLVM coverage tools can analyze coverage over multiple runs, including some changes to source code; so mangled names must be consistent across compilations. @@ -71,8 +69,8 @@ important benefits: ## The LLVM profiler runtime -Coverage data is only generated by running the executable Rust program. `rustc` -statically links coverage-instrumented binaries with LLVM runtime code +Coverage data is only generated by running the executable Rust program. +`rustc` statically links coverage-instrumented binaries with LLVM runtime code ([compiler-rt][compiler-rt-profile]) that implements program hooks (such as an `exit` hook) to write the counter values to the `.profraw` file. From 48760618e8ecdd5eab2acdb4f1a9c8f7d9416b99 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:02:14 +0200 Subject: [PATCH 274/319] replace html with markdown --- src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md index 0dde8ed6c9ff..8e94928c8a1a 100644 --- a/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md +++ b/src/doc/rustc-dev-guide/src/llvm-coverage-instrumentation.md @@ -21,8 +21,7 @@ Developers use existing LLVM coverage analysis tools to decode `.profraw` files, with corresponding Coverage Maps (from matching binaries that produced them), and generate various reports for analysis, for example: -Screenshot of sample `llvm-cov show` result, for function add_quoted_string
+![Screenshot of sample `llvm-cov show` result, for function add_quoted_string](img/llvm-cov-show-01.png) Detailed instructions and examples are documented in the [rustc book][rustc-book-instrument-coverage]. From 0c6e040daddbaef2badd583de2beed043068fa11 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:03:06 +0200 Subject: [PATCH 275/319] sembr src/profiling.md --- src/doc/rustc-dev-guide/src/profiling.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/profiling.md b/src/doc/rustc-dev-guide/src/profiling.md index 06ebb8998342..66eabe7d8c93 100644 --- a/src/doc/rustc-dev-guide/src/profiling.md +++ b/src/doc/rustc-dev-guide/src/profiling.md @@ -33,10 +33,11 @@ Since most of the time compiling rustc is spent in LLVM, the idea is that by reducing the amount of code passed to LLVM, compiling rustc gets faster. To use `cargo-llvm-lines` together with somewhat custom rustc build process, you can use -`-C save-temps` to obtain required LLVM IR. The option preserves temporary work products -created during compilation. Among those is LLVM IR that represents an input to the -optimization pipeline; ideal for our purposes. It is stored in files with `*.no-opt.bc` -extension in LLVM bitcode format. +`-C save-temps` to obtain required LLVM IR. +The option preserves temporary work products created during compilation. +Among those is LLVM IR that represents an input to the +optimization pipeline; ideal for our purposes. +It is stored in files with `*.no-opt.bc` extension in LLVM bitcode format. Example usage: ``` @@ -105,7 +106,8 @@ rust.codegen-units = 0 # num_cpus The llvm-lines output is affected by several options. `rust.optimize = false` increases it from 2.1GB to 3.5GB and `codegen-units = 0` to 4.1GB. -MIR optimizations have little impact. Compared to the default `RUSTFLAGS="-Z +MIR optimizations have little impact. +Compared to the default `RUSTFLAGS="-Z mir-opt-level=1"`, level 0 adds 0.3GB and level 2 removes 0.2GB. As of July 2022, inlining happens in LLVM and GCC codegen backends, From 809bf7922fc47eb4bf6ea59fde8db66779ae40fb Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:04:49 +0200 Subject: [PATCH 276/319] sembr src/tests/docker.md --- src/doc/rustc-dev-guide/src/tests/docker.md | 60 +++++++++++++-------- 1 file changed, 37 insertions(+), 23 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 436aa13545eb..94fdf34dc80d 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -1,12 +1,14 @@ # Testing with Docker -The [`src/ci/docker`] directory includes [Docker] image definitions for Linux-based jobs executed on GitHub Actions (non-Linux jobs run outside Docker). You can run these jobs on your local development machine, which can be -helpful to test environments different from your local system. You will -need to install Docker on a Linux, Windows, or macOS system (typically Linux +The [`src/ci/docker`] directory includes [Docker] image definitions for Linux-based jobs executed on GitHub Actions (non-Linux jobs run outside Docker). +You can run these jobs on your local development machine, which can be +helpful to test environments different from your local system. +You will need to install Docker on a Linux, Windows, or macOS system (typically Linux will be much faster than Windows or macOS because the latter use virtual machines to emulate a Linux environment). -Jobs running in CI are configured through a set of bash scripts, and it is not always trivial to reproduce their behavior locally. If you want to run a CI job locally in the simplest way possible, you can use a provided helper `citool` that tries to replicate what happens on CI as closely as possible: +Jobs running in CI are configured through a set of bash scripts, and it is not always trivial to reproduce their behavior locally. +If you want to run a CI job locally in the simplest way possible, you can use a provided helper `citool` that tries to replicate what happens on CI as closely as possible: ```bash cargo run --manifest-path src/ci/citool/Cargo.toml run-local @@ -18,39 +20,51 @@ If the above script does not work for you, you would like to have more control o ## The `run.sh` script The [`src/ci/docker/run.sh`] script is used to build a specific Docker image, run it, -build Rust within the image, and either run tests or prepare a set of archives designed for distribution. The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. All the compiler artifacts will be stored in the `obj` directory. The shell will start out in the `obj`directory. From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. +build Rust within the image, and either run tests or prepare a set of archives designed for distribution. +The script will mount your local Rust source tree in read-only mode, and an `obj` directory in read-write mode. +All the compiler artifacts will be stored in the `obj` directory. +The shell will start out in the `obj`directory. +From there, it will execute `../src/ci/run.sh` which starts the build as defined by the Docker image. -You can run `src/ci/docker/run.sh ` directly. A few important notes regarding the `run.sh` script: -- When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). -- `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). +You can run `src/ci/docker/run.sh ` directly. +A few important notes regarding the `run.sh` script: +- When executed on CI, the script expects that all submodules are checked out. + If some submodule that is accessed by the job is not available, the build will result in an error. + You should thus make sure that you have all required submodules checked out locally. + You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). +- `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. + Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). - If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. - If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. -- Some of the std tests require IPv6 support. Docker on Linux seems to have it - disabled by default. Run the commands in [`enable-docker-ipv6.sh`] to enable - IPv6 before creating the container. This only needs to be done once. +- Some of the std tests require IPv6 support. + Docker on Linux seems to have it disabled by default. + Run the commands in [`enable-docker-ipv6.sh`] to enable IPv6 before creating the container. + This only needs to be done once. ### Interactive mode -Sometimes, it can be useful to build a specific Docker image, and then run custom commands inside it, so that you can experiment with how the given system behaves. You can do that using an interactive mode, which will +Sometimes, it can be useful to build a specific Docker image, and then run custom commands inside it, so that you can experiment with how the given system behaves. +You can do that using an interactive mode, which will start a bash shell in the container, using `src/ci/docker/run.sh --dev `. -When inside the Docker container, you can run individual commands to do specific tasks. For -example, you can run `../x test tests/ui` to just run UI tests. +When inside the Docker container, you can run individual commands to do specific tasks. +For example, you can run `../x test tests/ui` to just run UI tests. Some additional notes about using the interactive mode: - The container will be deleted automatically when you exit the shell, however - the build artifacts persist in the `obj` directory. If you are switching - between different Docker images, the artifacts from previous environments - stored in the `obj` directory may confuse the build system. Sometimes you - will need to delete parts or all of the `obj` directory before building + the build artifacts persist in the `obj` directory. + If you are switching between different Docker images, the artifacts from previous environments + stored in the `obj` directory may confuse the build system. + Sometimes you will need to delete parts or all of the `obj` directory before building inside the container. -- The container is bare-bones, with only a minimal set of packages. You may - want to install some things like `apt install less vim`. -- You can open multiple shells in the container. First you need the container +- The container is bare-bones, with only a minimal set of packages. + You may want to install some things like `apt install less vim`. +- You can open multiple shells in the container. + First you need the container name (a short hash), which is displayed in the shell prompt, or you can run - `docker container ls` outside of the container to list the available - containers. With the container name, run `docker exec -it + `docker container ls` outside of the container to list the available containers. + With the container name, run `docker exec -it /bin/bash` where `` is the container name like `4ba195e95cef`. [Docker]: https://www.docker.com/ From 06af960fba2777f8865351e4cc256f82fee911c9 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 01:10:53 +0200 Subject: [PATCH 277/319] sounds better as separate sentences --- src/doc/rustc-dev-guide/src/tests/docker.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/docker.md b/src/doc/rustc-dev-guide/src/tests/docker.md index 94fdf34dc80d..51beee8a866c 100644 --- a/src/doc/rustc-dev-guide/src/tests/docker.md +++ b/src/doc/rustc-dev-guide/src/tests/docker.md @@ -31,9 +31,11 @@ A few important notes regarding the `run.sh` script: - When executed on CI, the script expects that all submodules are checked out. If some submodule that is accessed by the job is not available, the build will result in an error. You should thus make sure that you have all required submodules checked out locally. - You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules (this might not be enough for the given CI job that you are trying to execute though). + You can either do that manually through git, or set `build.submodules = true` in your `bootstrap.toml` and run a command such as `x build` to let bootstrap download the most important submodules + Note that this might not be enough for the given CI job that you are trying to execute though. - `` corresponds to a single directory located in one of the `src/ci/docker/host-*` directories. - Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments (this is a part of the complexity that makes it difficult to run CI jobs locally). + Note that image name does not necessarily correspond to a job name, as some jobs execute the same image, but with different environment variables or Docker build arguments + This is a part of the complexity that makes it difficult to run CI jobs locally. - If you are executing a "dist" job (job beginning with `dist-`), you should set the `DEPLOY=1` environment variable. - If you are executing an "alternative dist" job (job beginning with `dist-` and ending with `-alt`), you should set the `DEPLOY_ALT=1` environment variable. - Some of the std tests require IPv6 support. From 3ea1571787bbfdbdf0596e4c73a08e1257ac89d4 Mon Sep 17 00:00:00 2001 From: Tshepang Mbambo Date: Tue, 3 Feb 2026 00:54:07 +0200 Subject: [PATCH 278/319] reflow --- src/doc/rustc-dev-guide/src/compiler-debugging.md | 3 +-- src/doc/rustc-dev-guide/src/tests/compiletest.md | 3 +-- src/doc/rustc-dev-guide/src/tracing.md | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/compiler-debugging.md b/src/doc/rustc-dev-guide/src/compiler-debugging.md index 8efcf07d5237..d4c599a6edf7 100644 --- a/src/doc/rustc-dev-guide/src/compiler-debugging.md +++ b/src/doc/rustc-dev-guide/src/compiler-debugging.md @@ -55,8 +55,7 @@ If this is not desirable, you can prevent the ICE file from being created with ` When you have an ICE (panic in the compiler), you can set `RUST_BACKTRACE=1` to get the stack trace of the `panic!` like in normal Rust programs. -IIRC backtraces **don't work** on MinGW, -sorry. +IIRC backtraces **don't work** on MinGW, sorry. If you have trouble or the backtraces are full of `unknown`, you might want to find some way to use Linux, Mac, or MSVC on Windows. diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index d7372de11ff8..959a73a5a9ad 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -277,8 +277,7 @@ the debugger currently being used: gdb is in a range (inclusive) - `min-lldb-version: 310` — ignores the test if the version of lldb is below the given version - `rust-lldb` — ignores the test if lldb is not contain the Rust plugin. - NOTE: The "Rust" version of LLDB doesn't exist anymore, so this will always be - ignored. + NOTE: The "Rust" version of LLDB doesn't exist anymore, so this will always be ignored. This should probably be removed. By passing the `--debugger` option to compiletest, you can specify a single debugger to run tests with. diff --git a/src/doc/rustc-dev-guide/src/tracing.md b/src/doc/rustc-dev-guide/src/tracing.md index 323f81dcee04..2ad035bbafbc 100644 --- a/src/doc/rustc-dev-guide/src/tracing.md +++ b/src/doc/rustc-dev-guide/src/tracing.md @@ -74,8 +74,7 @@ RUSTC_LOG=[typeck] The query arguments are included as a tracing field which means that you can filter on the debug display of the arguments. For example, the `typeck` query has an argument `key: LocalDefId` of what is being checked. -You can use a regex to match on that `LocalDefId` to log type checking for a specific -function: +You can use a regex to match on that `LocalDefId` to log type checking for a specific function: ``` RUSTC_LOG=[typeck{key=.*name_of_item.*}] From 28feae0c875f8e95ea667abd5d32b49bcf978164 Mon Sep 17 00:00:00 2001 From: ltdk Date: Mon, 2 Feb 2026 17:34:15 -0500 Subject: [PATCH 279/319] Move bigint helper tracking issues --- library/core/src/lib.rs | 4 ++- library/core/src/num/int_macros.rs | 26 ++++++++++---------- library/core/src/num/uint_macros.rs | 24 +++++++++--------- library/coretests/tests/lib.rs | 4 ++- library/stdarch/crates/core_arch/src/lib.rs | 1 - tests/assembly-llvm/x86_64-bigint-helpers.rs | 1 - tests/codegen-llvm/bigint-helpers.rs | 1 - 7 files changed, 31 insertions(+), 30 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index e8c9d26fb3b5..432ca50b3361 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -97,7 +97,6 @@ // tidy-alphabetical-start #![feature(array_ptr_get)] #![feature(asm_experimental_arch)] -#![feature(bigint_helper_methods)] #![feature(bstr)] #![feature(bstr_internals)] #![feature(cfg_select)] @@ -107,6 +106,7 @@ #![feature(const_destruct)] #![feature(const_eval_select)] #![feature(const_select_unpredictable)] +#![feature(const_unsigned_bigint_helpers)] #![feature(core_intrinsics)] #![feature(coverage_attribute)] #![feature(disjoint_bitor)] @@ -120,6 +120,7 @@ #![feature(ptr_alignment_type)] #![feature(ptr_metadata)] #![feature(set_ptr_value)] +#![feature(signed_bigint_helpers)] #![feature(slice_ptr_get)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] @@ -129,6 +130,7 @@ #![feature(unsafe_pinned)] #![feature(utf16_extra)] #![feature(variant_count)] +#![feature(widening_mul)] // tidy-alphabetical-end // // Language features: diff --git a/library/core/src/num/int_macros.rs b/library/core/src/num/int_macros.rs index 9a27f8a0b5e6..b21865a9ae54 100644 --- a/library/core/src/num/int_macros.rs +++ b/library/core/src/num/int_macros.rs @@ -2522,7 +2522,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 10 MAX (a = 10 × 2^", stringify!($BITS), " + 2^", stringify!($BITS), " - 1)")] @@ -2544,7 +2544,7 @@ macro_rules! int_impl { /// /// assert_eq!((sum1, sum0), (6, 8)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2625,7 +2625,7 @@ macro_rules! int_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// // Only the most significant word is signed. /// // #[doc = concat!("// 6 8 (a = 6 × 2^", stringify!($BITS), " + 8)")] @@ -2647,7 +2647,7 @@ macro_rules! int_impl { /// #[doc = concat!("assert_eq!((diff1, diff0), (10, ", stringify!($UnsignedT), "::MAX));")] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2717,12 +2717,12 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5i32.widening_mul(-2), (4294967286, -1)); /// assert_eq!(1_000_000_000i32.widening_mul(-10), (2884901888, -3)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2747,7 +2747,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul(-2, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul(-2, 10), (0, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul(-10, 0), (2884901888, -3)); @@ -2757,8 +2757,8 @@ macro_rules! int_impl { "(", stringify!($SelfT), "::MAX.unsigned_abs() + 1, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2784,7 +2784,7 @@ macro_rules! int_impl { /// Please note that this example is shared among integer types, which is why `i32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(signed_bigint_helpers)] /// assert_eq!(5i32.carrying_mul_add(-2, 0, 0), (4294967286, -1)); /// assert_eq!(5i32.carrying_mul_add(-2, 10, 10), (10, 0)); /// assert_eq!(1_000_000_000i32.carrying_mul_add(-10, 0, 0), (2884901888, -3)); @@ -2794,8 +2794,8 @@ macro_rules! int_impl { "(", stringify!($UnsignedT), "::MAX, ", stringify!($SelfT), "::MAX / 2));" )] /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "signed_bigint_helpers", issue = "151989")] + #[rustc_const_unstable(feature = "signed_bigint_helpers", issue = "151989")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/core/src/num/uint_macros.rs b/library/core/src/num/uint_macros.rs index c48320c0eab3..5c263ea845cc 100644 --- a/library/core/src/num/uint_macros.rs +++ b/library/core/src/num/uint_macros.rs @@ -2807,7 +2807,7 @@ macro_rules! uint_impl { /// assert_eq!((sum1, sum0), (9, 6)); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -2899,7 +2899,7 @@ macro_rules! uint_impl { #[doc = concat!("assert_eq!((diff1, diff0), (3, ", stringify!($SelfT), "::MAX));")] /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3011,14 +3011,14 @@ macro_rules! uint_impl { /// # Examples /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(5_", stringify!($SelfT), ".widening_mul(7), (35, 0));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::MAX.widening_mul(", stringify!($SelfT), "::MAX), (1, ", stringify!($SelfT), "::MAX - 1));")] /// ``` /// /// Compared to other `*_mul` methods: /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::widening_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, 3));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::overflowing_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), (0, true));")] #[doc = concat!("assert_eq!(", stringify!($SelfT), "::wrapping_mul(1 << ", stringify!($BITS_MINUS_ONE), ", 6), 0);")] @@ -3028,12 +3028,12 @@ macro_rules! uint_impl { /// Please note that this example is shared among integer types, which is why `u32` is used. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(widening_mul)] /// assert_eq!(5u32.widening_mul(2), (10, 0)); /// assert_eq!(1_000_000_000u32.widening_mul(10), (1410065408, 2)); /// ``` - #[unstable(feature = "bigint_helper_methods", issue = "85532")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[unstable(feature = "widening_mul", issue = "152016")] + #[rustc_const_unstable(feature = "widening_mul", issue = "152016")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3072,7 +3072,7 @@ macro_rules! uint_impl { /// implementing it for wider-than-native types. /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// fn scalar_mul_eq(little_endian_digits: &mut Vec, multiplicand: u16) { /// let mut carry = 0; /// for d in little_endian_digits.iter_mut() { @@ -3097,7 +3097,7 @@ macro_rules! uint_impl { /// except that it gives the value of the overflow instead of just whether one happened: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// let r = u8::carrying_mul(7, 13, 0); /// assert_eq!((r.0, r.1 != 0), u8::overflowing_mul(7, 13)); /// let r = u8::carrying_mul(13, 42, 0); @@ -3109,14 +3109,14 @@ macro_rules! uint_impl { /// [`wrapping_add`](Self::wrapping_add) methods: /// /// ``` - /// #![feature(bigint_helper_methods)] + /// #![feature(const_unsigned_bigint_helpers)] /// assert_eq!( /// 789_u16.carrying_mul(456, 123).0, /// 789_u16.wrapping_mul(456).wrapping_add(123), /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] @@ -3182,7 +3182,7 @@ macro_rules! uint_impl { /// ); /// ``` #[stable(feature = "unsigned_bigint_helpers", since = "1.91.0")] - #[rustc_const_unstable(feature = "bigint_helper_methods", issue = "85532")] + #[rustc_const_unstable(feature = "const_unsigned_bigint_helpers", issue = "152015")] #[must_use = "this returns the result of the operation, \ without modifying the original"] #[inline] diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 9054eada12fb..d085e4ad1a8f 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -8,7 +8,6 @@ #![feature(ascii_char_variants)] #![feature(async_iter_from_iter)] #![feature(async_iterator)] -#![feature(bigint_helper_methods)] #![feature(bool_to_result)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] @@ -34,6 +33,7 @@ #![feature(const_result_trait_fn)] #![feature(const_select_unpredictable)] #![feature(const_trait_impl)] +#![feature(const_unsigned_bigint_helpers)] #![feature(control_flow_ok)] #![feature(core_float_math)] #![feature(core_intrinsics)] @@ -98,6 +98,7 @@ #![feature(portable_simd)] #![feature(ptr_metadata)] #![feature(result_option_map_or_default)] +#![feature(signed_bigint_helpers)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] #![feature(slice_internals)] @@ -122,6 +123,7 @@ #![feature(uint_gather_scatter_bits)] #![feature(unsize)] #![feature(unwrap_infallible)] +#![feature(widening_mul)] // tidy-alphabetical-end #![allow(internal_features)] #![deny(fuzzy_provenance_casts)] diff --git a/library/stdarch/crates/core_arch/src/lib.rs b/library/stdarch/crates/core_arch/src/lib.rs index 3992eaee30fd..039a4c4411f2 100644 --- a/library/stdarch/crates/core_arch/src/lib.rs +++ b/library/stdarch/crates/core_arch/src/lib.rs @@ -33,7 +33,6 @@ f16, aarch64_unstable_target_feature, target_feature_inline_always, - bigint_helper_methods, funnel_shifts, avx10_target_feature, const_trait_impl, diff --git a/tests/assembly-llvm/x86_64-bigint-helpers.rs b/tests/assembly-llvm/x86_64-bigint-helpers.rs index 9d998a31cf30..d5d1eba99f39 100644 --- a/tests/assembly-llvm/x86_64-bigint-helpers.rs +++ b/tests/assembly-llvm/x86_64-bigint-helpers.rs @@ -4,7 +4,6 @@ //@ compile-flags: -C llvm-args=-x86-asm-syntax=intel #![no_std] -#![feature(bigint_helper_methods)] // This checks that the `carrying_add` and `borrowing_sub` implementation successfully chain, // to catch issues like diff --git a/tests/codegen-llvm/bigint-helpers.rs b/tests/codegen-llvm/bigint-helpers.rs index ec70a3eabedb..404dc901de8c 100644 --- a/tests/codegen-llvm/bigint-helpers.rs +++ b/tests/codegen-llvm/bigint-helpers.rs @@ -1,7 +1,6 @@ //@ compile-flags: -C opt-level=3 #![crate_type = "lib"] -#![feature(bigint_helper_methods)] // Note that there's also an assembly test for this, which is what checks for // the `ADC` (Add with Carry) instruction on x86 now that the IR we emit uses From 1c4940b2bedafe80058fa81d283c6de32307ed4f Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 3 Feb 2026 09:29:54 +1100 Subject: [PATCH 280/319] Remove an unneeded `HashStable` derive. This has the nice side-effect of eliminating `rustc_codegen_ssa`'s dependency on `rustc_query_system`. (Indeed, looking through such dependencies was how I found this.) --- Cargo.lock | 1 - compiler/rustc_codegen_ssa/Cargo.toml | 1 - compiler/rustc_codegen_ssa/src/lib.rs | 4 ++-- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 02459afa9072..d3bd5d7b71b7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3684,7 +3684,6 @@ dependencies = [ "rustc_macros", "rustc_metadata", "rustc_middle", - "rustc_query_system", "rustc_serialize", "rustc_session", "rustc_span", diff --git a/compiler/rustc_codegen_ssa/Cargo.toml b/compiler/rustc_codegen_ssa/Cargo.toml index 9c5a3d839ceb..3d045a02fef1 100644 --- a/compiler/rustc_codegen_ssa/Cargo.toml +++ b/compiler/rustc_codegen_ssa/Cargo.toml @@ -28,7 +28,6 @@ rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } -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_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 3ffc16d49ac1..904c74944959 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -24,7 +24,7 @@ use rustc_data_structures::unord::UnordMap; use rustc_hir::CRATE_HIR_ID; use rustc_hir::attrs::{CfgEntry, NativeLibKind, WindowsSubsystemKind}; use rustc_hir::def_id::CrateNum; -use rustc_macros::{Decodable, Encodable, HashStable}; +use rustc_macros::{Decodable, Encodable}; use rustc_metadata::EncodedMetadata; use rustc_middle::dep_graph::WorkProduct; use rustc_middle::lint::LevelAndSource; @@ -175,7 +175,7 @@ bitflags::bitflags! { } } -#[derive(Clone, Debug, Encodable, Decodable, HashStable)] +#[derive(Clone, Debug, Encodable, Decodable)] pub struct NativeLib { pub kind: NativeLibKind, pub name: Symbol, From 3a5d7df8418b6d287a9c258fc3bd5e6b2e1746dd Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Tue, 3 Feb 2026 15:46:05 +1100 Subject: [PATCH 281/319] Add a useful comment on `rustc_codegen_ssa::NativeLib`. --- compiler/rustc_codegen_ssa/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs index 904c74944959..5cca916c17b6 100644 --- a/compiler/rustc_codegen_ssa/src/lib.rs +++ b/compiler/rustc_codegen_ssa/src/lib.rs @@ -175,6 +175,11 @@ bitflags::bitflags! { } } +// This is the same as `rustc_session::cstore::NativeLib`, except: +// - (important) the `foreign_module` field is missing, because it contains a `DefId`, which can't +// be encoded with `FileEncoder`. +// - (less important) the `verbatim` field is a `bool` rather than an `Option`, because here +// we can treat `false` and `absent` the same. #[derive(Clone, Debug, Encodable, Decodable)] pub struct NativeLib { pub kind: NativeLibKind, From f14e3ee38ff234ca00cb73b61bf306316b9ab91d Mon Sep 17 00:00:00 2001 From: Martin Nordholts Date: Thu, 15 Jan 2026 06:12:57 +0100 Subject: [PATCH 282/319] compiletest: Don't assume `aux-crate` becomes a `*.so` with `no-prefer-dynamic` Since it does not make sense to do so. If someone prefers no dynamic stuff, the last thing they want to look for is an `.so` file. Also add a regression test. Without the fix, the test fails with: error: test compilation failed although it shouldn't! --- stderr ------------------------------- error: extern location for no_prefer_dynamic_lib does not exist: .../auxiliary/libno_prefer_dynamic_lib.so --> .../no-prefer-dynamic-means-no-so.rs:9:5 | LL | no_prefer_dynamic_lib::return_42(); | ^^^^^^^^^^^^^^^^^^^^^ --- src/tools/compiletest/src/runtest.rs | 2 +- .../auxiliary/no_prefer_dynamic_lib.rs | 10 ++++++++++ .../no-prefer-dynamic-means-no-so.rs | 10 ++++++++++ 3 files changed, 21 insertions(+), 1 deletion(-) create mode 100644 tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs create mode 100644 tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index ef90b81fb2a7..1804490a5f12 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1438,7 +1438,7 @@ impl<'test> TestCx<'test> { } else if aux_type.is_some() { panic!("aux_type {aux_type:?} not expected"); } else if aux_props.no_prefer_dynamic { - (AuxType::Dylib, None) + (AuxType::Lib, None) } else if self.config.target.contains("emscripten") || (self.config.target.contains("musl") && !aux_props.force_host diff --git a/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs b/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs new file mode 100644 index 000000000000..6688dadbab24 --- /dev/null +++ b/tests/ui/compiletest-self-test/auxiliary/no_prefer_dynamic_lib.rs @@ -0,0 +1,10 @@ +//@ no-prefer-dynamic + +//! Since this is `no-prefer-dynamic` we expect compiletest to _not_ look for +//! this create as `libno_prefer_dynamic_lib.so`. + +#![crate_type = "rlib"] + +pub fn return_42() -> i32 { + 42 +} diff --git a/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs b/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs new file mode 100644 index 000000000000..b7e8fae506c3 --- /dev/null +++ b/tests/ui/compiletest-self-test/no-prefer-dynamic-means-no-so.rs @@ -0,0 +1,10 @@ +//! Since we and our `aux-crate` is `no-prefer-dynamic`, we expect compiletest +//! to _not_ look for `libno_prefer_dynamic_lib.so`. + +//@ check-pass +//@ no-prefer-dynamic +//@ aux-crate: no_prefer_dynamic_lib=no_prefer_dynamic_lib.rs + +fn main() { + no_prefer_dynamic_lib::return_42(); +} From c07f10c2c1296431efba33bb52c64ee72eb7146f Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Sun, 1 Feb 2026 14:25:18 +0100 Subject: [PATCH 283/319] Convert to inline diagnostics in `rustc_driver_impl` --- Cargo.lock | 1 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/messages.ftl | 29 ------------ compiler/rustc_driver_impl/src/lib.rs | 3 -- .../src/session_diagnostics.rs | 44 +++++++++++-------- 5 files changed, 25 insertions(+), 53 deletions(-) delete mode 100644 compiler/rustc_driver_impl/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..91fce715c8cd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3789,7 +3789,6 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", - "rustc_fluent_macro", "rustc_hir_analysis", "rustc_hir_pretty", "rustc_hir_typeck", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..d97c552b412b 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -21,7 +21,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } diff --git a/compiler/rustc_driver_impl/messages.ftl b/compiler/rustc_driver_impl/messages.ftl deleted file mode 100644 index b62cdc35f513..000000000000 --- a/compiler/rustc_driver_impl/messages.ftl +++ /dev/null @@ -1,29 +0,0 @@ -driver_impl_cant_emit_mir = could not emit MIR: {$error} - -driver_impl_ice = the compiler unexpectedly panicked. this is a bug. -driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url} -driver_impl_ice_bug_report_internal_feature = using internal features is not supported and expected to cause internal compiler errors when used incorrectly -driver_impl_ice_bug_report_update_note = please make sure that you have updated to the latest nightly -driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden - -driver_impl_ice_flags = compiler flags: {$flags} -driver_impl_ice_path = please attach the file at `{$path}` to your bug report -driver_impl_ice_path_error = the ICE couldn't be written to `{$path}`: {$error} -driver_impl_ice_path_error_env = the environment variable `RUSTC_ICE` is set to `{$env_var}` -driver_impl_ice_version = rustc {$version} running on {$triple} - -driver_impl_rlink_corrupt_file = corrupt metadata encountered in `{$file}` - -driver_impl_rlink_empty_version_number = the input does not contain version number - -driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}` - -driver_impl_rlink_no_a_file = rlink must be a file - -driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}` - -driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}` - -driver_impl_rlink_wrong_file_type = the input does not look like a .rlink file - -driver_impl_unstable_feature_usage = cannot dump feature usage metrics: {$error} diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..a0cb29ef15cf 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -108,15 +108,12 @@ use crate::session_diagnostics::{ RLinkWrongFileType, RlinkCorruptFile, RlinkNotAFile, RlinkUnableToRead, UnstableFeatureUsage, }; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn default_translator() -> Translator { Translator::with_fallback_bundle(DEFAULT_LOCALE_RESOURCES.to_vec(), false) } pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ // tidy-alphabetical-start - crate::DEFAULT_LOCALE_RESOURCE, rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE, rustc_ast_passes::DEFAULT_LOCALE_RESOURCE, rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_driver_impl/src/session_diagnostics.rs b/compiler/rustc_driver_impl/src/session_diagnostics.rs index 774221fd396a..565c176645de 100644 --- a/compiler/rustc_driver_impl/src/session_diagnostics.rs +++ b/compiler/rustc_driver_impl/src/session_diagnostics.rs @@ -3,82 +3,88 @@ use std::error::Error; use rustc_macros::{Diagnostic, Subdiagnostic}; #[derive(Diagnostic)] -#[diag(driver_impl_cant_emit_mir)] +#[diag("could not emit MIR: {$error}")] pub struct CantEmitMIR { pub error: std::io::Error, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_unable_to_read)] +#[diag("failed to read rlink file: `{$err}`")] pub(crate) struct RlinkUnableToRead { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_wrong_file_type)] +#[diag("the input does not look like a .rlink file")] pub(crate) struct RLinkWrongFileType; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_empty_version_number)] +#[diag("the input does not contain version number")] pub(crate) struct RLinkEmptyVersionNumber; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_encoding_version_mismatch)] +#[diag( + ".rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`" +)] pub(crate) struct RLinkEncodingVersionMismatch { pub version_array: String, pub rlink_version: u32, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_rustc_version_mismatch)] +#[diag( + ".rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`" +)] pub(crate) struct RLinkRustcVersionMismatch<'a> { pub rustc_version: String, pub current_version: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_rlink_no_a_file)] +#[diag("rlink must be a file")] pub(crate) struct RlinkNotAFile; #[derive(Diagnostic)] -#[diag(driver_impl_rlink_corrupt_file)] +#[diag("corrupt metadata encountered in `{$file}`")] pub(crate) struct RlinkCorruptFile<'a> { pub file: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(driver_impl_ice)] +#[diag("the compiler unexpectedly panicked. this is a bug.")] pub(crate) struct Ice; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report)] +#[diag("we would appreciate a bug report: {$bug_report_url}")] pub(crate) struct IceBugReport<'a> { pub bug_report_url: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_update_note)] +#[diag("please make sure that you have updated to the latest nightly")] pub(crate) struct UpdateNightlyNote; #[derive(Diagnostic)] -#[diag(driver_impl_ice_bug_report_internal_feature)] +#[diag( + "using internal features is not supported and expected to cause internal compiler errors when used incorrectly" +)] pub(crate) struct IceBugReportInternalFeature; #[derive(Diagnostic)] -#[diag(driver_impl_ice_version)] +#[diag("rustc {$version} running on {$triple}")] pub(crate) struct IceVersion<'a> { pub version: &'a str, pub triple: &'a str, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_path)] +#[diag("please attach the file at `{$path}` to your bug report")] pub(crate) struct IcePath { pub path: std::path::PathBuf, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_path_error)] +#[diag("the ICE couldn't be written to `{$path}`: {$error}")] pub(crate) struct IcePathError { pub path: std::path::PathBuf, pub error: String, @@ -87,23 +93,23 @@ pub(crate) struct IcePathError { } #[derive(Subdiagnostic)] -#[note(driver_impl_ice_path_error_env)] +#[note("the environment variable `RUSTC_ICE` is set to `{$env_var}`")] pub(crate) struct IcePathErrorEnv { pub env_var: std::path::PathBuf, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_flags)] +#[diag("compiler flags: {$flags}")] pub(crate) struct IceFlags { pub flags: String, } #[derive(Diagnostic)] -#[diag(driver_impl_ice_exclude_cargo_defaults)] +#[diag("some of the compiler flags provided by cargo are hidden")] pub(crate) struct IceExcludeCargoDefaults; #[derive(Diagnostic)] -#[diag(driver_impl_unstable_feature_usage)] +#[diag("cannot dump feature usage metrics: {$error}")] pub(crate) struct UnstableFeatureUsage { pub error: Box, } From 079913ec7169bae6f48528d40da03a5da321dbe4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 3 Feb 2026 09:02:27 +0100 Subject: [PATCH 284/319] disable socket tests in Miri --- library/std/tests/windows_unix_socket.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/std/tests/windows_unix_socket.rs b/library/std/tests/windows_unix_socket.rs index 18f4c52e72c2..1f20cf586ca2 100644 --- a/library/std/tests/windows_unix_socket.rs +++ b/library/std/tests/windows_unix_socket.rs @@ -1,4 +1,5 @@ #![cfg(windows)] +#![cfg(not(miri))] // no socket support in Miri #![feature(windows_unix_domain_sockets)] // Now only test windows_unix_domain_sockets feature // in the future, will test both unix and windows uds From e9ec12a2256471dcf0d6227e7ec13d6ea49fd7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lauren=C8=9Biu=20Nicola?= Date: Tue, 27 Jan 2026 19:00:26 +0200 Subject: [PATCH 285/319] Fix postcard test too --- .../crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs index 33ca1d791de7..ba9657a9bb45 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv-cli/tests/bidirectional_postcard.rs @@ -1,4 +1,8 @@ #![cfg(feature = "sysroot-abi")] +#![cfg_attr(feature = "in-rust-tree", feature(rustc_private))] + +#[cfg(feature = "in-rust-tree")] +extern crate rustc_driver as _; mod common { pub(crate) mod utils; From a9f81ea43e45eb4d64c93099be6b7d2e47a9290b Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 09:59:11 +0100 Subject: [PATCH 286/319] Convert to inline diagnostics in `rustc_attr_parsing` --- Cargo.lock | 2 - compiler/rustc_attr_parsing/Cargo.toml | 1 - compiler/rustc_attr_parsing/messages.ftl | 246 ----------------- .../rustc_attr_parsing/src/attributes/cfg.rs | 4 +- .../src/attributes/link_attrs.rs | 13 +- compiler/rustc_attr_parsing/src/lib.rs | 2 - .../src/session_diagnostics.rs | 250 ++++++++++-------- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - 9 files changed, 142 insertions(+), 378 deletions(-) delete mode 100644 compiler/rustc_attr_parsing/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..04d65e0a4999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3543,7 +3543,6 @@ dependencies = [ "rustc_ast_pretty", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hir", "rustc_lexer", "rustc_macros", @@ -3780,7 +3779,6 @@ dependencies = [ "rustc_ast_lowering", "rustc_ast_passes", "rustc_ast_pretty", - "rustc_attr_parsing", "rustc_borrowck", "rustc_builtin_macros", "rustc_codegen_ssa", diff --git a/compiler/rustc_attr_parsing/Cargo.toml b/compiler/rustc_attr_parsing/Cargo.toml index 79193f394fe4..411f3f5ccbd1 100644 --- a/compiler/rustc_attr_parsing/Cargo.toml +++ b/compiler/rustc_attr_parsing/Cargo.toml @@ -10,7 +10,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_lexer = { path = "../rustc_lexer" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_attr_parsing/messages.ftl b/compiler/rustc_attr_parsing/messages.ftl deleted file mode 100644 index 3e4c1a9dfad8..000000000000 --- a/compiler/rustc_attr_parsing/messages.ftl +++ /dev/null @@ -1,246 +0,0 @@ -attr_parsing_as_needed_compatibility = - linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds - -attr_parsing_bundle_needs_static = - linking modifier `bundle` is only compatible with `static` linking kind - -attr_parsing_cfg_attr_bad_delim = wrong `cfg_attr` delimiters - -attr_parsing_deprecated_item_suggestion = - suggestions on deprecated items are unstable - .help = add `#![feature(deprecated_suggestion)]` to the crate root - .note = see #94785 for more details - -attr_parsing_doc_alias_bad_char = - {$char_} character isn't allowed in {$attr_str} - -attr_parsing_doc_alias_empty = - {$attr_str} attribute cannot have empty value - -attr_parsing_doc_alias_malformed = - doc alias attribute expects a string `#[doc(alias = "a")]` or a list of strings `#[doc(alias("a", "b"))]` - -attr_parsing_doc_alias_start_end = - {$attr_str} cannot start or end with ' ' - -attr_parsing_doc_attr_not_crate_level = - `#![doc({$attr_name} = "...")]` isn't allowed as a crate-level attribute - -attr_parsing_doc_attribute_not_attribute = - nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = "...")]` - .help = only existing builtin attributes are allowed in core/std - -attr_parsing_doc_keyword_not_keyword = - nonexistent keyword `{$keyword}` used in `#[doc(keyword = "...")]` - .help = only existing keywords are allowed in core/std - -attr_parsing_empty_confusables = - expected at least one confusable name - -attr_parsing_empty_link_name = - link name must not be empty - .label = empty link name - -attr_parsing_expected_single_version_literal = - expected single version literal - -attr_parsing_expected_version_literal = - expected a version literal - -attr_parsing_expects_feature_list = - `{$name}` expects a list of feature names - -attr_parsing_expects_features = - `{$name}` expects feature names - -attr_parsing_import_name_type_raw = - import name type can only be used with link kind `raw-dylib` - -attr_parsing_import_name_type_x86 = - import name type is only supported on x86 - -attr_parsing_incompatible_wasm_link = - `wasm_import_module` is incompatible with other arguments in `#[link]` attributes - -attr_parsing_incorrect_repr_format_align_one_arg = - incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses - -attr_parsing_incorrect_repr_format_expect_literal_integer = - incorrect `repr(align)` attribute format: `align` expects a literal integer as argument - -attr_parsing_incorrect_repr_format_generic = - incorrect `repr({$repr_arg})` attribute format - .suggestion = use parentheses instead - -attr_parsing_incorrect_repr_format_packed_expect_integer = - incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument - -attr_parsing_incorrect_repr_format_packed_one_or_zero_arg = - incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all - -attr_parsing_invalid_alignment_value = - invalid alignment value: {$error_part} - -attr_parsing_invalid_attr_unsafe = `{$name}` is not an unsafe attribute - .label = this is not an unsafe attribute - .suggestion = remove the `unsafe(...)` - .note = extraneous unsafe is not allowed in attributes - -attr_parsing_invalid_issue_string = - `issue` must be a non-zero numeric string or "none" - .must_not_be_zero = `issue` must not be "0", use "none" instead - .empty = cannot parse integer from empty string - .invalid_digit = invalid digit found in string - .pos_overflow = number too large to fit in target type - .neg_overflow = number too small to fit in target type - -attr_parsing_invalid_link_modifier = - invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed - -attr_parsing_invalid_meta_item = expected a literal (`1u8`, `1.0f32`, `"string"`, etc.) here, found {$descr} - .remove_neg_sugg = negative numbers are not literals, try removing the `-` sign - .quote_ident_sugg = surround the identifier with quotation marks to make it into a string literal - .label = {$descr}s are not allowed here - -attr_parsing_invalid_predicate = - invalid predicate `{$predicate}` - -attr_parsing_invalid_repr_align_need_arg = - invalid `repr(align)` attribute: `align` needs an argument - .suggestion = supply an argument here - -attr_parsing_invalid_repr_generic = - invalid `repr({$repr_arg})` attribute: {$error_part} - -attr_parsing_invalid_repr_hint_no_paren = - invalid representation hint: `{$name}` does not take a parenthesized argument list - -attr_parsing_invalid_repr_hint_no_value = - invalid representation hint: `{$name}` does not take a value - -attr_parsing_invalid_since = - 'since' must be a Rust version number, such as "1.31.0" - -attr_parsing_invalid_target = `#[{$name}]` attribute cannot be used on {$target} - .help = `#[{$name}]` can {$only}be applied to {$applied} - .suggestion = remove the attribute - -attr_parsing_limit_invalid = - `limit` must be a non-negative integer - .label = {$error_str} -attr_parsing_link_arg_unstable = - link kind `link-arg` is unstable - -attr_parsing_link_cfg_unstable = - link cfg is unstable - -attr_parsing_link_framework_apple = - link kind `framework` is only supported on Apple targets - -attr_parsing_link_ordinal_out_of_range = ordinal value in `link_ordinal` is too large: `{$ordinal}` - .note = the value may not exceed `u16::MAX` - -attr_parsing_link_requires_name = - `#[link]` attribute requires a `name = "string"` argument - .label = missing `name` argument - -attr_parsing_meta_bad_delim = wrong meta list delimiters -attr_parsing_meta_bad_delim_suggestion = the delimiters should be `(` and `)` - -attr_parsing_missing_feature = - missing 'feature' - -attr_parsing_missing_issue = - missing 'issue' - -attr_parsing_missing_note = - missing 'note' - -attr_parsing_missing_since = - missing 'since' - -attr_parsing_multiple_modifiers = - multiple `{$modifier}` modifiers in a single `modifiers` argument - -attr_parsing_multiple_stability_levels = - multiple stability levels - -attr_parsing_naked_functions_incompatible_attribute = - attribute incompatible with `#[unsafe(naked)]` - .label = the `{$attr}` attribute is incompatible with `#[unsafe(naked)]` - .naked_attribute = function marked with `#[unsafe(naked)]` here - -attr_parsing_non_ident_feature = - 'feature' is not an identifier - -attr_parsing_null_on_export = `export_name` may not contain null characters - -attr_parsing_null_on_link_section = `link_section` may not contain null characters - -attr_parsing_null_on_objc_class = `objc::class!` may not contain null characters - -attr_parsing_null_on_objc_selector = `objc::selector!` may not contain null characters - -attr_parsing_objc_class_expected_string_literal = `objc::class!` expected a string literal - -attr_parsing_objc_selector_expected_string_literal = `objc::selector!` expected a string literal - -attr_parsing_raw_dylib_elf_unstable = - link kind `raw-dylib` is unstable on ELF platforms - -attr_parsing_raw_dylib_no_nul = - link name must not contain NUL characters if link kind is `raw-dylib` - -attr_parsing_raw_dylib_only_windows = - link kind `raw-dylib` is only supported on Windows targets - -attr_parsing_repr_ident = - meta item in `repr` must be an identifier - -attr_parsing_rustc_allowed_unstable_pairing = - `rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute - -attr_parsing_rustc_promotable_pairing = - `rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute - -attr_parsing_rustc_scalable_vector_count_out_of_range = element count in `rustc_scalable_vector` is too large: `{$n}` - .note = the value may not exceed `u16::MAX` - -attr_parsing_soft_no_args = - `soft` should not have any arguments - -attr_parsing_stability_outside_std = stability attributes may not be used outside of the standard library - -attr_parsing_suffixed_literal_in_attribute = suffixed literals are not allowed in attributes - .help = instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.) - -attr_parsing_unknown_version_literal = - unknown version literal format, assuming it refers to a future version - -attr_parsing_unrecognized_repr_hint = - unrecognized representation hint - .help = valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize` - .note = for more information, visit - -attr_parsing_unsafe_attr_outside_unsafe = unsafe attribute used without unsafe - .label = usage of unsafe attribute -attr_parsing_unsafe_attr_outside_unsafe_suggestion = wrap the attribute in `unsafe(...)` - -attr_parsing_unstable_cfg_target_compact = - compact `cfg(target(..))` is experimental and subject to change - -attr_parsing_unstable_feature_bound_incompatible_stability = item annotated with `#[unstable_feature_bound]` should not be stable - .help = if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]` - -attr_parsing_unsupported_instruction_set = target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]` - -attr_parsing_unsupported_literal_suggestion = - consider removing the prefix - -attr_parsing_unused_multiple = - multiple `{$name}` attributes - .suggestion = remove this attribute - .note = attribute also specified here - -attr_parsing_whole_archive_needs_static = - linking modifier `whole-archive` is only compatible with `static` linking kind diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index 3a540d80998d..ca6bfdbeaf5f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -25,7 +25,7 @@ use crate::session_diagnostics::{ AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg, ParsedDescription, }; -use crate::{AttributeParser, fluent_generated, parse_version, session_diagnostics}; +use crate::{AttributeParser, parse_version, session_diagnostics}; pub const CFG_TEMPLATE: AttributeTemplate = template!( List: &["predicate"], @@ -141,7 +141,7 @@ fn parse_cfg_entry_target( cx.sess(), sym::cfg_target_compact, meta_span, - fluent_generated::attr_parsing_unstable_cfg_target_compact, + "compact `cfg(target(..))` is experimental and subject to change", ) .emit(); } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 548f53a986b8..02723e96ad19 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -10,7 +10,6 @@ use rustc_target::spec::{Arch, BinaryFormat}; use super::prelude::*; use super::util::parse_single_integer; use crate::attributes::cfg::parse_cfg_entry; -use crate::fluent_generated; use crate::session_diagnostics::{ AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange, @@ -305,7 +304,7 @@ impl LinkParser { sess, sym::raw_dylib_elf, nv.value_span, - fluent_generated::attr_parsing_raw_dylib_elf_unstable, + "link kind `raw-dylib` is unstable on ELF platforms", ) .emit(); } else { @@ -320,7 +319,7 @@ impl LinkParser { sess, sym::link_arg_attribute, nv.value_span, - fluent_generated::attr_parsing_link_arg_unstable, + "link kind `link-arg` is unstable", ) .emit(); } @@ -385,13 +384,7 @@ impl LinkParser { return true; }; if !features.link_cfg() { - feature_err( - sess, - sym::link_cfg, - item.span(), - fluent_generated::attr_parsing_link_cfg_unstable, - ) - .emit(); + feature_err(sess, sym::link_cfg, item.span(), "link cfg is unstable").emit(); } *cfg = parse_cfg_entry(cx, link_cfg).ok(); true diff --git a/compiler/rustc_attr_parsing/src/lib.rs b/compiler/rustc_attr_parsing/src/lib.rs index 349e6c234520..fe050250e354 100644 --- a/compiler/rustc_attr_parsing/src/lib.rs +++ b/compiler/rustc_attr_parsing/src/lib.rs @@ -113,5 +113,3 @@ pub use attributes::util::{is_builtin_attr, parse_version}; pub use context::{Early, Late, OmitDoc, ShouldEmit}; pub use interface::AttributeParser; pub use session_diagnostics::ParsedDescription; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f9748542beb9..4055b7463f30 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -11,10 +11,8 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_predicate, code = E0537)] +#[diag("invalid predicate `{$predicate}`", code = E0537)] pub(crate) struct InvalidPredicate { #[primary_span] pub span: Span, @@ -23,7 +21,7 @@ pub(crate) struct InvalidPredicate { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_empty)] +#[diag("{$attr_str} attribute cannot have empty value")] pub(crate) struct DocAliasEmpty<'a> { #[primary_span] pub span: Span, @@ -31,7 +29,7 @@ pub(crate) struct DocAliasEmpty<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_bad_char)] +#[diag("{$char_} character isn't allowed in {$attr_str}")] pub(crate) struct DocAliasBadChar<'a> { #[primary_span] pub span: Span, @@ -40,7 +38,7 @@ pub(crate) struct DocAliasBadChar<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_start_end)] +#[diag("{$attr_str} cannot start or end with ' '")] pub(crate) struct DocAliasStartEnd<'a> { #[primary_span] pub span: Span, @@ -48,7 +46,7 @@ pub(crate) struct DocAliasStartEnd<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_attr_not_crate_level)] +#[diag("`#![doc({$attr_name} = \"...\")]` isn't allowed as a crate-level attribute")] pub(crate) struct DocAttrNotCrateLevel { #[primary_span] pub span: Span, @@ -56,8 +54,8 @@ pub(crate) struct DocAttrNotCrateLevel { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_keyword_not_keyword)] -#[help] +#[diag("nonexistent keyword `{$keyword}` used in `#[doc(keyword = \"...\")]`")] +#[help("only existing keywords are allowed in core/std")] pub(crate) struct DocKeywordNotKeyword { #[primary_span] pub span: Span, @@ -65,8 +63,8 @@ pub(crate) struct DocKeywordNotKeyword { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_attribute_not_attribute)] -#[help] +#[diag("nonexistent builtin attribute `{$attribute}` used in `#[doc(attribute = \"...\")]`")] +#[help("only existing builtin attributes are allowed in core/std")] pub(crate) struct DocAttributeNotAttribute { #[primary_span] pub span: Span, @@ -74,28 +72,28 @@ pub(crate) struct DocAttributeNotAttribute { } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_since, code = E0542)] +#[diag("missing 'since'", code = E0542)] pub(crate) struct MissingSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_note, code = E0543)] +#[diag("missing 'note'", code = E0543)] pub(crate) struct MissingNote { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_multiple_stability_levels, code = E0544)] +#[diag("multiple stability levels", code = E0544)] pub(crate) struct MultipleStabilityLevels { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_issue_string, code = E0545)] +#[diag("`issue` must be a non-zero numeric string or \"none\"", code = E0545)] pub(crate) struct InvalidIssueString { #[primary_span] pub span: Span, @@ -108,31 +106,31 @@ pub(crate) struct InvalidIssueString { // translatable. #[derive(Subdiagnostic)] pub(crate) enum InvalidIssueStringCause { - #[label(attr_parsing_must_not_be_zero)] + #[label("`issue` must not be \"0\", use \"none\" instead")] MustNotBeZero { #[primary_span] span: Span, }, - #[label(attr_parsing_empty)] + #[label("cannot parse integer from empty string")] Empty { #[primary_span] span: Span, }, - #[label(attr_parsing_invalid_digit)] + #[label("invalid digit found in string")] InvalidDigit { #[primary_span] span: Span, }, - #[label(attr_parsing_pos_overflow)] + #[label("number too large to fit in target type")] PosOverflow { #[primary_span] span: Span, }, - #[label(attr_parsing_neg_overflow)] + #[label("number too small to fit in target type")] NegOverflow { #[primary_span] span: Span, @@ -153,21 +151,21 @@ impl InvalidIssueStringCause { } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_feature, code = E0546)] +#[diag("missing 'feature'", code = E0546)] pub(crate) struct MissingFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_non_ident_feature, code = E0546)] +#[diag("'feature' is not an identifier", code = E0546)] pub(crate) struct NonIdentFeature { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_missing_issue, code = E0547)] +#[diag("missing 'issue'", code = E0547)] pub(crate) struct MissingIssue { #[primary_span] pub span: Span, @@ -176,20 +174,20 @@ pub(crate) struct MissingIssue { // FIXME: Why is this the same error code as `InvalidReprHintNoParen` and `InvalidReprHintNoValue`? // It is more similar to `IncorrectReprFormatGeneric`. #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_packed_one_or_zero_arg, code = E0552)] +#[diag("incorrect `repr(packed)` attribute format: `packed` takes exactly one parenthesized argument, or no parentheses at all", code = E0552)] pub(crate) struct IncorrectReprFormatPackedOneOrZeroArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_packed_expect_integer, code = E0552)] +#[diag("incorrect `repr(packed)` attribute format: `packed` expects a literal integer as argument", code = E0552)] pub(crate) struct IncorrectReprFormatPackedExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_hint_no_paren, code = E0552)] +#[diag("invalid representation hint: `{$name}` does not take a parenthesized argument list", code = E0552)] pub(crate) struct InvalidReprHintNoParen { #[primary_span] pub span: Span, @@ -198,7 +196,7 @@ pub(crate) struct InvalidReprHintNoParen { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_hint_no_value, code = E0552)] +#[diag("invalid representation hint: `{$name}` does not take a value", code = E0552)] pub(crate) struct InvalidReprHintNoValue { #[primary_span] pub span: Span, @@ -207,15 +205,19 @@ pub(crate) struct InvalidReprHintNoValue { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_align_need_arg, code = E0589)] +#[diag("invalid `repr(align)` attribute: `align` needs an argument", code = E0589)] pub(crate) struct InvalidReprAlignNeedArg { #[primary_span] - #[suggestion(code = "align(...)", applicability = "has-placeholders")] + #[suggestion( + "supply an argument here", + code = "align(...)", + applicability = "has-placeholders" + )] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_repr_generic, code = E0589)] +#[diag("invalid `repr({$repr_arg})` attribute: {$error_part}", code = E0589)] pub(crate) struct InvalidReprGeneric<'a> { #[primary_span] pub span: Span, @@ -225,21 +227,21 @@ pub(crate) struct InvalidReprGeneric<'a> { } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_align_one_arg, code = E0693)] +#[diag("incorrect `repr(align)` attribute format: `align` takes exactly one argument in parentheses", code = E0693)] pub(crate) struct IncorrectReprFormatAlignOneArg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_expect_literal_integer, code = E0693)] +#[diag("incorrect `repr(align)` attribute format: `align` expects a literal integer as argument", code = E0693)] pub(crate) struct IncorrectReprFormatExpectInteger { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incorrect_repr_format_generic, code = E0693)] +#[diag("incorrect `repr({$repr_arg})` attribute format", code = E0693)] pub(crate) struct IncorrectReprFormatGeneric { #[primary_span] pub span: Span, @@ -253,7 +255,7 @@ pub(crate) struct IncorrectReprFormatGeneric { #[derive(Subdiagnostic)] pub(crate) enum IncorrectReprFormatGenericCause { #[suggestion( - attr_parsing_suggestion, + "use parentheses instead", code = "{name}({value})", applicability = "machine-applicable" )] @@ -269,7 +271,7 @@ pub(crate) enum IncorrectReprFormatGenericCause { }, #[suggestion( - attr_parsing_suggestion, + "use parentheses instead", code = "{name}({value})", applicability = "machine-applicable" )] @@ -298,48 +300,48 @@ impl IncorrectReprFormatGenericCause { } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_promotable_pairing, code = E0717)] +#[diag("`rustc_promotable` attribute must be paired with either a `rustc_const_unstable` or a `rustc_const_stable` attribute", code = E0717)] pub(crate) struct RustcPromotablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_allowed_unstable_pairing, code = E0789)] +#[diag("`rustc_allowed_through_unstable_modules` attribute must be paired with a `stable` attribute", code = E0789)] pub(crate) struct RustcAllowedUnstablePairing { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_deprecated_item_suggestion)] +#[diag("suggestions on deprecated items are unstable")] pub(crate) struct DeprecatedItemSuggestion { #[primary_span] pub span: Span, - #[help] + #[help("add `#![feature(deprecated_suggestion)]` to the crate root")] pub is_nightly: bool, - #[note] + #[note("see #94785 for more details")] pub details: (), } #[derive(Diagnostic)] -#[diag(attr_parsing_expected_single_version_literal)] +#[diag("expected single version literal")] pub(crate) struct ExpectedSingleVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_expected_version_literal)] +#[diag("expected a version literal")] pub(crate) struct ExpectedVersionLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_expects_feature_list)] +#[diag("`{$name}` expects a list of feature names")] pub(crate) struct ExpectsFeatureList { #[primary_span] pub span: Span, @@ -348,7 +350,7 @@ pub(crate) struct ExpectsFeatureList { } #[derive(Diagnostic)] -#[diag(attr_parsing_expects_features)] +#[diag("`{$name}` expects feature names")] pub(crate) struct ExpectsFeatures { #[primary_span] pub span: Span, @@ -357,21 +359,21 @@ pub(crate) struct ExpectsFeatures { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_since)] +#[diag("'since' must be a Rust version number, such as \"1.31.0\"")] pub(crate) struct InvalidSince { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_soft_no_args)] +#[diag("`soft` should not have any arguments")] pub(crate) struct SoftNoArgs { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unknown_version_literal)] +#[diag("unknown version literal format, assuming it refers to a future version")] pub(crate) struct UnknownVersionLiteral { #[primary_span] pub span: Span, @@ -379,78 +381,83 @@ pub(crate) struct UnknownVersionLiteral { // FIXME(jdonszelmann) duplicated from `rustc_passes`, remove once `check_attr` is integrated. #[derive(Diagnostic)] -#[diag(attr_parsing_unused_multiple)] +#[diag("multiple `{$name}` attributes")] pub(crate) struct UnusedMultiple { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable")] + #[suggestion("remove this attribute", code = "", applicability = "machine-applicable")] pub this: Span, - #[note] + #[note("attribute also specified here")] pub other: Span, pub name: Symbol, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_export, code = E0648)] +#[diag("`export_name` may not contain null characters", code = E0648)] pub(crate) struct NullOnExport { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_link_section, code = E0648)] +#[diag("`link_section` may not contain null characters", code = E0648)] pub(crate) struct NullOnLinkSection { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_objc_class)] +#[diag("`objc::class!` may not contain null characters")] pub(crate) struct NullOnObjcClass { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_null_on_objc_selector)] +#[diag("`objc::selector!` may not contain null characters")] pub(crate) struct NullOnObjcSelector { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_objc_class_expected_string_literal)] +#[diag("`objc::class!` expected a string literal")] pub(crate) struct ObjcClassExpectedStringLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_objc_selector_expected_string_literal)] +#[diag("`objc::selector!` expected a string literal")] pub(crate) struct ObjcSelectorExpectedStringLiteral { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_stability_outside_std, code = E0734)] +#[diag("stability attributes may not be used outside of the standard library", code = E0734)] pub(crate) struct StabilityOutsideStd { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_empty_confusables)] +#[diag("expected at least one confusable name")] pub(crate) struct EmptyConfusables { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[help] -#[diag(attr_parsing_invalid_target)] +#[help("`#[{$name}]` can {$only}be applied to {$applied}")] +#[diag("`#[{$name}]` attribute cannot be used on {$target}")] pub(crate) struct InvalidTarget { #[primary_span] - #[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")] + #[suggestion( + "remove the attribute", + code = "", + applicability = "machine-applicable", + style = "tool-only" + )] pub span: Span, pub name: AttrPath, pub target: &'static str, @@ -459,7 +466,7 @@ pub(crate) struct InvalidTarget { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_alignment_value, code = E0589)] +#[diag("invalid alignment value: {$error_part}", code = E0589)] pub(crate) struct InvalidAlignmentValue { #[primary_span] pub span: Span, @@ -467,43 +474,49 @@ pub(crate) struct InvalidAlignmentValue { } #[derive(Diagnostic)] -#[diag(attr_parsing_repr_ident, code = E0565)] +#[diag("meta item in `repr` must be an identifier", code = E0565)] pub(crate) struct ReprIdent { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unrecognized_repr_hint, code = E0552)] -#[help] -#[note] +#[diag("unrecognized representation hint", code = E0552)] +#[help( + "valid reprs are `Rust` (default), `C`, `align`, `packed`, `transparent`, `simd`, `i8`, `u8`, `i16`, `u16`, `i32`, `u32`, `i64`, `u64`, `i128`, `u128`, `isize`, `usize`" +)] +#[note( + "for more information, visit " +)] pub(crate) struct UnrecognizedReprHint { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unstable_feature_bound_incompatible_stability)] -#[help] +#[diag("item annotated with `#[unstable_feature_bound]` should not be stable")] +#[help( + "if this item is meant to be stable, do not use any functions annotated with `#[unstable_feature_bound]`. Otherwise, mark this item as unstable with `#[unstable]`" +)] pub(crate) struct UnstableFeatureBoundIncompatibleStability { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_naked_functions_incompatible_attribute, code = E0736)] +#[diag("attribute incompatible with `#[unsafe(naked)]`", code = E0736)] pub(crate) struct NakedFunctionIncompatibleAttribute { #[primary_span] - #[label] + #[label("the `{$attr}` attribute is incompatible with `#[unsafe(naked)]`")] pub span: Span, - #[label(attr_parsing_naked_attribute)] + #[label("function marked with `#[unsafe(naked)]` here")] pub naked_span: Span, pub attr: String, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_ordinal_out_of_range)] -#[note] +#[diag("ordinal value in `link_ordinal` is too large: `{$ordinal}`")] +#[note("the value may not exceed `u16::MAX`")] pub(crate) struct LinkOrdinalOutOfRange { #[primary_span] pub span: Span, @@ -511,8 +524,8 @@ pub(crate) struct LinkOrdinalOutOfRange { } #[derive(Diagnostic)] -#[diag(attr_parsing_rustc_scalable_vector_count_out_of_range)] -#[note] +#[diag("element count in `rustc_scalable_vector` is too large: `{$n}`")] +#[note("the value may not exceed `u16::MAX`")] pub(crate) struct RustcScalableVectorCountOutOfRange { #[primary_span] pub span: Span, @@ -586,7 +599,7 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { if let Some(start_point_span) = byte_string { diag.span_suggestion( start_point_span, - fluent::attr_parsing_unsupported_literal_suggestion, + "consider removing the prefix", "", Applicability::MaybeIncorrect, ); @@ -751,30 +764,27 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_attr_unsafe)] -#[note] +#[diag("`{$name}` is not an unsafe attribute")] +#[note("extraneous unsafe is not allowed in attributes")] pub(crate) struct InvalidAttrUnsafe { #[primary_span] - #[label] + #[label("this is not an unsafe attribute")] pub span: Span, pub name: AttrPath, } #[derive(Diagnostic)] -#[diag(attr_parsing_unsafe_attr_outside_unsafe)] +#[diag("unsafe attribute used without unsafe")] pub(crate) struct UnsafeAttrOutsideUnsafe { #[primary_span] - #[label] + #[label("usage of unsafe attribute")] pub span: Span, #[subdiagnostic] pub suggestion: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion( - attr_parsing_unsafe_attr_outside_unsafe_suggestion, - applicability = "machine-applicable" -)] +#[multipart_suggestion("wrap the attribute in `unsafe(...)`", applicability = "machine-applicable")] pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { #[suggestion_part(code = "unsafe(")] pub left: Span, @@ -783,7 +793,7 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion { } #[derive(Diagnostic)] -#[diag(attr_parsing_meta_bad_delim)] +#[diag("wrong meta list delimiters")] pub(crate) struct MetaBadDelim { #[primary_span] pub span: Span, @@ -793,7 +803,7 @@ pub(crate) struct MetaBadDelim { #[derive(Subdiagnostic)] #[multipart_suggestion( - attr_parsing_meta_bad_delim_suggestion, + "the delimiters should be `(` and `)`", applicability = "machine-applicable" )] pub(crate) struct MetaBadDelimSugg { @@ -804,7 +814,7 @@ pub(crate) struct MetaBadDelimSugg { } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_meta_item)] +#[diag("expected a literal (`1u8`, `1.0f32`, `\"string\"`, etc.) here, found {$descr}")] pub(crate) struct InvalidMetaItem { #[primary_span] pub span: Span, @@ -813,12 +823,15 @@ pub(crate) struct InvalidMetaItem { pub quote_ident_sugg: Option, #[subdiagnostic] pub remove_neg_sugg: Option, - #[label] + #[label("{$descr}s are not allowed here")] pub label: Option, } #[derive(Subdiagnostic)] -#[multipart_suggestion(attr_parsing_quote_ident_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "surround the identifier with quotation marks to make it into a string literal", + applicability = "machine-applicable" +)] pub(crate) struct InvalidMetaItemQuoteIdentSugg { #[suggestion_part(code = "\"")] pub before: Span, @@ -827,73 +840,80 @@ pub(crate) struct InvalidMetaItemQuoteIdentSugg { } #[derive(Subdiagnostic)] -#[multipart_suggestion(attr_parsing_remove_neg_sugg, applicability = "machine-applicable")] +#[multipart_suggestion( + "negative numbers are not literals, try removing the `-` sign", + applicability = "machine-applicable" +)] pub(crate) struct InvalidMetaItemRemoveNegSugg { #[suggestion_part(code = "")] pub negative_sign: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_suffixed_literal_in_attribute)] -#[help] +#[diag("suffixed literals are not allowed in attributes")] +#[help( + "instead of using a suffixed literal (`1u8`, `1.0f32`, etc.), use an unsuffixed version (`1`, `1.0`, etc.)" +)] pub(crate) struct SuffixedLiteralInAttribute { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_empty_link_name, code = E0454)] +#[diag("link name must not be empty", code = E0454)] pub(crate) struct EmptyLinkName { #[primary_span] - #[label] + #[label("empty link name")] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_framework_apple, code = E0455)] +#[diag("link kind `framework` is only supported on Apple targets", code = E0455)] pub(crate) struct LinkFrameworkApple { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_incompatible_wasm_link)] +#[diag("`wasm_import_module` is incompatible with other arguments in `#[link]` attributes")] pub(crate) struct IncompatibleWasmLink { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_link_requires_name, code = E0459)] +#[diag("`#[link]` attribute requires a `name = \"string\"` argument", code = E0459)] pub(crate) struct LinkRequiresName { #[primary_span] - #[label] + #[label("missing `name` argument")] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_raw_dylib_no_nul)] +#[diag("link name must not contain NUL characters if link kind is `raw-dylib`")] pub(crate) struct RawDylibNoNul { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_raw_dylib_only_windows, code = E0455)] +#[diag("link kind `raw-dylib` is only supported on Windows targets", code = E0455)] pub(crate) struct RawDylibOnlyWindows { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_invalid_link_modifier)] +#[diag( + "invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed" +)] pub(crate) struct InvalidLinkModifier { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_multiple_modifiers)] +#[diag("multiple `{$modifier}` modifiers in a single `modifiers` argument")] pub(crate) struct MultipleModifiers { #[primary_span] pub span: Span, @@ -901,52 +921,54 @@ pub(crate) struct MultipleModifiers { } #[derive(Diagnostic)] -#[diag(attr_parsing_import_name_type_x86)] +#[diag("import name type is only supported on x86")] pub(crate) struct ImportNameTypeX86 { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_bundle_needs_static)] +#[diag("linking modifier `bundle` is only compatible with `static` linking kind")] pub(crate) struct BundleNeedsStatic { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_whole_archive_needs_static)] +#[diag("linking modifier `whole-archive` is only compatible with `static` linking kind")] pub(crate) struct WholeArchiveNeedsStatic { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_as_needed_compatibility)] +#[diag( + "linking modifier `as-needed` is only compatible with `dylib`, `framework` and `raw-dylib` linking kinds" +)] pub(crate) struct AsNeededCompatibility { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_import_name_type_raw)] +#[diag("import name type can only be used with link kind `raw-dylib`")] pub(crate) struct ImportNameTypeRaw { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_limit_invalid)] +#[diag("`limit` must be a non-negative integer")] pub(crate) struct LimitInvalid<'a> { #[primary_span] pub span: Span, - #[label] + #[label("{$error_str}")] pub value_span: Span, pub error_str: &'a str, } #[derive(Diagnostic)] -#[diag(attr_parsing_cfg_attr_bad_delim)] +#[diag("wrong `cfg_attr` delimiters")] pub(crate) struct CfgAttrBadDelim { #[primary_span] pub span: Span, @@ -955,14 +977,16 @@ pub(crate) struct CfgAttrBadDelim { } #[derive(Diagnostic)] -#[diag(attr_parsing_doc_alias_malformed)] +#[diag( + "doc alias attribute expects a string `#[doc(alias = \"a\")]` or a list of strings `#[doc(alias(\"a\", \"b\"))]`" +)] pub(crate) struct DocAliasMalformed { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(attr_parsing_unsupported_instruction_set)] +#[diag("target `{$current_target}` does not support `#[instruction_set({$instruction_set}::*)]`")] pub(crate) struct UnsupportedInstructionSet<'a> { #[primary_span] pub span: Span, diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..49b73e4392d1 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -12,7 +12,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_ast_lowering = { path = "../rustc_ast_lowering" } rustc_ast_passes = { path = "../rustc_ast_passes" } rustc_ast_pretty = { path = "../rustc_ast_pretty" } -rustc_attr_parsing = { path = "../rustc_attr_parsing" } rustc_borrowck = { path = "../rustc_borrowck" } rustc_builtin_macros = { path = "../rustc_builtin_macros" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..6738504e7219 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -119,7 +119,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ crate::DEFAULT_LOCALE_RESOURCE, rustc_ast_lowering::DEFAULT_LOCALE_RESOURCE, rustc_ast_passes::DEFAULT_LOCALE_RESOURCE, - rustc_attr_parsing::DEFAULT_LOCALE_RESOURCE, rustc_borrowck::DEFAULT_LOCALE_RESOURCE, rustc_builtin_macros::DEFAULT_LOCALE_RESOURCE, rustc_codegen_ssa::DEFAULT_LOCALE_RESOURCE, From e58538c55257e73f789bd671c8d96f0fecd4ff49 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Sun, 1 Feb 2026 22:18:59 +1100 Subject: [PATCH 287/319] Rename `collect_active_jobs` to several distinct names --- compiler/rustc_interface/src/util.rs | 2 +- compiler/rustc_query_impl/src/plumbing.rs | 40 ++++++++++++++----- compiler/rustc_query_system/src/query/job.rs | 4 +- compiler/rustc_query_system/src/query/mod.rs | 5 ++- .../rustc_query_system/src/query/plumbing.rs | 22 ++++++---- 5 files changed, 52 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 249368fd1194..25f59f0e89df 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -254,7 +254,7 @@ internal compiler error: query cycle handler thread panicked, aborting process"; || { // Ensure there were no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - QueryCtxt::new(tcx).collect_active_jobs(false).expect( + QueryCtxt::new(tcx).collect_active_jobs_from_all_queries(false).expect( "failed to collect active queries in deadlock handler", ) }, diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index 8be45d1fb464..d1721f1f455f 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -50,7 +50,9 @@ impl<'tcx> QueryCtxt<'tcx> { } fn depth_limit_error(self, job: QueryJobId) { - let query_map = self.collect_active_jobs(true).expect("failed to collect active queries"); + let query_map = self + .collect_active_jobs_from_all_queries(true) + .expect("failed to collect active queries"); let (info, depth) = job.find_dep_kind_root(query_map); let suggested_limit = match self.tcx.recursion_limit() { @@ -98,7 +100,7 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { tls::with_related_context(self.tcx, |icx| icx.query) } - /// Returns a map of currently active query jobs. + /// Returns a map of currently active query jobs, collected from all queries. /// /// If `require_complete` is `true`, this function locks all shards of the /// query results to produce a complete map, which always returns `Ok`. @@ -108,12 +110,15 @@ impl<'tcx> QueryContext<'tcx> for QueryCtxt<'tcx> { /// Prefer passing `false` to `require_complete` to avoid potential deadlocks, /// especially when called from within a deadlock handler, unless a /// complete map is needed and no deadlock is possible at this call site. - fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>> { + fn collect_active_jobs_from_all_queries( + self, + require_complete: bool, + ) -> Result, QueryMap<'tcx>> { let mut jobs = QueryMap::default(); let mut complete = true; - for collect in super::COLLECT_ACTIVE_JOBS.iter() { - if collect(self.tcx, &mut jobs, require_complete).is_none() { + for gather_fn in crate::PER_QUERY_GATHER_ACTIVE_JOBS_FNS.iter() { + if gather_fn(self.tcx, &mut jobs, require_complete).is_none() { complete = false; } } @@ -731,7 +736,10 @@ macro_rules! define_queries { } } - pub(crate) fn collect_active_jobs<'tcx>( + /// Internal per-query plumbing for collecting the set of active jobs for this query. + /// + /// Should only be called through `PER_QUERY_GATHER_ACTIVE_JOBS_FNS`. + pub(crate) fn gather_active_jobs<'tcx>( tcx: TyCtxt<'tcx>, qmap: &mut QueryMap<'tcx>, require_complete: bool, @@ -741,12 +749,15 @@ macro_rules! define_queries { let name = stringify!($name); $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) }; - let res = tcx.query_system.states.$name.collect_active_jobs( + + // Call `gather_active_jobs_inner` to do the actual work. + let res = tcx.query_system.states.$name.gather_active_jobs_inner( tcx, make_frame, qmap, require_complete, ); + // this can be called during unwinding, and the function has a `try_`-prefix, so // don't `unwrap()` here, just manually check for `None` and do best-effort error // reporting. @@ -816,10 +827,17 @@ macro_rules! define_queries { // These arrays are used for iteration and can't be indexed by `DepKind`. - const COLLECT_ACTIVE_JOBS: &[ - for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, bool) -> Option<()> - ] = - &[$(query_impl::$name::collect_active_jobs),*]; + /// Used by `collect_active_jobs_from_all_queries` to iterate over all + /// queries, and gather the active jobs for each query. + /// + /// (We arbitrarily use the word "gather" when collecting the jobs for + /// each individual query, so that we have distinct function names to + /// grep for.) + const PER_QUERY_GATHER_ACTIVE_JOBS_FNS: &[ + for<'tcx> fn(TyCtxt<'tcx>, &mut QueryMap<'tcx>, require_complete: bool) -> Option<()> + ] = &[ + $(query_impl::$name::gather_active_jobs),* + ]; const ALLOC_SELF_PROFILE_QUERY_STRINGS: &[ for<'tcx> fn(TyCtxt<'tcx>, &mut QueryKeyStringCache) diff --git a/compiler/rustc_query_system/src/query/job.rs b/compiler/rustc_query_system/src/query/job.rs index 82b23b022c37..50cb58f0b4d5 100644 --- a/compiler/rustc_query_system/src/query/job.rs +++ b/compiler/rustc_query_system/src/query/job.rs @@ -32,6 +32,8 @@ impl<'tcx> QueryInfo> { } } +/// Map from query job IDs to job information collected by +/// [`QueryContext::collect_active_jobs_from_all_queries`]. pub type QueryMap<'tcx> = FxHashMap>; /// A value uniquely identifying an active query job. @@ -613,7 +615,7 @@ pub fn print_query_stack<'tcx, Qcx: QueryContext<'tcx>>( let mut count_total = 0; // Make use of a partial query map if we fail to take locks collecting active queries. - let query_map = match qcx.collect_active_jobs(false) { + let query_map = match qcx.collect_active_jobs_from_all_queries(false) { Ok(query_map) => query_map, Err(query_map) => query_map, }; diff --git a/compiler/rustc_query_system/src/query/mod.rs b/compiler/rustc_query_system/src/query/mod.rs index 54e5fa4d7229..dbf7395bd61a 100644 --- a/compiler/rustc_query_system/src/query/mod.rs +++ b/compiler/rustc_query_system/src/query/mod.rs @@ -166,7 +166,10 @@ pub trait QueryContext<'tcx>: HasDepContext { /// Get the query information from the TLS context. fn current_query_job(self) -> Option; - fn collect_active_jobs(self, require_complete: bool) -> Result, QueryMap<'tcx>>; + fn collect_active_jobs_from_all_queries( + self, + require_complete: bool, + ) -> Result, QueryMap<'tcx>>; /// Load a side effect associated to the node in the previous session. fn load_side_effect( diff --git a/compiler/rustc_query_system/src/query/plumbing.rs b/compiler/rustc_query_system/src/query/plumbing.rs index 472839116107..fcd2e80a4fdc 100644 --- a/compiler/rustc_query_system/src/query/plumbing.rs +++ b/compiler/rustc_query_system/src/query/plumbing.rs @@ -11,7 +11,6 @@ use rustc_data_structures::fingerprint::Fingerprint; use rustc_data_structures::hash_table::{self, Entry, HashTable}; use rustc_data_structures::sharded::{self, Sharded}; use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_data_structures::sync::LockGuard; use rustc_data_structures::{outline, sync}; use rustc_errors::{Diag, FatalError, StashKey}; use rustc_span::{DUMMY_SP, Span}; @@ -79,7 +78,10 @@ where self.active.lock_shards().all(|shard| shard.is_empty()) } - pub fn collect_active_jobs( + /// Internal plumbing for collecting the set of active jobs for this query. + /// + /// Should only be called from `gather_active_jobs`. + pub fn gather_active_jobs_inner( &self, qcx: Qcx, make_frame: fn(Qcx, K) -> QueryStackFrame>, @@ -88,23 +90,26 @@ where ) -> Option<()> { let mut active = Vec::new(); - let mut collect = |iter: LockGuard<'_, HashTable<(K, ActiveKeyStatus<'tcx>)>>| { - for (k, v) in iter.iter() { + // Helper to gather active jobs from a single shard. + let mut gather_shard_jobs = |shard: &HashTable<(K, ActiveKeyStatus<'tcx>)>| { + for (k, v) in shard.iter() { if let ActiveKeyStatus::Started(ref job) = *v { active.push((*k, job.clone())); } } }; + // Lock shards and gather jobs from each shard. if require_complete { for shard in self.active.lock_shards() { - collect(shard); + gather_shard_jobs(&shard); } } else { // We use try_lock_shards here since we are called from the // deadlock handler, and this shouldn't be locked. for shard in self.active.try_lock_shards() { - collect(shard?); + let shard = shard?; + gather_shard_jobs(&shard); } } @@ -294,7 +299,10 @@ where { // Ensure there was no errors collecting all active jobs. // We need the complete map to ensure we find a cycle to break. - let query_map = qcx.collect_active_jobs(false).ok().expect("failed to collect active queries"); + let query_map = qcx + .collect_active_jobs_from_all_queries(false) + .ok() + .expect("failed to collect active queries"); let error = try_execute.find_cycle_in_stack(query_map, &qcx.current_query_job(), span); (mk_cycle(query, qcx, error.lift()), None) From f8fe49ea9e1c997c689df921e0da2460545e9c39 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 10:18:22 +0100 Subject: [PATCH 288/319] Introduce `inline_fluent` macro --- compiler/rustc_attr_parsing/src/attributes/cfg.rs | 4 ++-- .../rustc_attr_parsing/src/attributes/link_attrs.rs | 8 +++++--- compiler/rustc_errors/src/translation.rs | 11 +++++++++++ 3 files changed, 18 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index ca6bfdbeaf5f..3d885135cbe0 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -3,7 +3,7 @@ use std::convert::identity; use rustc_ast::token::Delimiter; use rustc_ast::tokenstream::DelimSpan; use rustc_ast::{AttrItem, Attribute, CRATE_NODE_ID, LitKind, ast, token}; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, PResult, inline_fluent}; use rustc_feature::{ AttrSuggestionStyle, AttributeTemplate, Features, GatedCfg, find_gated_cfg, template, }; @@ -141,7 +141,7 @@ fn parse_cfg_entry_target( cx.sess(), sym::cfg_target_compact, meta_span, - "compact `cfg(target(..))` is experimental and subject to change", + inline_fluent!("compact `cfg(target(..))` is experimental and subject to change"), ) .emit(); } diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index 02723e96ad19..c9da2f3b14bf 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -1,3 +1,4 @@ +use rustc_errors::inline_fluent; use rustc_feature::Features; use rustc_hir::attrs::AttributeKind::{LinkName, LinkOrdinal, LinkSection}; use rustc_hir::attrs::*; @@ -304,7 +305,7 @@ impl LinkParser { sess, sym::raw_dylib_elf, nv.value_span, - "link kind `raw-dylib` is unstable on ELF platforms", + inline_fluent!("link kind `raw-dylib` is unstable on ELF platforms"), ) .emit(); } else { @@ -319,7 +320,7 @@ impl LinkParser { sess, sym::link_arg_attribute, nv.value_span, - "link kind `link-arg` is unstable", + inline_fluent!("link kind `link-arg` is unstable"), ) .emit(); } @@ -384,7 +385,8 @@ impl LinkParser { return true; }; if !features.link_cfg() { - feature_err(sess, sym::link_cfg, item.span(), "link cfg is unstable").emit(); + feature_err(sess, sym::link_cfg, item.span(), inline_fluent!("link cfg is unstable")) + .emit(); } *cfg = parse_cfg_entry(cx, link_cfg).ok(); true diff --git a/compiler/rustc_errors/src/translation.rs b/compiler/rustc_errors/src/translation.rs index 0ee2b7b06090..aba79c30f3a3 100644 --- a/compiler/rustc_errors/src/translation.rs +++ b/compiler/rustc_errors/src/translation.rs @@ -166,3 +166,14 @@ impl Translator { } } } + +/// This macro creates a translatable `DiagMessage` from a literal string. +/// It should be used in places where a translatable message is needed, but struct diagnostics are undesired. +/// +/// This is a macro because in the future we may want to globally register these messages. +#[macro_export] +macro_rules! inline_fluent { + ($inline: literal) => { + rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed($inline)) + }; +} From 6b3d1e060490bd735fb3cd6048532297369a1893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 3 Feb 2026 11:47:30 +0100 Subject: [PATCH 289/319] Update Cargo.lock It probably wasn't committed after an earlier change. --- src/ci/citool/Cargo.lock | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/ci/citool/Cargo.lock b/src/ci/citool/Cargo.lock index 9e1ded4d3f7c..fe1c92f049e0 100644 --- a/src/ci/citool/Cargo.lock +++ b/src/ci/citool/Cargo.lock @@ -66,9 +66,9 @@ checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" [[package]] name = "askama" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb7125972258312e79827b60c9eb93938334100245081cf701a2dee981b17427" +checksum = "08e1676b346cadfec169374f949d7490fd80a24193d37d2afce0c047cf695e57" dependencies = [ "askama_macros", "itoa", @@ -79,9 +79,9 @@ dependencies = [ [[package]] name = "askama_derive" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ba5e7259a1580c61571e3116ebaaa01e3c001b2132b17c4cc5c70780ca3e994" +checksum = "7661ff56517787343f376f75db037426facd7c8d3049cef8911f1e75016f3a37" dependencies = [ "askama_parser", "basic-toml", @@ -96,18 +96,18 @@ dependencies = [ [[package]] name = "askama_macros" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "236ce20b77cb13506eaf5024899f4af6e12e8825f390bd943c4c37fd8f322e46" +checksum = "713ee4dbfd1eb719c2dab859465b01fa1d21cb566684614a713a6b7a99a4e47b" dependencies = [ "askama_derive", ] [[package]] name = "askama_parser" -version = "0.15.1" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c63392767bb2df6aa65a6e1e3b80fd89bb7af6d58359b924c0695620f1512e" +checksum = "1d62d674238a526418b30c0def480d5beadb9d8964e7f38d635b03bf639c704c" dependencies = [ "rustc-hash", "serde", From cb9c43b7e867299db5284bffce1f498356e5167f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Tue, 3 Feb 2026 11:47:53 +0100 Subject: [PATCH 290/319] Show largest job duration changes in hours and minutes, rather than just seconds --- src/ci/citool/src/analysis.rs | 31 ++++++++++++++++++++++++++----- src/ci/citool/src/main.rs | 4 ++-- 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 39b115154f9f..e91d27a36092 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -194,8 +194,8 @@ pub fn output_test_diffs( report_test_diffs(aggregated_test_diffs, job_metrics, job_info_resolver); } -/// Prints the ten largest differences in bootstrap durations. -pub fn output_largest_duration_changes( +/// Prints the ten largest differences in job durations. +pub fn output_largest_job_duration_changes( job_metrics: &HashMap, job_info_resolver: &mut JobInfoResolver, ) { @@ -237,11 +237,11 @@ pub fn output_largest_duration_changes( println!("# Job duration changes"); for (index, entry) in changes.into_iter().take(10).enumerate() { println!( - "{}. {}: {:.1}s -> {:.1}s ({:+.1}%)", + "{}. {}: {} -> {} ({:+.1}%)", index + 1, format_job_link(job_info_resolver, job_metrics, entry.job), - entry.before.as_secs_f64(), - entry.after.as_secs_f64(), + format_duration(entry.before), + format_duration(entry.after), entry.change ); } @@ -256,6 +256,27 @@ mostly for t-infra members, for simpler debugging of potential CI slow-downs."# }); } +fn format_duration(duration: Duration) -> String { + let total_secs = duration.as_secs(); + let hours = total_secs / 3600; + let minutes = (total_secs % 3600) / 60; + let seconds = total_secs % 60; + + let mut res = String::new(); + + if hours > 0 { + res.push_str(&format!("{hours}h ")); + } + if minutes > 0 { + res.push_str(&format!("{minutes}m ")); + } + if hours == 0 && seconds > 0 { + res.push_str(&format!("{seconds}s")); + } + + res.trim().to_string() +} + #[derive(Default)] struct TestSuiteRecord { passed: u64, diff --git a/src/ci/citool/src/main.rs b/src/ci/citool/src/main.rs index 01c0650b3c98..9b9cbe3862e3 100644 --- a/src/ci/citool/src/main.rs +++ b/src/ci/citool/src/main.rs @@ -17,7 +17,7 @@ use clap::Parser; use jobs::JobDatabase; use serde_yaml::Value; -use crate::analysis::{output_largest_duration_changes, output_test_diffs}; +use crate::analysis::{output_largest_job_duration_changes, output_test_diffs}; use crate::cpu_usage::load_cpu_usage; use crate::datadog::upload_datadog_metric; use crate::github::JobInfoResolver; @@ -205,7 +205,7 @@ And then open `test-dashboard/index.html` in your browser to see an overview of ); }); - output_largest_duration_changes(&metrics, &mut job_info_resolver); + output_largest_job_duration_changes(&metrics, &mut job_info_resolver); Ok(()) } From 0418f9aa42a9c9e7ca38a8c2a4a1e275c68a5154 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 3 Feb 2026 22:53:38 +1100 Subject: [PATCH 291/319] coverage: Add a test case for a previously-unknown span mismatch --- .../rustc_mir_transform/src/coverage/spans.rs | 8 ++-- .../context-mismatch-issue-147339.cov-map | 40 +++++++++++++++++++ .../context-mismatch-issue-147339.coverage | 28 +++++++++++++ .../macros/context-mismatch-issue-147339.rs | 20 ++++++++++ 4 files changed, 91 insertions(+), 5 deletions(-) create mode 100644 tests/coverage/macros/context-mismatch-issue-147339.cov-map create mode 100644 tests/coverage/macros/context-mismatch-issue-147339.coverage create mode 100644 tests/coverage/macros/context-mismatch-issue-147339.rs diff --git a/compiler/rustc_mir_transform/src/coverage/spans.rs b/compiler/rustc_mir_transform/src/coverage/spans.rs index b1ce0069b43a..bdc861e2cece 100644 --- a/compiler/rustc_mir_transform/src/coverage/spans.rs +++ b/compiler/rustc_mir_transform/src/coverage/spans.rs @@ -55,12 +55,10 @@ pub(super) fn extract_refined_covspans<'tcx>( } // Each pushed covspan should have the same context as the body span. - // If it somehow doesn't, discard the covspan, or panic in debug builds. + // If it somehow doesn't, discard the covspan. if !body_span.eq_ctxt(covspan_span) { - debug_assert!( - false, - "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}" - ); + // FIXME(Zalathar): Investigate how and why this is triggered + // by `tests/coverage/macros/context-mismatch-issue-147339.rs`. return false; } diff --git a/tests/coverage/macros/context-mismatch-issue-147339.cov-map b/tests/coverage/macros/context-mismatch-issue-147339.cov-map new file mode 100644 index 000000000000..7aa829cab72b --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.cov-map @@ -0,0 +1,40 @@ +Function name: context_mismatch_issue_147339::a (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::b (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::c (unused) +Raw bytes (14): 0x[01, 01, 00, 02, 00, 0c, 27, 00, 35, 00, 00, 3b, 00, 3c] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Zero) at (prev + 12, 39) to (start + 0, 53) +- Code(Zero) at (prev + 0, 59) to (start + 0, 60) +Highest counter ID seen: (none) + +Function name: context_mismatch_issue_147339::main +Raw bytes (14): 0x[01, 01, 00, 02, 01, 14, 01, 00, 0a, 01, 00, 0c, 00, 0d] +Number of files: 1 +- file 0 => $DIR/context-mismatch-issue-147339.rs +Number of expressions: 0 +Number of file 0 mappings: 2 +- Code(Counter(0)) at (prev + 20, 1) to (start + 0, 10) +- Code(Counter(0)) at (prev + 0, 12) to (start + 0, 13) +Highest counter ID seen: c0 + diff --git a/tests/coverage/macros/context-mismatch-issue-147339.coverage b/tests/coverage/macros/context-mismatch-issue-147339.coverage new file mode 100644 index 000000000000..9b4fc67b8dff --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.coverage @@ -0,0 +1,28 @@ + LL| |//@ edition: 2024 + LL| | + LL| |// These nested macro expansions were found to cause span refinement to produce + LL| |// spans with a context that doesn't match the function body span, triggering + LL| |// a defensive check that discards the span. + LL| |// + LL| |// Reported in . + LL| | + LL| |macro_rules! foo { + LL| | ($($m:ident $($f:ident $v:tt)+),*) => { + LL| | $($(macro_rules! $f { () => { $v } })+)* + LL| 0| $(macro_rules! $m { () => { $(fn $f() -> i32 { $v })+ } })* + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::a + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::b + ------------------ + | Unexecuted instantiation: context_mismatch_issue_147339::c + ------------------ + LL| | } + LL| |} + LL| | + LL| |foo!(m a 1 b 2, n c 3); + LL| |m!(); + LL| |n!(); + LL| | + LL| 1|fn main() {} + diff --git a/tests/coverage/macros/context-mismatch-issue-147339.rs b/tests/coverage/macros/context-mismatch-issue-147339.rs new file mode 100644 index 000000000000..80e744afc7c6 --- /dev/null +++ b/tests/coverage/macros/context-mismatch-issue-147339.rs @@ -0,0 +1,20 @@ +//@ edition: 2024 + +// These nested macro expansions were found to cause span refinement to produce +// spans with a context that doesn't match the function body span, triggering +// a defensive check that discards the span. +// +// Reported in . + +macro_rules! foo { + ($($m:ident $($f:ident $v:tt)+),*) => { + $($(macro_rules! $f { () => { $v } })+)* + $(macro_rules! $m { () => { $(fn $f() -> i32 { $v })+ } })* + } +} + +foo!(m a 1 b 2, n c 3); +m!(); +n!(); + +fn main() {} From 5e30860d311ff54116711ee3381dcea218ae870c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 13:24:04 +0100 Subject: [PATCH 292/319] Don't check variables in subdiagnostic messages --- compiler/rustc_macros/src/diagnostics/diagnostic.rs | 4 ++-- .../rustc_macros/src/diagnostics/diagnostic_builder.rs | 6 +++--- compiler/rustc_macros/src/diagnostics/message.rs | 9 +++++++-- compiler/rustc_macros/src/diagnostics/subdiagnostic.rs | 9 +++------ 4 files changed, 15 insertions(+), 13 deletions(-) diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic.rs b/compiler/rustc_macros/src/diagnostics/diagnostic.rs index b4270f45422e..e8356655dd9f 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic.rs @@ -31,7 +31,7 @@ impl<'a> DiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; messages.borrow_mut().push(message.clone()); - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); let init = quote! { let mut diag = rustc_errors::Diag::new( @@ -97,7 +97,7 @@ impl<'a> LintDiagnosticDerive<'a> { return DiagnosticDeriveError::ErrorHandled.to_compile_error(); }; messages.borrow_mut().push(message.clone()); - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); let primary_message = quote! { diag.primary_message(#message); }; diff --git a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs index e6d9409a1fa3..6107b181eea2 100644 --- a/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs +++ b/compiler/rustc_macros/src/diagnostics/diagnostic_builder.rs @@ -456,7 +456,7 @@ impl DiagnosticDeriveVariantBuilder { applicability.set_once(quote! { #static_applicability }, span); } - let message = slug.diag_message(variant); + let message = slug.diag_message(Some(variant)); let applicability = applicability .value() .unwrap_or_else(|| quote! { rustc_errors::Applicability::Unspecified }); @@ -487,7 +487,7 @@ impl DiagnosticDeriveVariantBuilder { variant: &VariantInfo<'_>, ) -> TokenStream { let fn_name = format_ident!("span_{}", kind); - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); quote! { diag.#fn_name( #field_binding, @@ -504,7 +504,7 @@ impl DiagnosticDeriveVariantBuilder { message: Message, variant: &VariantInfo<'_>, ) -> TokenStream { - let message = message.diag_message(variant); + let message = message.diag_message(Some(variant)); quote! { diag.#kind(#message); } diff --git a/compiler/rustc_macros/src/diagnostics/message.rs b/compiler/rustc_macros/src/diagnostics/message.rs index 153abecf8937..6c8aded89f16 100644 --- a/compiler/rustc_macros/src/diagnostics/message.rs +++ b/compiler/rustc_macros/src/diagnostics/message.rs @@ -14,13 +14,18 @@ pub(crate) enum Message { } impl Message { - pub(crate) fn diag_message(&self, variant: &VariantInfo<'_>) -> TokenStream { + /// Get the diagnostic message for this diagnostic + /// The passed `variant` is used to check whether all variables in the message are used. + /// For subdiagnostics, we cannot check this. + pub(crate) fn diag_message(&self, variant: Option<&VariantInfo<'_>>) -> TokenStream { match self { Message::Slug(slug) => { quote! { crate::fluent_generated::#slug } } Message::Inline(message_span, message) => { - verify_fluent_message(*message_span, &message, variant); + if let Some(variant) = variant { + verify_fluent_message(*message_span, &message, variant); + } quote! { rustc_errors::DiagMessage::Inline(std::borrow::Cow::Borrowed(#message)) } } } diff --git a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs index adc968dacd5c..ac1fa984664c 100644 --- a/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs +++ b/compiler/rustc_macros/src/diagnostics/subdiagnostic.rs @@ -75,7 +75,7 @@ impl SubdiagnosticDerive { has_subdiagnostic: false, is_enum, }; - builder.into_tokens(variant).unwrap_or_else(|v| v.to_compile_error()) + builder.into_tokens().unwrap_or_else(|v| v.to_compile_error()) }); quote! { @@ -497,10 +497,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { } } - pub(crate) fn into_tokens( - &mut self, - variant: &VariantInfo<'_>, - ) -> Result { + pub(crate) fn into_tokens(&mut self) -> Result { let kind_slugs = self.identify_kind()?; let kind_stats: KindsStatistics = kind_slugs.iter().map(|(kind, _slug)| kind).collect(); @@ -538,7 +535,7 @@ impl<'parent, 'a> SubdiagnosticDeriveVariantBuilder<'parent, 'a> { let mut calls = TokenStream::new(); for (kind, slug) in kind_slugs { let message = format_ident!("__message"); - let message_stream = slug.diag_message(variant); + let message_stream = slug.diag_message(None); calls.extend(quote! { let #message = #diag.eagerly_translate(#message_stream); }); let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind); From 30f82aac5bb7f4a1c47f5e0c07f100eca045009d Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 14:01:05 +0100 Subject: [PATCH 293/319] Convert to inline diagnostics in `rustc_query_system` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_query_system/Cargo.toml | 1 - compiler/rustc_query_system/messages.ftl | 30 ---------------- compiler/rustc_query_system/src/error.rs | 46 ++++++++++++++---------- compiler/rustc_query_system/src/lib.rs | 2 -- 7 files changed, 27 insertions(+), 56 deletions(-) delete mode 100644 compiler/rustc_query_system/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..618f3f67fd04 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3812,7 +3812,6 @@ dependencies = [ "rustc_pattern_analysis", "rustc_privacy", "rustc_public", - "rustc_query_system", "rustc_resolve", "rustc_session", "rustc_span", @@ -4572,7 +4571,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_hashes", "rustc_hir", "rustc_index", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..0f8b65c47b06 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -44,7 +44,6 @@ rustc_passes = { path = "../rustc_passes" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } rustc_privacy = { path = "../rustc_privacy" } rustc_public = { path = "../rustc_public", features = ["rustc_internal"] } -rustc_query_system = { path = "../rustc_query_system" } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..c72dc35064b0 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -142,7 +142,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_passes::DEFAULT_LOCALE_RESOURCE, rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, rustc_privacy::DEFAULT_LOCALE_RESOURCE, - rustc_query_system::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_query_system/Cargo.toml b/compiler/rustc_query_system/Cargo.toml index 0ad8143c5a4f..761a299eab77 100644 --- a/compiler/rustc_query_system/Cargo.toml +++ b/compiler/rustc_query_system/Cargo.toml @@ -11,7 +11,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_query_system/messages.ftl b/compiler/rustc_query_system/messages.ftl deleted file mode 100644 index d2ab2d34c5fc..000000000000 --- a/compiler/rustc_query_system/messages.ftl +++ /dev/null @@ -1,30 +0,0 @@ -query_system_cycle = cycle detected when {$stack_bottom} - .note = see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information - -query_system_cycle_recursive_trait_alias = trait aliases cannot be recursive - -query_system_cycle_recursive_ty_alias = type aliases cannot be recursive -query_system_cycle_recursive_ty_alias_help1 = consider using a struct, enum, or union instead to break the cycle -query_system_cycle_recursive_ty_alias_help2 = see for more information - -query_system_cycle_stack_middle = ...which requires {$desc}... - -query_system_cycle_stack_multiple = ...which again requires {$stack_bottom}, completing the cycle - -query_system_cycle_stack_single = ...which immediately requires {$stack_bottom} again - -query_system_cycle_usage = cycle used when {$usage} - -query_system_increment_compilation = internal compiler error: encountered incremental compilation error with {$dep_node} - -query_system_increment_compilation_note1 = please follow the instructions below to create a bug report with the provided information -query_system_increment_compilation_note2 = for incremental compilation bugs, having a reproduction is vital -query_system_increment_compilation_note3 = an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again -query_system_increment_compilation_note4 = as a workaround, you can run {$run_cmd} to allow your project to compile - -query_system_overflow_note = query depth increased by {$depth} when {$desc} - -query_system_query_overflow = queries overflow the depth limit! - .help = consider increasing the recursion limit by adding a `#![recursion_limit = "{$suggested_limit}"]` attribute to your crate (`{$crate_name}`) - -query_system_reentrant = internal compiler error: reentrant incremental verify failure, suppressing message diff --git a/compiler/rustc_query_system/src/error.rs b/compiler/rustc_query_system/src/error.rs index 4b1effe2b33d..55f2feba0d86 100644 --- a/compiler/rustc_query_system/src/error.rs +++ b/compiler/rustc_query_system/src/error.rs @@ -4,7 +4,7 @@ use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Subdiagnostic)] -#[note(query_system_cycle_stack_middle)] +#[note("...which requires {$desc}...")] pub(crate) struct CycleStack { #[primary_span] pub span: Span, @@ -13,24 +13,26 @@ pub(crate) struct CycleStack { #[derive(Subdiagnostic)] pub(crate) enum StackCount { - #[note(query_system_cycle_stack_single)] + #[note("...which immediately requires {$stack_bottom} again")] Single, - #[note(query_system_cycle_stack_multiple)] + #[note("...which again requires {$stack_bottom}, completing the cycle")] Multiple, } #[derive(Subdiagnostic)] pub(crate) enum Alias { - #[note(query_system_cycle_recursive_ty_alias)] - #[help(query_system_cycle_recursive_ty_alias_help1)] - #[help(query_system_cycle_recursive_ty_alias_help2)] + #[note("type aliases cannot be recursive")] + #[help("consider using a struct, enum, or union instead to break the cycle")] + #[help( + "see for more information" + )] Ty, - #[note(query_system_cycle_recursive_trait_alias)] + #[note("trait aliases cannot be recursive")] Trait, } #[derive(Subdiagnostic)] -#[note(query_system_cycle_usage)] +#[note("cycle used when {$usage}")] pub(crate) struct CycleUsage { #[primary_span] pub span: Span, @@ -38,7 +40,7 @@ pub(crate) struct CycleUsage { } #[derive(Diagnostic)] -#[diag(query_system_cycle, code = E0391)] +#[diag("cycle detected when {$stack_bottom}", code = E0391)] pub(crate) struct Cycle { #[primary_span] pub span: Span, @@ -51,28 +53,34 @@ pub(crate) struct Cycle { pub alias: Option, #[subdiagnostic] pub cycle_usage: Option, - #[note] + #[note( + "see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information" + )] pub note_span: (), } #[derive(Diagnostic)] -#[diag(query_system_reentrant)] +#[diag("internal compiler error: reentrant incremental verify failure, suppressing message")] pub(crate) struct Reentrant; #[derive(Diagnostic)] -#[diag(query_system_increment_compilation)] -#[note(query_system_increment_compilation_note1)] -#[note(query_system_increment_compilation_note2)] -#[note(query_system_increment_compilation_note3)] -#[note(query_system_increment_compilation_note4)] +#[diag("internal compiler error: encountered incremental compilation error with {$dep_node}")] +#[note("please follow the instructions below to create a bug report with the provided information")] +#[note("for incremental compilation bugs, having a reproduction is vital")] +#[note( + "an ideal reproduction consists of the code before and some patch that then triggers the bug when applied and compiled again" +)] +#[note("as a workaround, you can run {$run_cmd} to allow your project to compile")] pub(crate) struct IncrementCompilation { pub run_cmd: String, pub dep_node: String, } #[derive(Diagnostic)] -#[help] -#[diag(query_system_query_overflow)] +#[help( + "consider increasing the recursion limit by adding a `#![recursion_limit = \"{$suggested_limit}\"]` attribute to your crate (`{$crate_name}`)" +)] +#[diag("queries overflow the depth limit!")] pub struct QueryOverflow { #[primary_span] pub span: Span, @@ -83,7 +91,7 @@ pub struct QueryOverflow { } #[derive(Subdiagnostic)] -#[note(query_system_overflow_note)] +#[note("query depth increased by {$depth} when {$desc}")] pub struct QueryOverflowNote { pub desc: String, pub depth: usize, diff --git a/compiler/rustc_query_system/src/lib.rs b/compiler/rustc_query_system/src/lib.rs index cdfe3454061c..d1907a8c582d 100644 --- a/compiler/rustc_query_system/src/lib.rs +++ b/compiler/rustc_query_system/src/lib.rs @@ -14,5 +14,3 @@ mod values; pub use error::{QueryOverflow, QueryOverflowNote}; pub use values::Value; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From b668057d794d87de368156626504d721ffa1c767 Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Fri, 30 Jan 2026 03:06:46 +0000 Subject: [PATCH 294/319] Port `rustc_mir` to attribute parser --- Cargo.lock | 2 +- .../src/attributes/rustc_internal.rs | 93 +++++++++++++++++- compiler/rustc_attr_parsing/src/context.rs | 8 +- .../src/session_diagnostics.rs | 4 + .../rustc_hir/src/attrs/data_structures.rs | 18 ++++ .../rustc_hir/src/attrs/encode_cross_crate.rs | 1 + .../rustc_hir/src/attrs/pretty_printing.rs | 9 ++ compiler/rustc_mir_dataflow/Cargo.toml | 2 +- compiler/rustc_mir_dataflow/messages.ftl | 12 --- compiler/rustc_mir_dataflow/src/errors.rs | 32 +------ .../src/framework/graphviz.rs | 94 +++++-------------- compiler/rustc_mir_dataflow/src/rustc_peek.rs | 71 ++++++-------- compiler/rustc_passes/src/check_attr.rs | 1 + 13 files changed, 186 insertions(+), 161 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..ffd11e46d98b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4338,11 +4338,11 @@ dependencies = [ "polonius-engine", "regex", "rustc_abi", - "rustc_ast", "rustc_data_structures", "rustc_errors", "rustc_fluent_macro", "rustc_graphviz", + "rustc_hir", "rustc_index", "rustc_macros", "rustc_middle", diff --git a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs index 250bceecbd65..8961dc47706f 100644 --- a/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs +++ b/compiler/rustc_attr_parsing/src/attributes/rustc_internal.rs @@ -1,5 +1,7 @@ +use std::path::PathBuf; + use rustc_ast::{LitIntType, LitKind, MetaItemLit}; -use rustc_hir::attrs::RustcLayoutType; +use rustc_hir::attrs::{BorrowckGraphvizFormatKind, RustcLayoutType, RustcMirKind}; use rustc_session::errors; use super::prelude::*; @@ -357,7 +359,6 @@ impl CombineAttributeParser for RustcLayoutParser { const TEMPLATE: AttributeTemplate = template!(List: &["abi", "align", "size", "homogenous_aggregate", "debug"]); - fn extend( cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser, @@ -397,6 +398,94 @@ impl CombineAttributeParser for RustcLayoutParser { } } +pub(crate) struct RustcMirParser; + +impl CombineAttributeParser for RustcMirParser { + const PATH: &[rustc_span::Symbol] = &[sym::rustc_mir]; + + type Item = RustcMirKind; + + const CONVERT: ConvertFn = |items, _| AttributeKind::RustcMir(items); + + const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[ + Allow(Target::Fn), + Allow(Target::Method(MethodKind::Inherent)), + Allow(Target::Method(MethodKind::TraitImpl)), + Allow(Target::Method(MethodKind::Trait { body: false })), + Allow(Target::Method(MethodKind::Trait { body: true })), + ]); + + const TEMPLATE: AttributeTemplate = template!(List: &["arg1, arg2, ..."]); + + fn extend( + cx: &mut AcceptContext<'_, '_, S>, + args: &ArgParser, + ) -> impl IntoIterator { + let Some(list) = args.list() else { + cx.expected_list(cx.attr_span, args); + return ThinVec::new(); + }; + + list.mixed() + .filter_map(|arg| arg.meta_item()) + .filter_map(|mi| { + if let Some(ident) = mi.ident() { + match ident.name { + sym::rustc_peek_maybe_init => Some(RustcMirKind::PeekMaybeInit), + sym::rustc_peek_maybe_uninit => Some(RustcMirKind::PeekMaybeUninit), + sym::rustc_peek_liveness => Some(RustcMirKind::PeekLiveness), + sym::stop_after_dataflow => Some(RustcMirKind::StopAfterDataflow), + sym::borrowck_graphviz_postflow => { + let Some(nv) = mi.args().name_value() else { + cx.expected_name_value( + mi.span(), + Some(sym::borrowck_graphviz_postflow), + ); + return None; + }; + let Some(path) = nv.value_as_str() else { + cx.expected_string_literal(nv.value_span, None); + return None; + }; + let path = PathBuf::from(path.to_string()); + if path.file_name().is_some() { + Some(RustcMirKind::BorrowckGraphvizPostflow { path }) + } else { + cx.expected_filename_literal(nv.value_span); + None + } + } + sym::borrowck_graphviz_format => { + let Some(nv) = mi.args().name_value() else { + cx.expected_name_value( + mi.span(), + Some(sym::borrowck_graphviz_format), + ); + return None; + }; + let Some(format) = nv.value_as_ident() else { + cx.expected_identifier(nv.value_span); + return None; + }; + match format.name { + sym::two_phase => Some(RustcMirKind::BorrowckGraphvizFormat { + format: BorrowckGraphvizFormatKind::TwoPhase, + }), + _ => { + cx.expected_specific_argument(format.span, &[sym::two_phase]); + None + } + } + } + _ => None, + } + } else { + None + } + }) + .collect() + } +} pub(crate) struct RustcNonConstTraitMethodParser; impl NoArgsAttributeParser for RustcNonConstTraitMethodParser { diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index f340b393567e..322e189e6d12 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -81,7 +81,7 @@ use crate::attributes::rustc_internal::{ RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, - RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, + RustcMirParser, RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, RustcNoImplicitAutorefsParser, RustcNonConstTraitMethodParser, RustcNounwindParser, RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, RustcSimdMonomorphizeLaneLimitParser, @@ -202,6 +202,7 @@ attribute_parsers!( Combine, Combine, Combine, + Combine, Combine, Combine, // tidy-alphabetical-end @@ -517,6 +518,11 @@ impl<'f, 'sess: 'f, S: Stage> AcceptContext<'f, 'sess, S> { ) } + /// Error that a filename string literal was expected. + pub(crate) fn expected_filename_literal(&self, span: Span) { + self.emit_parse_error(span, AttributeParseErrorReason::ExpectedFilenameLiteral); + } + pub(crate) fn expected_integer_literal(&self, span: Span) -> ErrorGuaranteed { self.emit_parse_error(span, AttributeParseErrorReason::ExpectedIntegerLiteral) } diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index f9748542beb9..e67fc95e2806 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -524,6 +524,7 @@ pub(crate) enum AttributeParseErrorReason<'a> { ExpectedStringLiteral { byte_string: Option, }, + ExpectedFilenameLiteral, ExpectedIntegerLiteral, ExpectedIntegerLiteralInRange { lower_bound: isize, @@ -597,6 +598,9 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for AttributeParseError<'_> { diag.span_label(self.span, "expected a string literal here"); } } + AttributeParseErrorReason::ExpectedFilenameLiteral => { + diag.span_label(self.span, "expected a filename string literal here"); + } AttributeParseErrorReason::ExpectedIntegerLiteral => { diag.span_label(self.span, "expected an integer literal here"); } diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index fba53c913e16..6138ffc8d954 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -699,6 +699,21 @@ pub enum RustcLayoutType { Debug, } +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] +pub enum RustcMirKind { + PeekMaybeInit, + PeekMaybeUninit, + PeekLiveness, + StopAfterDataflow, + BorrowckGraphvizPostflow { path: PathBuf }, + BorrowckGraphvizFormat { format: BorrowckGraphvizFormatKind }, +} + +#[derive(Clone, Debug, HashStable_Generic, Encodable, Decodable, PrintAttribute, PartialEq, Eq)] +pub enum BorrowckGraphvizFormatKind { + TwoPhase, +} + /// Represents parsed *built-in* inert attributes. /// /// ## Overview @@ -1090,6 +1105,9 @@ pub enum AttributeKind { /// Represents `#[rustc_main]`. RustcMain, + /// Represents `#[rustc_mir]`. + RustcMir(ThinVec), + /// Represents `#[rustc_must_implement_one_of]` RustcMustImplementOneOf { attr_span: Span, fn_names: ThinVec }, diff --git a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs index 26e5fc6e6257..7ec1920152a5 100644 --- a/compiler/rustc_hir/src/attrs/encode_cross_crate.rs +++ b/compiler/rustc_hir/src/attrs/encode_cross_crate.rs @@ -122,6 +122,7 @@ impl AttributeKind { RustcLintUntrackedQueryInformation => Yes, RustcMacroTransparency(..) => Yes, RustcMain => No, + RustcMir(..) => Yes, RustcMustImplementOneOf { .. } => No, RustcNeverReturnsNullPointer => Yes, RustcNoImplicitAutorefs => Yes, diff --git a/compiler/rustc_hir/src/attrs/pretty_printing.rs b/compiler/rustc_hir/src/attrs/pretty_printing.rs index c2ad644688fc..bd268d2c423f 100644 --- a/compiler/rustc_hir/src/attrs/pretty_printing.rs +++ b/compiler/rustc_hir/src/attrs/pretty_printing.rs @@ -1,5 +1,6 @@ use std::num::NonZero; use std::ops::Deref; +use std::path::PathBuf; use rustc_abi::Align; use rustc_ast::attr::data_structures::CfgEntry; @@ -96,7 +97,15 @@ impl PrintAttribute for FxIndexMap { p.word("]"); } } +impl PrintAttribute for PathBuf { + fn should_render(&self) -> bool { + true + } + fn print_attribute(&self, p: &mut Printer) { + p.word(self.display().to_string()); + } +} macro_rules! print_skip { ($($t: ty),* $(,)?) => {$( impl PrintAttribute for $t { diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index 9621f9f20bdc..e41d5da71397 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -8,11 +8,11 @@ edition = "2024" polonius-engine = "0.13.0" regex = "1" rustc_abi = { path = "../rustc_abi" } -rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } +rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_mir_dataflow/messages.ftl b/compiler/rustc_mir_dataflow/messages.ftl index 5698367e42ba..3783c647b03e 100644 --- a/compiler/rustc_mir_dataflow/messages.ftl +++ b/compiler/rustc_mir_dataflow/messages.ftl @@ -1,9 +1,3 @@ -mir_dataflow_duplicate_values_for = - duplicate values for `{$name}` - -mir_dataflow_path_must_end_in_filename = - path must end in a filename - mir_dataflow_peek_argument_not_a_local = rustc_peek: argument was not a local @@ -19,11 +13,5 @@ mir_dataflow_peek_must_be_not_temporary = mir_dataflow_peek_must_be_place_or_ref_place = rustc_peek: argument expression must be either `place` or `&place` -mir_dataflow_requires_an_argument = - `{$name}` requires an argument - mir_dataflow_stop_after_dataflow_ended_compilation = stop_after_dataflow ended compilation - -mir_dataflow_unknown_formatter = - unknown formatter diff --git a/compiler/rustc_mir_dataflow/src/errors.rs b/compiler/rustc_mir_dataflow/src/errors.rs index cfacc0ec370c..9d8c34c8a1f3 100644 --- a/compiler/rustc_mir_dataflow/src/errors.rs +++ b/compiler/rustc_mir_dataflow/src/errors.rs @@ -1,35 +1,5 @@ use rustc_macros::Diagnostic; -use rustc_span::{Span, Symbol}; - -#[derive(Diagnostic)] -#[diag(mir_dataflow_path_must_end_in_filename)] -pub(crate) struct PathMustEndInFilename { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_unknown_formatter)] -pub(crate) struct UnknownFormatter { - #[primary_span] - pub span: Span, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_duplicate_values_for)] -pub(crate) struct DuplicateValuesFor { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -#[derive(Diagnostic)] -#[diag(mir_dataflow_requires_an_argument)] -pub(crate) struct RequiresAnArgument { - #[primary_span] - pub span: Span, - pub name: Symbol, -} +use rustc_span::Span; #[derive(Diagnostic)] #[diag(mir_dataflow_stop_after_dataflow_ended_compilation)] diff --git a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs index 22bff3806b15..c4b9b4ce6416 100644 --- a/compiler/rustc_mir_dataflow/src/framework/graphviz.rs +++ b/compiler/rustc_mir_dataflow/src/framework/graphviz.rs @@ -7,6 +7,9 @@ use std::sync::OnceLock; use std::{io, ops, str}; use regex::Regex; +use rustc_graphviz as dot; +use rustc_hir::attrs::{AttributeKind, BorrowckGraphvizFormatKind, RustcMirKind}; +use rustc_hir::find_attr; use rustc_index::bit_set::DenseBitSet; use rustc_middle::mir::{ self, BasicBlock, Body, Location, MirDumper, graphviz_safe_def_name, traversal, @@ -14,17 +17,12 @@ use rustc_middle::mir::{ use rustc_middle::ty::TyCtxt; use rustc_middle::ty::print::with_no_trimmed_paths; use rustc_span::def_id::DefId; -use rustc_span::{Symbol, sym}; use tracing::debug; -use {rustc_ast as ast, rustc_graphviz as dot}; use super::fmt::{DebugDiffWithAdapter, DebugWithAdapter, DebugWithContext}; use super::{ Analysis, CallReturnPlaces, Direction, Results, ResultsCursor, ResultsVisitor, visit_results, }; -use crate::errors::{ - DuplicateValuesFor, PathMustEndInFilename, RequiresAnArgument, UnknownFormatter, -}; /// Writes a DOT file containing the results of a dataflow analysis if the user requested it via /// `rustc_mir` attributes and `-Z dump-mir-dataflow`. The `Result` in and the `Results` out are @@ -43,10 +41,7 @@ where use std::io::Write; let def_id = body.source.def_id(); - let Ok(attrs) = RustcMirAttrs::parse(tcx, def_id) else { - // Invalid `rustc_mir` attrs are reported in `RustcMirAttrs::parse` - return Ok(()); - }; + let attrs = RustcMirAttrs::parse(tcx, def_id); let file = try { match attrs.output_path(A::NAME) { @@ -72,10 +67,7 @@ where Err(e) => return Err(e), }; - let style = match attrs.formatter { - Some(sym::two_phase) => OutputStyle::BeforeAndAfter, - _ => OutputStyle::AfterOnly, - }; + let style = attrs.formatter.unwrap_or(OutputStyle::AfterOnly); let mut buf = Vec::new(); @@ -98,71 +90,33 @@ where #[derive(Default)] struct RustcMirAttrs { basename_and_suffix: Option, - formatter: Option, + formatter: Option, } impl RustcMirAttrs { - fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Result { - let mut result = Ok(()); + fn parse(tcx: TyCtxt<'_>, def_id: DefId) -> Self { let mut ret = RustcMirAttrs::default(); - let rustc_mir_attrs = tcx - .get_attrs(def_id, sym::rustc_mir) - .flat_map(|attr| attr.meta_item_list().into_iter().flat_map(|v| v.into_iter())); - - for attr in rustc_mir_attrs { - let attr_result = match attr.name() { - Some(name @ sym::borrowck_graphviz_postflow) => { - Self::set_field(&mut ret.basename_and_suffix, tcx, name, &attr, |s| { - let path = PathBuf::from(s.to_string()); - match path.file_name() { - Some(_) => Ok(path), - None => { - tcx.dcx().emit_err(PathMustEndInFilename { span: attr.span() }); - Err(()) + let attrs = tcx.get_all_attrs(def_id); + if let Some(rustc_mir_attrs) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + for attr in rustc_mir_attrs { + match attr { + RustcMirKind::BorrowckGraphvizPostflow { path } => { + ret.basename_and_suffix = Some(path.clone()); + } + RustcMirKind::BorrowckGraphvizFormat { format } => { + ret.formatter = match format { + BorrowckGraphvizFormatKind::TwoPhase => { + Some(OutputStyle::BeforeAndAfter) } - } - }) - } - Some(name @ sym::borrowck_graphviz_format) => { - Self::set_field(&mut ret.formatter, tcx, name, &attr, |s| match s { - sym::two_phase => Ok(s), - _ => { - tcx.dcx().emit_err(UnknownFormatter { span: attr.span() }); - Err(()) - } - }) - } - _ => Ok(()), - }; - - result = result.and(attr_result); + }; + } + _ => (), + }; + } } - result.map(|()| ret) - } - - fn set_field( - field: &mut Option, - tcx: TyCtxt<'_>, - name: Symbol, - attr: &ast::MetaItemInner, - mapper: impl FnOnce(Symbol) -> Result, - ) -> Result<(), ()> { - if field.is_some() { - tcx.dcx().emit_err(DuplicateValuesFor { span: attr.span(), name }); - - return Err(()); - } - - if let Some(s) = attr.value_str() { - *field = Some(mapper(s)?); - Ok(()) - } else { - tcx.dcx() - .emit_err(RequiresAnArgument { span: attr.span(), name: attr.name().unwrap() }); - Err(()) - } + ret } /// Returns the path where dataflow results should be written, or `None` diff --git a/compiler/rustc_mir_dataflow/src/rustc_peek.rs b/compiler/rustc_mir_dataflow/src/rustc_peek.rs index a899ec1fa884..1c5b38361669 100644 --- a/compiler/rustc_mir_dataflow/src/rustc_peek.rs +++ b/compiler/rustc_mir_dataflow/src/rustc_peek.rs @@ -1,8 +1,8 @@ -use rustc_ast::MetaItem; +use rustc_hir::attrs::{AttributeKind, RustcMirKind}; +use rustc_hir::find_attr; use rustc_middle::mir::{self, Body, Local, Location}; use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::def_id::DefId; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{Span, sym}; use tracing::{debug, info}; use crate::errors::{ @@ -14,52 +14,37 @@ use crate::impls::{MaybeInitializedPlaces, MaybeLiveLocals, MaybeUninitializedPl use crate::move_paths::{HasMoveData, LookupResult, MoveData, MovePathIndex}; use crate::{Analysis, JoinSemiLattice, ResultsCursor}; -fn has_rustc_mir_with(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Option { - for attr in tcx.get_attrs(def_id, sym::rustc_mir) { - let items = attr.meta_item_list(); - for item in items.iter().flat_map(|l| l.iter()) { - match item.meta_item() { - Some(mi) if mi.has_name(name) => return Some(mi.clone()), - _ => continue, - } - } - } - None -} - pub fn sanity_check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { let def_id = body.source.def_id(); - if !tcx.has_attr(def_id, sym::rustc_mir) { - debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - return; - } else { + let attrs = tcx.get_all_attrs(def_id); + if let Some(kind) = find_attr!(attrs, AttributeKind::RustcMir(kind) => kind) { + let move_data = MoveData::gather_moves(body, tcx, |_| true); debug!("running rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); - } + if kind.contains(&RustcMirKind::PeekMaybeInit) { + let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_inits); + } - let move_data = MoveData::gather_moves(body, tcx, |_| true); + if kind.contains(&RustcMirKind::PeekMaybeUninit) { + let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) + .iterate_to_fixpoint(tcx, body, None) + .into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_uninits); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_init).is_some() { - let flow_inits = MaybeInitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_inits); - } + if kind.contains(&RustcMirKind::PeekLiveness) { + let flow_liveness = + MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); + sanity_check_via_rustc_peek(tcx, flow_liveness); + } - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_maybe_uninit).is_some() { - let flow_uninits = MaybeUninitializedPlaces::new(tcx, body, &move_data) - .iterate_to_fixpoint(tcx, body, None) - .into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_uninits); - } - - if has_rustc_mir_with(tcx, def_id, sym::rustc_peek_liveness).is_some() { - let flow_liveness = - MaybeLiveLocals.iterate_to_fixpoint(tcx, body, None).into_results_cursor(body); - sanity_check_via_rustc_peek(tcx, flow_liveness); - } - - if has_rustc_mir_with(tcx, def_id, sym::stop_after_dataflow).is_some() { - tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); + if kind.contains(&RustcMirKind::StopAfterDataflow) { + tcx.dcx().emit_fatal(StopAfterDataFlowEndedCompilation); + } + } else { + debug!("skipping rustc_peek::SanityCheck on {}", tcx.def_path_str(def_id)); } } diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index bd02bc551756..dd16092c99f9 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -309,6 +309,7 @@ impl<'tcx> CheckAttrVisitor<'tcx> { | AttributeKind::RustcLintUntrackedQueryInformation | AttributeKind::RustcMacroTransparency(_) | AttributeKind::RustcMain + | AttributeKind::RustcMir(_) | AttributeKind::RustcNeverReturnsNullPointer | AttributeKind::RustcNoImplicitAutorefs | AttributeKind::RustcNonConstTraitMethod From 1722b8e06b3c334b77024f4d8d8de4a832f10df9 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 15:14:49 +0100 Subject: [PATCH 295/319] Convert to inline diagnostics in `rustc_infer` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_infer/Cargo.toml | 1 - compiler/rustc_infer/messages.ftl | 5 ----- compiler/rustc_infer/src/errors.rs | 8 ++++---- compiler/rustc_infer/src/lib.rs | 2 -- 7 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 compiler/rustc_infer/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 234d709f3c31..111e5afb3e7e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3795,7 +3795,6 @@ dependencies = [ "rustc_hir_typeck", "rustc_incremental", "rustc_index", - "rustc_infer", "rustc_interface", "rustc_lexer", "rustc_lint", @@ -4093,7 +4092,6 @@ version = "0.0.0" dependencies = [ "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_macros", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index c160240a18a7..b688a6710a8e 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -27,7 +27,6 @@ rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } -rustc_infer = { path = "../rustc_infer" } rustc_interface = { path = "../rustc_interface" } rustc_lexer = { path = "../rustc_lexer" } rustc_lint = { path = "../rustc_lint" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614..ce6de3e2a1f8 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -129,7 +129,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE, rustc_incremental::DEFAULT_LOCALE_RESOURCE, - rustc_infer::DEFAULT_LOCALE_RESOURCE, rustc_interface::DEFAULT_LOCALE_RESOURCE, rustc_lint::DEFAULT_LOCALE_RESOURCE, rustc_metadata::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_infer/Cargo.toml b/compiler/rustc_infer/Cargo.toml index 08c036148849..c4fbe89315db 100644 --- a/compiler/rustc_infer/Cargo.toml +++ b/compiler/rustc_infer/Cargo.toml @@ -10,7 +10,6 @@ doctest = false # tidy-alphabetical-start rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl deleted file mode 100644 index e51734ff7a77..000000000000 --- a/compiler/rustc_infer/messages.ftl +++ /dev/null @@ -1,5 +0,0 @@ -infer_opaque_hidden_type = - opaque type's hidden type cannot be another opaque type from the same scope - .label = one of the two opaque types used here has to be outside its defining scope - .opaque_type = opaque type whose hidden type is being assigned - .hidden_type = opaque type being used as hidden type diff --git a/compiler/rustc_infer/src/errors.rs b/compiler/rustc_infer/src/errors.rs index 76ea9c3433d3..7c6e3b4ef314 100644 --- a/compiler/rustc_infer/src/errors.rs +++ b/compiler/rustc_infer/src/errors.rs @@ -2,13 +2,13 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(infer_opaque_hidden_type)] +#[diag("opaque type's hidden type cannot be another opaque type from the same scope")] pub(crate) struct OpaqueHiddenTypeDiag { #[primary_span] - #[label] + #[label("one of the two opaque types used here has to be outside its defining scope")] pub span: Span, - #[note(infer_opaque_type)] + #[note("opaque type whose hidden type is being assigned")] pub opaque_type: Span, - #[note(infer_hidden_type)] + #[note("opaque type being used as hidden type")] pub hidden_type: Span, } diff --git a/compiler/rustc_infer/src/lib.rs b/compiler/rustc_infer/src/lib.rs index 05ea0f813818..2a1733ef63cb 100644 --- a/compiler/rustc_infer/src/lib.rs +++ b/compiler/rustc_infer/src/lib.rs @@ -22,5 +22,3 @@ mod errors; pub mod infer; pub mod traits; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From 4e4192aa48ccb1955b945b0e898decbd73007d27 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 3 Feb 2026 16:07:12 +0100 Subject: [PATCH 296/319] Try to fix `rustdoc-gui/globals.goml` flakyness --- tests/rustdoc-gui/globals.goml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/rustdoc-gui/globals.goml b/tests/rustdoc-gui/globals.goml index 4f12175f6ab2..7fd9c5bfb6f2 100644 --- a/tests/rustdoc-gui/globals.goml +++ b/tests/rustdoc-gui/globals.goml @@ -6,7 +6,7 @@ include: "utils.goml" // URL query go-to: "file://" + |DOC_PATH| + "/test_docs/index.html?search=sa" wait-for: "#search-tabs" -assert-window-property-false: {"searchIndex": null} +wait-for-window-property-false: {"searchIndex": null} // Form input go-to: "file://" + |DOC_PATH| + "/test_docs/index.html" From dfc60ae100f424d28bb9d00979e17846335ac8a9 Mon Sep 17 00:00:00 2001 From: lapla Date: Wed, 4 Feb 2026 00:22:00 +0900 Subject: [PATCH 297/319] Distinguish error message for `#[diagnostic::on_const]` on const trait impls --- compiler/rustc_passes/messages.ftl | 4 ++++ compiler/rustc_passes/src/check_attr.rs | 18 +++++++++++++++++- .../on_const/misplaced_attr.rs | 2 +- .../on_const/misplaced_attr.stderr | 4 ++-- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_passes/messages.ftl b/compiler/rustc_passes/messages.ftl index f8ff46189c05..ab89af226793 100644 --- a/compiler/rustc_passes/messages.ftl +++ b/compiler/rustc_passes/messages.ftl @@ -87,6 +87,10 @@ passes_deprecated_annotation_has_no_effect = passes_deprecated_attribute = deprecated attribute must be paired with either stable or unstable attribute +passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls = + `#[diagnostic::on_const]` can only be applied to non-const trait impls + .label = this is a const trait impl + passes_diagnostic_diagnostic_on_const_only_for_trait_impls = `#[diagnostic::on_const]` can only be applied to trait impls .label = not a trait impl diff --git a/compiler/rustc_passes/src/check_attr.rs b/compiler/rustc_passes/src/check_attr.rs index 8cf68b280850..552985003fb5 100644 --- a/compiler/rustc_passes/src/check_attr.rs +++ b/compiler/rustc_passes/src/check_attr.rs @@ -67,6 +67,13 @@ struct DiagnosticOnConstOnlyForTraitImpls { item_span: Span, } +#[derive(LintDiagnostic)] +#[diag(passes_diagnostic_diagnostic_on_const_only_for_non_const_trait_impls)] +struct DiagnosticOnConstOnlyForNonConstTraitImpls { + #[label] + item_span: Span, +} + fn target_from_impl_item<'tcx>(tcx: TyCtxt<'tcx>, impl_item: &hir::ImplItem<'_>) -> Target { match impl_item.kind { hir::ImplItemKind::Const(..) => Target::AssocConst, @@ -629,7 +636,16 @@ impl<'tcx> CheckAttrVisitor<'tcx> { if target == (Target::Impl { of_trait: true }) { match item.unwrap() { ItemLike::Item(it) => match it.expect_impl().constness { - Constness::Const => {} + Constness::Const => { + let item_span = self.tcx.hir_span(hir_id); + self.tcx.emit_node_span_lint( + MISPLACED_DIAGNOSTIC_ATTRIBUTES, + hir_id, + attr_span, + DiagnosticOnConstOnlyForNonConstTraitImpls { item_span }, + ); + return; + } Constness::NotConst => return, }, ItemLike::ForeignItem => {} diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs index c0af549e2d01..f7babae3aa7c 100644 --- a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.rs @@ -6,7 +6,7 @@ pub struct Foo; #[diagnostic::on_const(message = "tadaa", note = "boing")] -//~^ ERROR: `#[diagnostic::on_const]` can only be applied to trait impls +//~^ ERROR: `#[diagnostic::on_const]` can only be applied to non-const trait impls impl const PartialEq for Foo { fn eq(&self, _other: &Foo) -> bool { true diff --git a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr index baa0b11f798b..f92ea501696e 100644 --- a/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr +++ b/tests/ui/diagnostic_namespace/on_const/misplaced_attr.stderr @@ -13,14 +13,14 @@ note: the lint level is defined here LL | #![deny(misplaced_diagnostic_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error: `#[diagnostic::on_const]` can only be applied to trait impls +error: `#[diagnostic::on_const]` can only be applied to non-const trait impls --> $DIR/misplaced_attr.rs:8:1 | LL | #[diagnostic::on_const(message = "tadaa", note = "boing")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ LL | LL | impl const PartialEq for Foo { - | ---------------------------- not a trait impl + | ---------------------------- this is a const trait impl error: `#[diagnostic::on_const]` can only be applied to trait impls --> $DIR/misplaced_attr.rs:16:1 From 6c4abb7fabc807c8e73f72d06a06ca7479c8c397 Mon Sep 17 00:00:00 2001 From: sgasho Date: Wed, 4 Feb 2026 00:58:34 +0900 Subject: [PATCH 298/319] Fix some autodiff tests require Clto=fat --- tests/run-make/autodiff/type-trees/array-typetree/rmake.rs | 2 +- .../run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs | 1 + tests/run-make/autodiff/type-trees/nott-flag/rmake.rs | 2 ++ tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f128-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f16-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f32-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/f64-typetree/rmake.rs | 2 +- .../autodiff/type-trees/scalar-types/i32-typetree/rmake.rs | 2 +- tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs | 2 +- tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs | 2 +- tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs | 2 +- 12 files changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs index 20b6a0669062..41805d26c6f7 100644 --- a/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/array-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("array.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs index 1c19963bc361..d740596907a3 100644 --- a/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/mixed-struct-typetree/rmake.rs @@ -9,6 +9,7 @@ fn main() { .arg("-Zautodiff=Enable") .arg("-Zautodiff=NoPostopt") .opt_level("0") + .arg("-Clto=fat") .emit("llvm-ir") .run(); diff --git a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs index de540b990cab..2e93d586a353 100644 --- a/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs +++ b/tests/run-make/autodiff/type-trees/nott-flag/rmake.rs @@ -8,6 +8,7 @@ fn main() { rustc() .input("test.rs") .arg("-Zautodiff=Enable,NoTT") + .arg("-Clto=fat") .emit("llvm-ir") .arg("-o") .arg("nott.ll") @@ -17,6 +18,7 @@ fn main() { rustc() .input("test.rs") .arg("-Zautodiff=Enable") + .arg("-Clto=fat") .emit("llvm-ir") .arg("-o") .arg("with_tt.ll") diff --git a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs index 78718f3a2159..af1eb4197b3b 100644 --- a/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/recursion-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("recursion.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs index 44320ecdd571..b1672cddb811 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f128-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f128 TypeTree metadata is correctly generated llvm_filecheck().patterns("f128.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs index 0aebdbf55209..3e308a91aaff 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f16-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f16 TypeTree metadata is correctly generated llvm_filecheck().patterns("f16.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs index ee3ab753bf50..3faba69f5bdd 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f32-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f32 TypeTree metadata is correctly generated llvm_filecheck().patterns("f32.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs index 5fac9b23bc80..4f1c2cec51c4 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/f64-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that f64 TypeTree metadata is correctly generated llvm_filecheck().patterns("f64.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs index a40fd55d88ad..328d690f29c8 100644 --- a/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/scalar-types/i32-typetree/rmake.rs @@ -5,7 +5,7 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { // Compile with TypeTree enabled and emit LLVM IR - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); // Check that i32 TypeTree metadata is correctly generated llvm_filecheck().patterns("i32.check").stdin_buf(rfs::read("test.ll")).run(); diff --git a/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs index b81fb50bf1a7..0cc6b42a0317 100644 --- a/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/slice-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("slice.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs index 0af1b65ee181..10499712d1e3 100644 --- a/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/struct-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("struct.check").stdin_buf(rfs::read("test.ll")).run(); } diff --git a/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs index 76913828901c..4c0458f588a8 100644 --- a/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs +++ b/tests/run-make/autodiff/type-trees/tuple-typetree/rmake.rs @@ -4,6 +4,6 @@ use run_make_support::{llvm_filecheck, rfs, rustc}; fn main() { - rustc().input("test.rs").arg("-Zautodiff=Enable").emit("llvm-ir").run(); + rustc().input("test.rs").arg("-Zautodiff=Enable").arg("-Clto=fat").emit("llvm-ir").run(); llvm_filecheck().patterns("tuple.check").stdin_buf(rfs::read("test.ll")).run(); } From 19e4108ed1418a8e320230ffa886f6096164576d Mon Sep 17 00:00:00 2001 From: Mads Marquart Date: Mon, 2 Feb 2026 22:47:05 +0100 Subject: [PATCH 299/319] Use DEVELOPER_DIR instead of a custom xcode-select script `DEVELOPER_DIR` is the standard environment variable for overriding the Xcode version, there is no need to invoke `xcode-select --switch` manually to do this. The variable is documented in both `man xcode-select` and `man xcrun`. --- .github/workflows/ci.yml | 3 --- src/ci/citool/tests/jobs.rs | 2 +- src/ci/citool/tests/test-jobs.yml | 4 ++-- src/ci/github-actions/jobs.yml | 8 ++++---- src/ci/scripts/select-xcode.sh | 11 ----------- 5 files changed, 7 insertions(+), 21 deletions(-) delete mode 100755 src/ci/scripts/select-xcode.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e6a00bb42785..4409d4f33afc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -165,9 +165,6 @@ jobs: - name: install sccache run: src/ci/scripts/install-sccache.sh - - name: select Xcode - run: src/ci/scripts/select-xcode.sh - - name: install clang run: src/ci/scripts/install-clang.sh diff --git a/src/ci/citool/tests/jobs.rs b/src/ci/citool/tests/jobs.rs index 6787f00d9af8..b0309c52003f 100644 --- a/src/ci/citool/tests/jobs.rs +++ b/src/ci/citool/tests/jobs.rs @@ -6,7 +6,7 @@ const TEST_JOBS_YML_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/tests/tes fn auto_jobs() { let stdout = get_matrix("push", "commit", "refs/heads/automation/bors/auto"); insta::assert_snapshot!(stdout, @r#" - jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-14","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","SELECT_XCODE":"/Applications/Xcode_15.4.app","TOOLSTATE_PUBLISH":1,"USE_XCODE_CLANG":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"pr-check-1","full_name":"auto - pr-check-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"pr-check-2","full_name":"auto - pr-check-2","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"tidy","full_name":"auto - tidy","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true,"doc_url":"https://foo.bar"}] + jobs=[{"name":"aarch64-gnu","full_name":"auto - aarch64-gnu","os":"ubuntu-22.04-arm","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"x86_64-gnu-llvm-18-1","full_name":"auto - x86_64-gnu-llvm-18-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DOCKER_SCRIPT":"stage_2_test_set1.sh","IMAGE":"x86_64-gnu-llvm-18","READ_ONLY_SRC":"0","RUST_BACKTRACE":1,"TOOLSTATE_PUBLISH":1},"free_disk":true},{"name":"aarch64-apple","full_name":"auto - aarch64-apple","os":"macos-14","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","DEVELOPER_DIR":"/Applications/Xcode_15.4.app/Contents/Developer","MACOSX_DEPLOYMENT_TARGET":11.0,"MACOSX_STD_DEPLOYMENT_TARGET":11.0,"NO_DEBUG_ASSERTIONS":1,"NO_LLVM_ASSERTIONS":1,"NO_OVERFLOW_CHECKS":1,"RUSTC_RETRY_LINKER_ON_SEGFAULT":1,"RUST_CONFIGURE_ARGS":"--enable-sanitizers --enable-profiler --set rust.jemalloc","SCRIPT":"./x.py --stage 2 test --host=aarch64-apple-darwin --target=aarch64-apple-darwin","TOOLSTATE_PUBLISH":1,"USE_XCODE_CLANG":1}},{"name":"dist-i686-msvc","full_name":"auto - dist-i686-msvc","os":"windows-2022","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","CODEGEN_BACKENDS":"llvm,cranelift","DEPLOY_BUCKET":"rust-lang-ci2","DIST_REQUIRE_ALL_TOOLS":1,"RUST_CONFIGURE_ARGS":"--build=i686-pc-windows-msvc --host=i686-pc-windows-msvc --target=i686-pc-windows-msvc,i586-pc-windows-msvc --enable-full-tools --enable-profiler","SCRIPT":"python x.py dist bootstrap --include-default-paths","TOOLSTATE_PUBLISH":1}},{"name":"pr-check-1","full_name":"auto - pr-check-1","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"pr-check-2","full_name":"auto - pr-check-2","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true},{"name":"tidy","full_name":"auto - tidy","os":"ubuntu-24.04","env":{"ARTIFACTS_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZN24CBO55","AWS_REGION":"us-west-1","CACHES_AWS_ACCESS_KEY_ID":"AKIA46X5W6CZI5DHEBFL","DEPLOY_BUCKET":"rust-lang-ci2","TOOLSTATE_PUBLISH":1},"continue_on_error":false,"free_disk":true,"doc_url":"https://foo.bar"}] run_type=auto "#); } diff --git a/src/ci/citool/tests/test-jobs.yml b/src/ci/citool/tests/test-jobs.yml index e26104905491..95cd328e7848 100644 --- a/src/ci/citool/tests/test-jobs.yml +++ b/src/ci/citool/tests/test-jobs.yml @@ -33,7 +33,7 @@ envs: # Ensure that host tooling is tested on our minimum supported macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_15.2.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer NO_LLVM_ASSERTIONS: 1 NO_DEBUG_ASSERTIONS: 1 NO_OVERFLOW_CHECKS: 1 @@ -112,7 +112,7 @@ auto: --enable-profiler --set rust.jemalloc RUSTC_RETRY_LINKER_ON_SEGFAULT: 1 - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else # supports the hardware, so only need to test it there. diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 6e07ffc43bc2..6f333ecde79b 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -459,7 +459,7 @@ auto: # Ensure that host tooling is built to support our minimum support macOS version. MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift @@ -475,7 +475,7 @@ auto: # FIXME(madsmtm): This might be redundant, as we're not building host tooling here (?) MACOSX_DEPLOYMENT_TARGET: 10.12 MACOSX_STD_DEPLOYMENT_TARGET: 10.12 - SELECT_XCODE: /Applications/Xcode_15.2.app + DEVELOPER_DIR: /Applications/Xcode_15.2.app/Contents/Developer <<: *job-macos - name: dist-aarch64-apple @@ -496,7 +496,7 @@ auto: # supports the hardware. MACOSX_DEPLOYMENT_TARGET: 11.0 MACOSX_STD_DEPLOYMENT_TARGET: 11.0 - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 DIST_REQUIRE_ALL_TOOLS: 1 CODEGEN_BACKENDS: llvm,cranelift @@ -511,7 +511,7 @@ auto: --enable-sanitizers --enable-profiler --set rust.jemalloc - SELECT_XCODE: /Applications/Xcode_15.4.app + DEVELOPER_DIR: /Applications/Xcode_15.4.app/Contents/Developer USE_XCODE_CLANG: 1 # Aarch64 tooling only needs to support macOS 11.0 and up as nothing else # supports the hardware, so only need to test it there. diff --git a/src/ci/scripts/select-xcode.sh b/src/ci/scripts/select-xcode.sh deleted file mode 100755 index 569c4a4136d9..000000000000 --- a/src/ci/scripts/select-xcode.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/bin/bash -# This script selects the Xcode instance to use. - -set -euo pipefail -IFS=$'\n\t' - -source "$(cd "$(dirname "$0")" && pwd)/../shared.sh" - -if isMacOS; then - sudo xcode-select -s "${SELECT_XCODE}" -fi From c9f4f7f4436bf4023bacdd0f201eaf680eb967c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 31 Jan 2026 05:52:38 +0100 Subject: [PATCH 300/319] Move the query list to `queries.rs` --- compiler/rustc_middle/src/{query/mod.rs => queries.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename compiler/rustc_middle/src/{query/mod.rs => queries.rs} (100%) diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/queries.rs similarity index 100% rename from compiler/rustc_middle/src/query/mod.rs rename to compiler/rustc_middle/src/queries.rs From 590fa1e6cb42ffdec1857d03ebeb26b5e9ab43c1 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 21:31:28 +0100 Subject: [PATCH 301/319] Convert to inline diagnostics in `rustc_ty_utils` --- Cargo.lock | 2 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_ty_utils/Cargo.toml | 1 - compiler/rustc_ty_utils/messages.ftl | 61 ----------------------- compiler/rustc_ty_utils/src/errors.rs | 72 ++++++++++++++------------- compiler/rustc_ty_utils/src/lib.rs | 2 - 7 files changed, 38 insertions(+), 102 deletions(-) delete mode 100644 compiler/rustc_ty_utils/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 91fce715c8cd..ee6b26aeebc5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3817,7 +3817,6 @@ dependencies = [ "rustc_span", "rustc_target", "rustc_trait_selection", - "rustc_ty_utils", "serde_json", "shlex", "tracing", @@ -4809,7 +4808,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hashes", "rustc_hir", "rustc_index", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index d97c552b412b..0c07c23c6394 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -49,7 +49,6 @@ rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } -rustc_ty_utils = { path = "../rustc_ty_utils" } serde_json = "1.0.59" shlex = "1.0" tracing = { version = "0.1.35" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b163ae2068a6..49a100fd6757 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -143,7 +143,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, - rustc_ty_utils::DEFAULT_LOCALE_RESOURCE, // tidy-alphabetical-end ]; diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml index ce08b300cc80..682cf941561f 100644 --- a/compiler/rustc_ty_utils/Cargo.toml +++ b/compiler/rustc_ty_utils/Cargo.toml @@ -9,7 +9,6 @@ itertools = "0.12" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_ty_utils/messages.ftl b/compiler/rustc_ty_utils/messages.ftl deleted file mode 100644 index c1684bfb43b6..000000000000 --- a/compiler/rustc_ty_utils/messages.ftl +++ /dev/null @@ -1,61 +0,0 @@ -ty_utils_address_and_deref_not_supported = dereferencing or taking the address is not supported in generic constants - -ty_utils_adt_not_supported = struct/enum construction is not supported in generic constants - -ty_utils_array_not_supported = array construction is not supported in generic constants - -ty_utils_assign_not_supported = assignment is not supported in generic constants - -ty_utils_binary_not_supported = unsupported binary operation in generic constants - -ty_utils_block_not_supported = blocks are not supported in generic constants - -ty_utils_borrow_not_supported = borrowing is not supported in generic constants - -ty_utils_box_not_supported = allocations are not allowed in generic constants - -ty_utils_by_use_not_supported = .use is not allowed in generic constants - -ty_utils_closure_and_return_not_supported = closures and function keywords are not supported in generic constants - -ty_utils_const_block_not_supported = const blocks are not supported in generic constants - -ty_utils_control_flow_not_supported = control flow is not supported in generic constants - -ty_utils_field_not_supported = field access is not supported in generic constants - -ty_utils_generic_constant_too_complex = overly complex generic constant - .help = consider moving this anonymous constant into a `const` function - .maybe_supported = this operation may be supported in the future - -ty_utils_impl_trait_duplicate_arg = non-defining opaque type use in defining scope - .label = generic argument `{$arg}` used twice - .note = for this opaque type - -ty_utils_impl_trait_not_param = non-defining opaque type use in defining scope - .label = argument `{$arg}` is not a generic parameter - .note = for this opaque type - -ty_utils_index_not_supported = indexing is not supported in generic constants - -ty_utils_inline_asm_not_supported = assembly is not supported in generic constants - -ty_utils_logical_op_not_supported = unsupported operation in generic constants, short-circuiting operations would imply control flow - -ty_utils_loop_not_supported = loops and loop control flow are not supported in generic constants - -ty_utils_needs_drop_overflow = overflow while checking whether `{$query_ty}` requires drop - -ty_utils_never_to_any_not_supported = coercing the `never` type is not supported in generic constants - -ty_utils_non_primitive_simd_type = monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}` - -ty_utils_operation_not_supported = unsupported operation in generic constants - -ty_utils_pointer_not_supported = pointer casts are not allowed in generic constants - -ty_utils_tuple_not_supported = tuple construction is not supported in generic constants - -ty_utils_unexpected_fnptr_associated_item = `FnPtr` trait with unexpected associated item - -ty_utils_yield_not_supported = coroutine control flow is not allowed in generic constants diff --git a/compiler/rustc_ty_utils/src/errors.rs b/compiler/rustc_ty_utils/src/errors.rs index f92c405242ce..ccea5a49bd7c 100644 --- a/compiler/rustc_ty_utils/src/errors.rs +++ b/compiler/rustc_ty_utils/src/errors.rs @@ -6,18 +6,18 @@ use rustc_middle::ty::{GenericArg, Ty}; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(ty_utils_needs_drop_overflow)] +#[diag("overflow while checking whether `{$query_ty}` requires drop")] pub(crate) struct NeedsDropOverflow<'tcx> { pub query_ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(ty_utils_generic_constant_too_complex)] -#[help] +#[diag("overly complex generic constant")] +#[help("consider moving this anonymous constant into a `const` function")] pub(crate) struct GenericConstantTooComplex { #[primary_span] pub span: Span, - #[note(ty_utils_maybe_supported)] + #[note("this operation may be supported in the future")] pub maybe_supported: bool, #[subdiagnostic] pub sub: GenericConstantTooComplexSub, @@ -25,84 +25,88 @@ pub(crate) struct GenericConstantTooComplex { #[derive(Subdiagnostic)] pub(crate) enum GenericConstantTooComplexSub { - #[label(ty_utils_borrow_not_supported)] + #[label("borrowing is not supported in generic constants")] BorrowNotSupported(#[primary_span] Span), - #[label(ty_utils_address_and_deref_not_supported)] + #[label("dereferencing or taking the address is not supported in generic constants")] AddressAndDerefNotSupported(#[primary_span] Span), - #[label(ty_utils_array_not_supported)] + #[label("array construction is not supported in generic constants")] ArrayNotSupported(#[primary_span] Span), - #[label(ty_utils_block_not_supported)] + #[label("blocks are not supported in generic constants")] BlockNotSupported(#[primary_span] Span), - #[label(ty_utils_never_to_any_not_supported)] + #[label("coercing the `never` type is not supported in generic constants")] NeverToAnyNotSupported(#[primary_span] Span), - #[label(ty_utils_tuple_not_supported)] + #[label("tuple construction is not supported in generic constants")] TupleNotSupported(#[primary_span] Span), - #[label(ty_utils_index_not_supported)] + #[label("indexing is not supported in generic constants")] IndexNotSupported(#[primary_span] Span), - #[label(ty_utils_field_not_supported)] + #[label("field access is not supported in generic constants")] FieldNotSupported(#[primary_span] Span), - #[label(ty_utils_const_block_not_supported)] + #[label("const blocks are not supported in generic constants")] ConstBlockNotSupported(#[primary_span] Span), - #[label(ty_utils_adt_not_supported)] + #[label("struct/enum construction is not supported in generic constants")] AdtNotSupported(#[primary_span] Span), - #[label(ty_utils_pointer_not_supported)] + #[label("pointer casts are not allowed in generic constants")] PointerNotSupported(#[primary_span] Span), - #[label(ty_utils_yield_not_supported)] + #[label("coroutine control flow is not allowed in generic constants")] YieldNotSupported(#[primary_span] Span), - #[label(ty_utils_loop_not_supported)] + #[label("loops and loop control flow are not supported in generic constants")] LoopNotSupported(#[primary_span] Span), - #[label(ty_utils_box_not_supported)] + #[label("allocations are not allowed in generic constants")] BoxNotSupported(#[primary_span] Span), - #[label(ty_utils_binary_not_supported)] + #[label("unsupported binary operation in generic constants")] BinaryNotSupported(#[primary_span] Span), - #[label(ty_utils_by_use_not_supported)] + #[label(".use is not allowed in generic constants")] ByUseNotSupported(#[primary_span] Span), - #[label(ty_utils_logical_op_not_supported)] + #[label( + "unsupported operation in generic constants, short-circuiting operations would imply control flow" + )] LogicalOpNotSupported(#[primary_span] Span), - #[label(ty_utils_assign_not_supported)] + #[label("assignment is not supported in generic constants")] AssignNotSupported(#[primary_span] Span), - #[label(ty_utils_closure_and_return_not_supported)] + #[label("closures and function keywords are not supported in generic constants")] ClosureAndReturnNotSupported(#[primary_span] Span), - #[label(ty_utils_control_flow_not_supported)] + #[label("control flow is not supported in generic constants")] ControlFlowNotSupported(#[primary_span] Span), - #[label(ty_utils_inline_asm_not_supported)] + #[label("assembly is not supported in generic constants")] InlineAsmNotSupported(#[primary_span] Span), - #[label(ty_utils_operation_not_supported)] + #[label("unsupported operation in generic constants")] OperationNotSupported(#[primary_span] Span), } #[derive(Diagnostic)] -#[diag(ty_utils_unexpected_fnptr_associated_item)] +#[diag("`FnPtr` trait with unexpected associated item")] pub(crate) struct UnexpectedFnPtrAssociatedItem { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(ty_utils_non_primitive_simd_type)] +#[diag( + "monomorphising SIMD type `{$ty}` with a non-primitive-scalar (integer/float/pointer) element type `{$e_ty}`" +)] pub(crate) struct NonPrimitiveSimdType<'tcx> { pub ty: Ty<'tcx>, pub e_ty: Ty<'tcx>, } #[derive(Diagnostic)] -#[diag(ty_utils_impl_trait_duplicate_arg)] +#[diag("non-defining opaque type use in defining scope")] pub(crate) struct DuplicateArg<'tcx> { pub arg: GenericArg<'tcx>, #[primary_span] - #[label] + #[label("generic argument `{$arg}` used twice")] pub span: Span, - #[note] + #[note("for this opaque type")] pub opaque_span: Span, } #[derive(Diagnostic)] -#[diag(ty_utils_impl_trait_not_param, code = E0792)] +#[diag("non-defining opaque type use in defining scope", code = E0792)] pub(crate) struct NotParam<'tcx> { pub arg: GenericArg<'tcx>, #[primary_span] - #[label] + #[label("argument `{$arg}` is not a generic parameter")] pub span: Span, - #[note] + #[note("for this opaque type")] pub opaque_span: Span, } diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs index d8b50b2d2e42..9f8f3b240890 100644 --- a/compiler/rustc_ty_utils/src/lib.rs +++ b/compiler/rustc_ty_utils/src/lib.rs @@ -31,8 +31,6 @@ pub mod sig_types; mod structural_match; mod ty; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub fn provide(providers: &mut Providers) { abi::provide(providers); assoc::provide(providers); From 247a022957f27f064cb468e0bbba3c3dee10e066 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Sat, 31 Jan 2026 06:45:46 +0100 Subject: [PATCH 302/319] Fix references and remove inner `queries` module --- compiler/rustc_macros/src/query.rs | 4 +- .../src/rmeta/decoder/cstore_impl.rs | 7 +- compiler/rustc_middle/src/lib.rs | 2 + compiler/rustc_middle/src/queries.rs | 24 +-- compiler/rustc_middle/src/query/erase.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 23 +++ compiler/rustc_middle/src/query/plumbing.rs | 178 +++++++++--------- compiler/rustc_middle/src/util/mod.rs | 4 +- compiler/rustc_query_impl/src/lib.rs | 7 +- compiler/rustc_query_impl/src/plumbing.rs | 12 +- tests/ui-fulldeps/obtain-borrowck.rs | 2 +- 11 files changed, 136 insertions(+), 129 deletions(-) create mode 100644 compiler/rustc_middle/src/query/mod.rs diff --git a/compiler/rustc_macros/src/query.rs b/compiler/rustc_macros/src/query.rs index 0e2b16d72eb1..bc6f856e8683 100644 --- a/compiler/rustc_macros/src/query.rs +++ b/compiler/rustc_macros/src/query.rs @@ -289,7 +289,7 @@ fn add_query_desc_cached_impl( cached.extend(quote! { #[allow(unused_variables, unused_braces, rustc::pass_by_value)] #[inline] - pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::query::queries::#name::Key<'tcx>) -> bool { + pub fn #name<'tcx>(#tcx: TyCtxt<'tcx>, #key: &crate::queries::#name::Key<'tcx>) -> bool { #ra_hint #expr } @@ -301,7 +301,7 @@ fn add_query_desc_cached_impl( let desc = quote! { #[allow(unused_variables)] - pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::query::queries::#name::Key<'tcx>) -> String { + pub fn #name<'tcx>(tcx: TyCtxt<'tcx>, key: crate::queries::#name::Key<'tcx>) -> String { let (#tcx, #key) = (tcx, key); format!(#desc) } diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs index 7bd3f7db55f9..b1f3bafd92fb 100644 --- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs +++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs @@ -11,7 +11,8 @@ use rustc_middle::bug; use rustc_middle::metadata::{AmbigModChild, ModChild}; use rustc_middle::middle::exported_symbols::ExportedSymbol; use rustc_middle::middle::stability::DeprecationEntry; -use rustc_middle::query::{ExternProviders, LocalCrate}; +use rustc_middle::queries::ExternProviders; +use rustc_middle::query::LocalCrate; use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::util::Providers; @@ -134,8 +135,8 @@ macro_rules! provide_one { ($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => $compute:block) => { fn $name<'tcx>( $tcx: TyCtxt<'tcx>, - def_id_arg: rustc_middle::query::queries::$name::Key<'tcx>, - ) -> rustc_middle::query::queries::$name::ProvidedValue<'tcx> { + def_id_arg: rustc_middle::queries::$name::Key<'tcx>, + ) -> rustc_middle::queries::$name::ProvidedValue<'tcx> { let _prof_timer = $tcx.prof.generic_activity(concat!("metadata_decode_entry_", stringify!($name))); diff --git a/compiler/rustc_middle/src/lib.rs b/compiler/rustc_middle/src/lib.rs index 5682c21e4353..bb94b4c927ae 100644 --- a/compiler/rustc_middle/src/lib.rs +++ b/compiler/rustc_middle/src/lib.rs @@ -86,6 +86,8 @@ mod values; #[macro_use] pub mod query; #[macro_use] +pub mod queries; +#[macro_use] pub mod dep_graph; // Allows macros to refer to this crate as `::rustc_middle` diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index c9291d89be8a..3ade5cab0915 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -100,8 +100,6 @@ use rustc_span::{DUMMY_SP, LocalExpnId, Span, Symbol}; use rustc_target::spec::PanicStrategy; use {rustc_abi as abi, rustc_ast as ast, rustc_hir as hir}; -pub use self::keys::{AsLocalKey, Key, LocalCrate}; -pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; use crate::infer::canonical::{self, Canonical}; use crate::lint::LintExpectation; use crate::metadata::ModChild; @@ -120,7 +118,10 @@ use crate::mir::interpret::{ use crate::mir::mono::{ CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, }; -use crate::query::plumbing::CyclePlaceholder; +use crate::query::plumbing::{ + CyclePlaceholder, IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, +}; +use crate::query::{AsLocalKey, describe_as_module}; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, @@ -142,14 +143,6 @@ use crate::ty::{ }; use crate::{dep_graph, mir, thir}; -mod arena_cached; -pub mod erase; -pub(crate) mod inner; -mod keys; -pub mod on_disk_cache; -#[macro_use] -pub mod plumbing; - // Each of these queries corresponds to a function pointer field in the // `Providers` struct for requesting a value of that type, and a method // on `tcx: TyCtxt` (and `tcx.at(span)`) for doing that request in a way @@ -2783,12 +2776,3 @@ rustc_queries! { rustc_with_all_queries! { define_callbacks! } rustc_feedable_queries! { define_feedable! } - -fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { - let def_id = def_id.into(); - if def_id.is_top_level_module() { - "top-level module".to_string() - } else { - format!("module `{}`", tcx.def_path_str(def_id)) - } -} diff --git a/compiler/rustc_middle/src/query/erase.rs b/compiler/rustc_middle/src/query/erase.rs index 32071595ec28..99b1f6d8c251 100644 --- a/compiler/rustc_middle/src/query/erase.rs +++ b/compiler/rustc_middle/src/query/erase.rs @@ -15,7 +15,7 @@ use rustc_span::source_map::Spanned; use crate::mir::interpret::EvalToValTreeResult; use crate::mir::mono::{MonoItem, NormalizationErrorInMono}; -use crate::query::CyclePlaceholder; +use crate::query::plumbing::CyclePlaceholder; use crate::traits::solve; use crate::ty::adjustment::CoerceUnsizedInfo; use crate::ty::{self, Ty, TyCtxt}; diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs new file mode 100644 index 000000000000..f0121eab053b --- /dev/null +++ b/compiler/rustc_middle/src/query/mod.rs @@ -0,0 +1,23 @@ +use rustc_hir::def_id::LocalDefId; + +pub use self::keys::{AsLocalKey, Key, LocalCrate}; +pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; +pub use crate::queries::Providers; +use crate::ty::TyCtxt; + +pub(crate) mod arena_cached; +pub mod erase; +pub(crate) mod inner; +mod keys; +pub mod on_disk_cache; +#[macro_use] +pub mod plumbing; + +pub fn describe_as_module(def_id: impl Into, tcx: TyCtxt<'_>) -> String { + let def_id = def_id.into(); + if def_id.is_top_level_module() { + "top-level module".to_string() + } else { + format!("module `{}`", tcx.def_path_str(def_id)) + } +} diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 78879dc9da9a..66e7c55a9d2b 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -12,10 +12,10 @@ pub use sealed::IntoQueryParam; use crate::dep_graph; use crate::dep_graph::DepKind; -use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; -use crate::query::{ +use crate::queries::{ ExternProviders, PerQueryVTables, Providers, QueryArenas, QueryCaches, QueryEngine, QueryStates, }; +use crate::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use crate::ty::TyCtxt; pub type WillCacheOnDiskForKeyFn<'tcx, Key> = fn(tcx: TyCtxt<'tcx>, key: &Key) -> bool; @@ -227,8 +227,8 @@ macro_rules! separate_provide_extern_decl { ([(separate_provide_extern) $($rest:tt)*][$name:ident]) => { for<'tcx> fn( TyCtxt<'tcx>, - queries::$name::Key<'tcx>, - ) -> queries::$name::ProvidedValue<'tcx> + $name::Key<'tcx>, + ) -> $name::ProvidedValue<'tcx> }; ([$other:tt $($modifiers:tt)*][$($args:tt)*]) => { separate_provide_extern_decl!([$($modifiers)*][$($args)*]) @@ -266,94 +266,90 @@ macro_rules! define_callbacks { [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty, )* ) => { + $(#[allow(unused_lifetimes)] pub mod $name { + use super::*; + use $crate::query::erase::{self, Erased}; - #[allow(unused_lifetimes)] - pub mod queries { - $(pub mod $name { - use super::super::*; - use $crate::query::erase::{self, Erased}; + pub type Key<'tcx> = $($K)*; + pub type Value<'tcx> = $V; - pub type Key<'tcx> = $($K)*; - pub type Value<'tcx> = $V; + pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); - pub type LocalKey<'tcx> = local_key_if_separate_extern!([$($modifiers)*] $($K)*); + /// This type alias specifies the type returned from query providers and the type + /// used for decoding. For regular queries this is the declared returned type `V`, + /// but `arena_cache` will use `::Provided` instead. + pub type ProvidedValue<'tcx> = query_if_arena!( + [$($modifiers)*] + (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) + ($V) + ); - /// This type alias specifies the type returned from query providers and the type - /// used for decoding. For regular queries this is the declared returned type `V`, - /// but `arena_cache` will use `::Provided` instead. - pub type ProvidedValue<'tcx> = query_if_arena!( - [$($modifiers)*] - (<$V as $crate::query::arena_cached::ArenaCached<'tcx>>::Provided) - ($V) - ); + /// This function takes `ProvidedValue` and converts it to an erased `Value` by + /// allocating it on an arena if the query has the `arena_cache` modifier. The + /// value is then erased and returned. This will happen when computing the query + /// using a provider or decoding a stored result. + #[inline(always)] + pub fn provided_to_erased<'tcx>( + _tcx: TyCtxt<'tcx>, + provided_value: ProvidedValue<'tcx>, + ) -> Erased> { + // Store the provided value in an arena and get a reference + // to it, for queries with `arena_cache`. + let value: Value<'tcx> = query_if_arena!([$($modifiers)*] + { + use $crate::query::arena_cached::ArenaCached; - /// This function takes `ProvidedValue` and converts it to an erased `Value` by - /// allocating it on an arena if the query has the `arena_cache` modifier. The - /// value is then erased and returned. This will happen when computing the query - /// using a provider or decoding a stored result. - #[inline(always)] - pub fn provided_to_erased<'tcx>( - _tcx: TyCtxt<'tcx>, - provided_value: ProvidedValue<'tcx>, - ) -> Erased> { - // Store the provided value in an arena and get a reference - // to it, for queries with `arena_cache`. - let value: Value<'tcx> = query_if_arena!([$($modifiers)*] - { - use $crate::query::arena_cached::ArenaCached; - - if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.query_system.arenas.$name.alloc(v), - provided_value, - ) - } else { - <$V as ArenaCached>::alloc_in_arena( - |v| _tcx.arena.dropless.alloc(v), - provided_value, - ) - } + if mem::needs_drop::<<$V as ArenaCached<'tcx>>::Allocated>() { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.query_system.arenas.$name.alloc(v), + provided_value, + ) + } else { + <$V as ArenaCached>::alloc_in_arena( + |v| _tcx.arena.dropless.alloc(v), + provided_value, + ) } - // Otherwise, the provided value is the value. - (provided_value) - ); - erase::erase_val(value) + } + // Otherwise, the provided value is the value. + (provided_value) + ); + erase::erase_val(value) + } + + pub type Storage<'tcx> = <$($K)* as $crate::query::Key>::Cache>; + + // Ensure that keys grow no larger than 88 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + const _: () = { + if size_of::>() > 88 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a key type `", + stringify!($($K)*), + "` that is too large" + )); } + }; - pub type Storage<'tcx> = <$($K)* as keys::Key>::Cache>; - - // Ensure that keys grow no larger than 88 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - const _: () = { - if size_of::>() > 88 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a key type `", - stringify!($($K)*), - "` that is too large" - )); - } - }; - - // Ensure that values grow no larger than 64 bytes by accident. - // Increase this limit if necessary, but do try to keep the size low if possible - #[cfg(target_pointer_width = "64")] - #[cfg(not(feature = "rustc_randomized_layouts"))] - const _: () = { - if size_of::>() > 64 { - panic!("{}", concat!( - "the query `", - stringify!($name), - "` has a value type `", - stringify!($V), - "` that is too large" - )); - } - }; - })* - } + // Ensure that values grow no larger than 64 bytes by accident. + // Increase this limit if necessary, but do try to keep the size low if possible + #[cfg(target_pointer_width = "64")] + #[cfg(not(feature = "rustc_randomized_layouts"))] + const _: () = { + if size_of::>() > 64 { + panic!("{}", concat!( + "the query `", + stringify!($name), + "` has a value type `", + stringify!($V), + "` that is too large" + )); + } + }; + })* /// Holds per-query arenas for queries with the `arena_cache` modifier. #[derive(Default)] @@ -371,7 +367,7 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryCaches<'tcx> { - $($(#[$attr])* pub $name: queries::$name::Storage<'tcx>,)* + $($(#[$attr])* pub $name: $name::Storage<'tcx>,)* } impl<'tcx> TyCtxtEnsureOk<'tcx> { @@ -438,7 +434,7 @@ macro_rules! define_callbacks { /// ("Per" just makes this pluralized name more visually distinct.) pub struct PerQueryVTables<'tcx> { $( - pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, queries::$name::Storage<'tcx>>, + pub $name: ::rustc_middle::query::plumbing::QueryVTable<'tcx, $name::Storage<'tcx>>, )* } @@ -452,8 +448,8 @@ macro_rules! define_callbacks { pub struct Providers { $(pub $name: for<'tcx> fn( TyCtxt<'tcx>, - queries::$name::LocalKey<'tcx>, - ) -> queries::$name::ProvidedValue<'tcx>,)* + $name::LocalKey<'tcx>, + ) -> $name::ProvidedValue<'tcx>,)* } pub struct ExternProviders { @@ -490,7 +486,7 @@ macro_rules! define_callbacks { $(pub $name: for<'tcx> fn( TyCtxt<'tcx>, Span, - queries::$name::Key<'tcx>, + $name::Key<'tcx>, QueryMode, ) -> Option<$crate::query::erase::Erased<$V>>,)* } @@ -502,11 +498,11 @@ macro_rules! define_feedable { $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { $(#[$attr])* #[inline(always)] - pub fn $name(self, value: queries::$name::ProvidedValue<'tcx>) { + pub fn $name(self, value: $name::ProvidedValue<'tcx>) { let key = self.key().into_query_param(); let tcx = self.tcx; - let erased_value = queries::$name::provided_to_erased(tcx, value); + let erased_value = $name::provided_to_erased(tcx, value); let dep_kind: dep_graph::DepKind = dep_graph::dep_kinds::$name; diff --git a/compiler/rustc_middle/src/util/mod.rs b/compiler/rustc_middle/src/util/mod.rs index d5076a278eab..0bbe3856033b 100644 --- a/compiler/rustc_middle/src/util/mod.rs +++ b/compiler/rustc_middle/src/util/mod.rs @@ -2,7 +2,7 @@ pub mod bug; #[derive(Default, Copy, Clone)] pub struct Providers { - pub queries: crate::query::Providers, - pub extern_queries: crate::query::ExternProviders, + pub queries: crate::queries::Providers, + pub extern_queries: crate::queries::ExternProviders, pub hooks: crate::hooks::Providers, } diff --git a/compiler/rustc_query_impl/src/lib.rs b/compiler/rustc_query_impl/src/lib.rs index bd69c99a32dc..baa37111c807 100644 --- a/compiler/rustc_query_impl/src/lib.rs +++ b/compiler/rustc_query_impl/src/lib.rs @@ -13,11 +13,12 @@ use rustc_data_structures::stable_hasher::HashStable; use rustc_data_structures::sync::AtomicU64; use rustc_middle::arena::Arena; use rustc_middle::dep_graph::{self, DepKind, DepKindVTable, DepNodeIndex}; +use rustc_middle::queries::{ + self, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, +}; +use rustc_middle::query::AsLocalKey; use rustc_middle::query::on_disk_cache::{CacheEncoder, EncodedDepNodeIndex, OnDiskCache}; use rustc_middle::query::plumbing::{QuerySystem, QuerySystemFns, QueryVTable}; -use rustc_middle::query::{ - AsLocalKey, ExternProviders, Providers, QueryCaches, QueryEngine, QueryStates, queries, -}; use rustc_middle::ty::TyCtxt; use rustc_query_system::Value; use rustc_query_system::dep_graph::SerializedDepNodeIndex; diff --git a/compiler/rustc_query_impl/src/plumbing.rs b/compiler/rustc_query_impl/src/plumbing.rs index d1721f1f455f..9ffea692df3a 100644 --- a/compiler/rustc_query_impl/src/plumbing.rs +++ b/compiler/rustc_query_impl/src/plumbing.rs @@ -648,7 +648,7 @@ macro_rules! define_queries { query_state: std::mem::offset_of!(QueryStates<'tcx>, $name), query_cache: std::mem::offset_of!(QueryCaches<'tcx>, $name), will_cache_on_disk_for_key_fn: should_ever_cache_on_disk!([$($modifiers)*] { - Some(::rustc_middle::query::cached::$name) + Some(queries::cached::$name) } { None }), @@ -672,7 +672,7 @@ macro_rules! define_queries { try_load_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { Some(|tcx, key, prev_index, index| { // Check the `cache_on_disk_if` condition for this key. - if !::rustc_middle::query::cached::$name(tcx, key) { + if !queries::cached::$name(tcx, key) { return None; } @@ -687,7 +687,7 @@ macro_rules! define_queries { }), is_loadable_from_disk_fn: should_ever_cache_on_disk!([$($modifiers)*] { Some(|tcx, key, index| -> bool { - ::rustc_middle::query::cached::$name(tcx, key) && + ::rustc_middle::queries::cached::$name(tcx, key) && $crate::plumbing::loadable_from_disk(tcx, index) }) } { @@ -747,7 +747,7 @@ macro_rules! define_queries { let make_frame = |tcx, key| { let kind = rustc_middle::dep_graph::dep_kinds::$name; let name = stringify!($name); - $crate::plumbing::create_query_frame(tcx, rustc_middle::query::descs::$name, key, kind, name) + $crate::plumbing::create_query_frame(tcx, queries::descs::$name, key, kind, name) }; // Call `gather_active_jobs_inner` to do the actual work. @@ -817,8 +817,8 @@ macro_rules! define_queries { } } - pub fn make_query_vtables<'tcx>() -> ::rustc_middle::query::PerQueryVTables<'tcx> { - ::rustc_middle::query::PerQueryVTables { + pub fn make_query_vtables<'tcx>() -> queries::PerQueryVTables<'tcx> { + queries::PerQueryVTables { $( $name: query_impl::$name::make_query_vtable(), )* diff --git a/tests/ui-fulldeps/obtain-borrowck.rs b/tests/ui-fulldeps/obtain-borrowck.rs index a562d0ccd3df..ea2dab614ec1 100644 --- a/tests/ui-fulldeps/obtain-borrowck.rs +++ b/tests/ui-fulldeps/obtain-borrowck.rs @@ -37,7 +37,7 @@ use rustc_hir::def::DefKind; use rustc_hir::def_id::LocalDefId; use rustc_interface::Config; use rustc_interface::interface::Compiler; -use rustc_middle::query::queries::mir_borrowck::ProvidedValue; +use rustc_middle::queries::mir_borrowck::ProvidedValue; use rustc_middle::ty::TyCtxt; use rustc_middle::util::Providers; use rustc_session::Session; From 9de16dd3eb3ff76198125d30db0b787198855427 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 3 Feb 2026 10:13:45 +0100 Subject: [PATCH 303/319] Remove imports in `queries` only used by `define_callbacks!` --- compiler/rustc_middle/src/queries.rs | 9 +++----- compiler/rustc_middle/src/query/mod.rs | 1 + compiler/rustc_middle/src/query/plumbing.rs | 24 ++++++++++----------- 3 files changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index 3ade5cab0915..ca8e5e90318d 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -49,7 +49,7 @@ //! ## Query Expansion and Code Generation //! //! The [`rustc_macros::rustc_queries`] macro expands each query definition into: -//! - A method on [`TyCtxt`] (and [`TyCtxtAt`]) for invoking the query. +//! - A method on [`TyCtxt`] (and [`crate::query::TyCtxtAt`]) for invoking the query. //! - Provider traits and structs for supplying the query's value. //! - Caching and dependency graph integration. //! - Support for incremental compilation, disk caching, and arena allocation as controlled by the modifiers. @@ -87,7 +87,6 @@ use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, PreciseCapturingArgKind, Trait use rustc_index::IndexVec; use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; -use rustc_query_system::query::{QueryMode, QueryState}; use rustc_session::Limits; use rustc_session::config::{EntryFnType, OptLevel, OutputFilenames, SymbolManglingVersion}; use rustc_session::cstore::{ @@ -118,10 +117,8 @@ use crate::mir::interpret::{ use crate::mir::mono::{ CodegenUnit, CollectionMode, MonoItem, MonoItemPartitions, NormalizationErrorInMono, }; -use crate::query::plumbing::{ - CyclePlaceholder, IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk, -}; -use crate::query::{AsLocalKey, describe_as_module}; +use crate::query::describe_as_module; +use crate::query::plumbing::CyclePlaceholder; use crate::traits::query::{ CanonicalAliasGoal, CanonicalDropckOutlivesGoal, CanonicalImpliedOutlivesBoundsGoal, CanonicalMethodAutoderefStepsGoal, CanonicalPredicateGoal, CanonicalTypeOpAscribeUserTypeGoal, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f0121eab053b..24a38e70ff6f 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1,4 +1,5 @@ use rustc_hir::def_id::LocalDefId; +pub use rustc_query_system::query::{QueryMode, QueryState}; pub use self::keys::{AsLocalKey, Key, LocalCrate}; pub use self::plumbing::{IntoQueryParam, TyCtxtAt, TyCtxtEnsureDone, TyCtxtEnsureOk}; diff --git a/compiler/rustc_middle/src/query/plumbing.rs b/compiler/rustc_middle/src/query/plumbing.rs index 66e7c55a9d2b..0b7dc3092719 100644 --- a/compiler/rustc_middle/src/query/plumbing.rs +++ b/compiler/rustc_middle/src/query/plumbing.rs @@ -189,8 +189,8 @@ macro_rules! query_ensure_select { } macro_rules! query_helper_param_ty { - (DefId) => { impl IntoQueryParam }; - (LocalDefId) => { impl IntoQueryParam }; + (DefId) => { impl $crate::query::IntoQueryParam }; + (LocalDefId) => { impl $crate::query::IntoQueryParam }; ($K:ty) => { $K }; } @@ -213,7 +213,7 @@ macro_rules! local_key_if_separate_extern { $($K)* }; ([(separate_provide_extern) $($rest:tt)*] $($K:tt)*) => { - <$($K)* as AsLocalKey>::LocalKey + <$($K)* as $crate::query::AsLocalKey>::LocalKey }; ([$other:tt $($modifiers:tt)*] $($K:tt)*) => { local_key_if_separate_extern!([$($modifiers)*] $($K)*) @@ -370,7 +370,7 @@ macro_rules! define_callbacks { $($(#[$attr])* pub $name: $name::Storage<'tcx>,)* } - impl<'tcx> TyCtxtEnsureOk<'tcx> { + impl<'tcx> $crate::query::TyCtxtEnsureOk<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name( @@ -382,13 +382,13 @@ macro_rules! define_callbacks { self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), false, ) })* } - impl<'tcx> TyCtxtEnsureDone<'tcx> { + impl<'tcx> $crate::query::TyCtxtEnsureDone<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) { @@ -396,7 +396,7 @@ macro_rules! define_callbacks { self.tcx, self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), true, ); })* @@ -412,7 +412,7 @@ macro_rules! define_callbacks { })* } - impl<'tcx> TyCtxtAt<'tcx> { + impl<'tcx> $crate::query::TyCtxtAt<'tcx> { $($(#[$attr])* #[inline(always)] pub fn $name(self, key: query_helper_param_ty!($($K)*)) -> $V @@ -424,7 +424,7 @@ macro_rules! define_callbacks { self.tcx.query_system.fns.engine.$name, &self.tcx.query_system.caches.$name, self.span, - key.into_query_param(), + $crate::query::IntoQueryParam::into_query_param(key), )) })* } @@ -441,7 +441,7 @@ macro_rules! define_callbacks { #[derive(Default)] pub struct QueryStates<'tcx> { $( - pub $name: QueryState<'tcx, $($K)*>, + pub $name: $crate::query::QueryState<'tcx, $($K)*>, )* } @@ -487,7 +487,7 @@ macro_rules! define_callbacks { TyCtxt<'tcx>, Span, $name::Key<'tcx>, - QueryMode, + $crate::query::QueryMode, ) -> Option<$crate::query::erase::Erased<$V>>,)* } }; @@ -495,7 +495,7 @@ macro_rules! define_callbacks { macro_rules! define_feedable { ($($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => { - $(impl<'tcx, K: IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { + $(impl<'tcx, K: $crate::query::IntoQueryParam<$($K)*> + Copy> TyCtxtFeed<'tcx, K> { $(#[$attr])* #[inline(always)] pub fn $name(self, value: $name::ProvidedValue<'tcx>) { From d457ffd4f4589dfdd1f8fd2ef1c3d784018cf127 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 22:44:03 +0100 Subject: [PATCH 304/319] Convert to inline diagnostics in `rustc_monomorphize` --- Cargo.lock | 2 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_monomorphize/Cargo.toml | 1 - compiler/rustc_monomorphize/messages.ftl | 82 ---------------- compiler/rustc_monomorphize/src/errors.rs | 109 ++++++++++++++++------ compiler/rustc_monomorphize/src/lib.rs | 2 - 7 files changed, 82 insertions(+), 116 deletions(-) delete mode 100644 compiler/rustc_monomorphize/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 91fce715c8cd..b7dd59d857fd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3805,7 +3805,6 @@ dependencies = [ "rustc_mir_build", "rustc_mir_dataflow", "rustc_mir_transform", - "rustc_monomorphize", "rustc_parse", "rustc_passes", "rustc_pattern_analysis", @@ -4384,7 +4383,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_index", "rustc_macros", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index d97c552b412b..28972d1c35ec 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -37,7 +37,6 @@ rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } rustc_mir_dataflow = { path = "../rustc_mir_dataflow" } rustc_mir_transform = { path = "../rustc_mir_transform" } -rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } rustc_passes = { path = "../rustc_passes" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index b163ae2068a6..80724c6a24f5 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -134,7 +134,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_mir_build::DEFAULT_LOCALE_RESOURCE, rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE, rustc_mir_transform::DEFAULT_LOCALE_RESOURCE, - rustc_monomorphize::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_passes::DEFAULT_LOCALE_RESOURCE, rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_monomorphize/Cargo.toml b/compiler/rustc_monomorphize/Cargo.toml index 0829d52283ab..552c092ef7c4 100644 --- a/compiler/rustc_monomorphize/Cargo.toml +++ b/compiler/rustc_monomorphize/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } rustc_macros = { path = "../rustc_macros" } diff --git a/compiler/rustc_monomorphize/messages.ftl b/compiler/rustc_monomorphize/messages.ftl deleted file mode 100644 index 9c791208c093..000000000000 --- a/compiler/rustc_monomorphize/messages.ftl +++ /dev/null @@ -1,82 +0,0 @@ -monomorphize_abi_error_disabled_vector_type = - this function {$is_call -> - [true] call - *[false] definition - } uses {$is_scalable -> - [true] scalable - *[false] SIMD - } vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> - [true] {" "}in the caller - *[false] {""} - } - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) - -monomorphize_abi_error_unsupported_unsized_parameter = - this function {$is_call -> - [true] call - *[false] definition - } uses unsized type `{$ty}` which is not supported with the chosen ABI - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = only rustic ABIs support unsized parameters - -monomorphize_abi_error_unsupported_vector_type = - this function {$is_call -> - [true] call - *[false] definition - } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI - .label = function {$is_call -> - [true] called - *[false] defined - } here - -monomorphize_abi_required_target_feature = - this function {$is_call -> - [true] call - *[false] definition - } uses ABI "{$abi}" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> - [true] {" "}in the caller - *[false] {""} - } - .label = function {$is_call -> - [true] called - *[false] defined - } here - .help = consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable="{$required_feature}")]`) - -monomorphize_couldnt_dump_mono_stats = - unexpected error occurred while dumping monomorphization stats: {$error} - -monomorphize_encountered_error_while_instantiating = - the above error was encountered while instantiating `{$kind} {$instance}` - -monomorphize_encountered_error_while_instantiating_global_asm = - the above error was encountered while instantiating `global_asm` - -monomorphize_large_assignments = - moving {$size} bytes - .label = value moved from here - .note = the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = "..."]` - -monomorphize_no_optimized_mir = - missing optimized MIR for `{$instance}` in the crate `{$crate_name}` - .note = missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?) - -monomorphize_recursion_limit = - reached the recursion limit while instantiating `{$instance}` - .note = `{$def_path_str}` defined here - -monomorphize_start_not_found = using `fn main` requires the standard library - .help = use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]` - -monomorphize_static_initializer_cyclic = static initializer forms a cycle involving `{$head}` - .label = part of this cycle - .note = cyclic static initializers are not supported for target `{$target}` - -monomorphize_symbol_already_defined = symbol `{$symbol}` is already defined diff --git a/compiler/rustc_monomorphize/src/errors.rs b/compiler/rustc_monomorphize/src/errors.rs index 723649f22117..d045ae0b92cb 100644 --- a/compiler/rustc_monomorphize/src/errors.rs +++ b/compiler/rustc_monomorphize/src/errors.rs @@ -3,37 +3,41 @@ use rustc_middle::ty::{Instance, Ty}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(monomorphize_recursion_limit)] +#[diag("reached the recursion limit while instantiating `{$instance}`")] pub(crate) struct RecursionLimit<'tcx> { #[primary_span] pub span: Span, pub instance: Instance<'tcx>, - #[note] + #[note("`{$def_path_str}` defined here")] pub def_span: Span, pub def_path_str: String, } #[derive(Diagnostic)] -#[diag(monomorphize_no_optimized_mir)] +#[diag("missing optimized MIR for `{$instance}` in the crate `{$crate_name}`")] pub(crate) struct NoOptimizedMir { - #[note] + #[note( + "missing optimized MIR for this item (was the crate `{$crate_name}` compiled with `--emit=metadata`?)" + )] pub span: Span, pub crate_name: Symbol, pub instance: String, } #[derive(LintDiagnostic)] -#[diag(monomorphize_large_assignments)] -#[note] +#[diag("moving {$size} bytes")] +#[note( + "the current maximum size is {$limit}, but it can be customized with the move_size_limit attribute: `#![move_size_limit = \"...\"]`" +)] pub(crate) struct LargeAssignmentsLint { - #[label] + #[label("value moved from here")] pub span: Span, pub size: u64, pub limit: u64, } #[derive(Diagnostic)] -#[diag(monomorphize_symbol_already_defined)] +#[diag("symbol `{$symbol}` is already defined")] pub(crate) struct SymbolAlreadyDefined { #[primary_span] pub span: Option, @@ -41,13 +45,13 @@ pub(crate) struct SymbolAlreadyDefined { } #[derive(Diagnostic)] -#[diag(monomorphize_couldnt_dump_mono_stats)] +#[diag("unexpected error occurred while dumping monomorphization stats: {$error}")] pub(crate) struct CouldntDumpMonoStats { pub error: String, } #[derive(Diagnostic)] -#[diag(monomorphize_encountered_error_while_instantiating)] +#[diag("the above error was encountered while instantiating `{$kind} {$instance}`")] pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { #[primary_span] pub span: Span, @@ -56,23 +60,41 @@ pub(crate) struct EncounteredErrorWhileInstantiating<'tcx> { } #[derive(Diagnostic)] -#[diag(monomorphize_encountered_error_while_instantiating_global_asm)] +#[diag("the above error was encountered while instantiating `global_asm`")] pub(crate) struct EncounteredErrorWhileInstantiatingGlobalAsm { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(monomorphize_start_not_found)] -#[help] +#[diag("using `fn main` requires the standard library")] +#[help( + "use `#![no_main]` to bypass the Rust generated entrypoint and declare a platform specific entrypoint yourself, usually with `#[no_mangle]`" +)] pub(crate) struct StartNotFound; #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_disabled_vector_type)] -#[help] +#[diag("this function {$is_call -> + [true] call + *[false] definition +} uses {$is_scalable -> + [true] scalable + *[false] SIMD +} vector type `{$ty}` which (with the chosen ABI) requires the `{$required_feature}` target feature, which is not enabled{$is_call -> + [true] {\" \"}in the caller + *[false] {\"\"} +}")] +#[help( + "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" +)] pub(crate) struct AbiErrorDisabledVectorType<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub required_feature: &'a str, pub ty: Ty<'a>, @@ -83,11 +105,21 @@ pub(crate) struct AbiErrorDisabledVectorType<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_unsupported_unsized_parameter)] -#[help] +#[diag( + "this function {$is_call -> + [true] call + *[false] definition + } uses unsized type `{$ty}` which is not supported with the chosen ABI" +)] +#[help("only rustic ABIs support unsized parameters")] pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub ty: Ty<'a>, /// Whether this is a problem at a call site or at a declaration. @@ -95,10 +127,20 @@ pub(crate) struct AbiErrorUnsupportedUnsizedParameter<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_error_unsupported_vector_type)] +#[diag( + "this function {$is_call -> + [true] call + *[false] definition + } uses SIMD vector type `{$ty}` which is not currently supported with the chosen ABI" +)] pub(crate) struct AbiErrorUnsupportedVectorType<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> + [true] called + *[false] defined + } here" + )] pub span: Span, pub ty: Ty<'a>, /// Whether this is a problem at a call site or at a declaration. @@ -106,11 +148,24 @@ pub(crate) struct AbiErrorUnsupportedVectorType<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_abi_required_target_feature)] -#[help] +#[diag("this function {$is_call -> + [true] call + *[false] definition +} uses ABI \"{$abi}\" which requires the `{$required_feature}` target feature, which is not enabled{$is_call -> + [true] {\" \"}in the caller + *[false] {\"\"} +}")] +#[help( + "consider enabling it globally (`-C target-feature=+{$required_feature}`) or locally (`#[target_feature(enable=\"{$required_feature}\")]`)" +)] pub(crate) struct AbiRequiredTargetFeature<'a> { #[primary_span] - #[label] + #[label( + "function {$is_call -> +[true] called +*[false] defined +} here" + )] pub span: Span, pub required_feature: &'a str, pub abi: &'a str, @@ -119,12 +174,12 @@ pub(crate) struct AbiRequiredTargetFeature<'a> { } #[derive(Diagnostic)] -#[diag(monomorphize_static_initializer_cyclic)] -#[note] +#[diag("static initializer forms a cycle involving `{$head}`")] +#[note("cyclic static initializers are not supported for target `{$target}`")] pub(crate) struct StaticInitializerCyclic<'a> { #[primary_span] pub span: Span, - #[label] + #[label("part of this cycle")] pub labels: Vec, pub head: &'a str, pub target: &'a str, diff --git a/compiler/rustc_monomorphize/src/lib.rs b/compiler/rustc_monomorphize/src/lib.rs index 30f6934bb937..51753563b67c 100644 --- a/compiler/rustc_monomorphize/src/lib.rs +++ b/compiler/rustc_monomorphize/src/lib.rs @@ -20,8 +20,6 @@ mod mono_checks; mod partitioning; mod util; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - fn custom_coerce_unsize_info<'tcx>( tcx: TyCtxtAt<'tcx>, source_ty: Ty<'tcx>, From 4cacfc00bcb875c04318b516139d0d8cf559ff7c Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 15:08:00 +0100 Subject: [PATCH 305/319] Convert to inline diagnostics in `rustc_incremental` --- Cargo.lock | 2 - compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_incremental/Cargo.toml | 1 - compiler/rustc_incremental/messages.ftl | 102 ---------------------- compiler/rustc_incremental/src/errors.rs | 106 +++++++++++++---------- compiler/rustc_incremental/src/lib.rs | 2 - 7 files changed, 62 insertions(+), 153 deletions(-) delete mode 100644 compiler/rustc_incremental/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 2be69cf0b23b..2bcf7d52310e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3789,7 +3789,6 @@ dependencies = [ "rustc_hir_analysis", "rustc_hir_pretty", "rustc_hir_typeck", - "rustc_incremental", "rustc_index", "rustc_interface", "rustc_lexer", @@ -4048,7 +4047,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_fs_util", "rustc_graphviz", "rustc_hashes", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index bdc577622b24..f888c54cefcd 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -23,7 +23,6 @@ rustc_feature = { path = "../rustc_feature" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_hir_typeck = { path = "../rustc_hir_typeck" } -rustc_incremental = { path = "../rustc_incremental" } rustc_index = { path = "../rustc_index" } rustc_interface = { path = "../rustc_interface" } rustc_lexer = { path = "../rustc_lexer" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index cdc9959605c1..cdf636f97d45 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -124,7 +124,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_expand::DEFAULT_LOCALE_RESOURCE, rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE, - rustc_incremental::DEFAULT_LOCALE_RESOURCE, rustc_interface::DEFAULT_LOCALE_RESOURCE, rustc_lint::DEFAULT_LOCALE_RESOURCE, rustc_metadata::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_incremental/Cargo.toml b/compiler/rustc_incremental/Cargo.toml index db0a58418875..ae96cc62e54a 100644 --- a/compiler/rustc_incremental/Cargo.toml +++ b/compiler/rustc_incremental/Cargo.toml @@ -9,7 +9,6 @@ rand = "0.9.0" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hashes = { path = "../rustc_hashes" } diff --git a/compiler/rustc_incremental/messages.ftl b/compiler/rustc_incremental/messages.ftl deleted file mode 100644 index bbc1fab05dfe..000000000000 --- a/compiler/rustc_incremental/messages.ftl +++ /dev/null @@ -1,102 +0,0 @@ -incremental_assert_loaded = - we asserted that an existing incremental cache directory should be successfully loaded, but it was not - -incremental_assert_not_loaded = - we asserted that the incremental cache should not be loaded, but it was loaded - -incremental_assertion_auto = - `except` specified DepNodes that can not be affected for "{$name}": "{$e}" - -incremental_associated_value_expected = expected an associated value - -incremental_associated_value_expected_for = associated value expected for `{$ident}` - -incremental_canonicalize_path = incremental compilation: error canonicalizing path `{$path}`: {$err} - -incremental_cargo_help_1 = - incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental) -incremental_cargo_help_2 = - the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir) - -incremental_copy_workproduct_to_cache = - error copying object file `{$from}` to incremental directory as `{$to}`: {$err} - -incremental_corrupt_file = corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant. - -incremental_create_dep_graph = failed to create dependency graph at `{$path}`: {$err} - -incremental_create_incr_comp_dir = - could not create incremental compilation {$tag} directory `{$path}`: {$err} - -incremental_create_lock = - incremental compilation: could not create session directory lock file: {$lock_err} -incremental_create_new = failed to create {$name} at `{$path}`: {$err} - -incremental_delete_full = error deleting incremental compilation session directory `{$path}`: {$err} - -incremental_delete_incompatible = - failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err} - -incremental_delete_lock = - error deleting lock file for incremental compilation session directory `{$path}`: {$err} - -incremental_delete_old = unable to delete old {$name} at `{$path}`: {$err} - -incremental_delete_partial = failed to delete partly initialized session dir `{$path}`: {$err} - -incremental_delete_workproduct = file-system error deleting outdated file `{$path}`: {$err} - -incremental_finalize = error finalizing incremental compilation session directory `{$path}`: {$err} - -incremental_finalized_gc_failed = - failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err} - -incremental_hard_link_failed = - hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}` - -incremental_invalid_gc_failed = - failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err} - -incremental_load_dep_graph = could not load dep-graph from `{$path}`: {$err} - -incremental_lock_unsupported = - the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation - -incremental_missing_depnode = missing `DepNode` variant - -incremental_missing_if_this_changed = no `#[rustc_if_this_changed]` annotation detected - -incremental_move_dep_graph = failed to move dependency graph from `{$from}` to `{$to}`: {$err} - -incremental_no_cfg = no cfg attribute - -incremental_no_path = no path from `{$source}` to `{$target}` - -incremental_not_clean = `{$dep_node_str}` should be clean but is not - -incremental_not_dirty = `{$dep_node_str}` should be dirty but is not - -incremental_not_loaded = `{$dep_node_str}` should have been loaded from disk but it was not - -incremental_ok = OK - -incremental_repeated_depnode_label = dep-node label `{$label}` is repeated - -incremental_session_gc_failed = - failed to garbage collect incremental compilation session directory `{$path}`: {$err} - -incremental_unchecked_clean = found unchecked `#[rustc_clean]` attribute - -incremental_undefined_clean_dirty_assertions = - clean/dirty auto-assertions not yet defined for {$kind} - -incremental_undefined_clean_dirty_assertions_item = - clean/dirty auto-assertions not yet defined for Node::Item.node={$kind} - -incremental_unknown_rustc_clean_argument = unknown `rustc_clean` argument - -incremental_unrecognized_depnode = unrecognized `DepNode` variant: {$name} - -incremental_unrecognized_depnode_label = dep-node label `{$label}` not recognized - -incremental_write_new = failed to write {$name} to `{$path}`: {$err} diff --git a/compiler/rustc_incremental/src/errors.rs b/compiler/rustc_incremental/src/errors.rs index dbc72d085be9..65109fdec03a 100644 --- a/compiler/rustc_incremental/src/errors.rs +++ b/compiler/rustc_incremental/src/errors.rs @@ -4,7 +4,7 @@ use rustc_macros::Diagnostic; use rustc_span::{Ident, Span, Symbol}; #[derive(Diagnostic)] -#[diag(incremental_unrecognized_depnode)] +#[diag("unrecognized `DepNode` variant: {$name}")] pub(crate) struct UnrecognizedDepNode { #[primary_span] pub span: Span, @@ -12,28 +12,28 @@ pub(crate) struct UnrecognizedDepNode { } #[derive(Diagnostic)] -#[diag(incremental_missing_depnode)] +#[diag("missing `DepNode` variant")] pub(crate) struct MissingDepNode { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_missing_if_this_changed)] +#[diag("no `#[rustc_if_this_changed]` annotation detected")] pub(crate) struct MissingIfThisChanged { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_ok)] +#[diag("OK")] pub(crate) struct Ok { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_no_path)] +#[diag("no path from `{$source}` to `{$target}`")] pub(crate) struct NoPath { #[primary_span] pub span: Span, @@ -42,7 +42,7 @@ pub(crate) struct NoPath { } #[derive(Diagnostic)] -#[diag(incremental_assertion_auto)] +#[diag("`except` specified DepNodes that can not be affected for \"{$name}\": \"{$e}\"")] pub(crate) struct AssertionAuto<'a> { #[primary_span] pub span: Span, @@ -51,7 +51,7 @@ pub(crate) struct AssertionAuto<'a> { } #[derive(Diagnostic)] -#[diag(incremental_undefined_clean_dirty_assertions_item)] +#[diag("clean/dirty auto-assertions not yet defined for Node::Item.node={$kind}")] pub(crate) struct UndefinedCleanDirtyItem { #[primary_span] pub span: Span, @@ -59,7 +59,7 @@ pub(crate) struct UndefinedCleanDirtyItem { } #[derive(Diagnostic)] -#[diag(incremental_undefined_clean_dirty_assertions)] +#[diag("clean/dirty auto-assertions not yet defined for {$kind}")] pub(crate) struct UndefinedCleanDirty { #[primary_span] pub span: Span, @@ -67,7 +67,7 @@ pub(crate) struct UndefinedCleanDirty { } #[derive(Diagnostic)] -#[diag(incremental_repeated_depnode_label)] +#[diag("dep-node label `{$label}` is repeated")] pub(crate) struct RepeatedDepNodeLabel<'a> { #[primary_span] pub span: Span, @@ -75,7 +75,7 @@ pub(crate) struct RepeatedDepNodeLabel<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unrecognized_depnode_label)] +#[diag("dep-node label `{$label}` not recognized")] pub(crate) struct UnrecognizedDepNodeLabel<'a> { #[primary_span] pub span: Span, @@ -83,7 +83,7 @@ pub(crate) struct UnrecognizedDepNodeLabel<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_dirty)] +#[diag("`{$dep_node_str}` should be dirty but is not")] pub(crate) struct NotDirty<'a> { #[primary_span] pub span: Span, @@ -91,7 +91,7 @@ pub(crate) struct NotDirty<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_clean)] +#[diag("`{$dep_node_str}` should be clean but is not")] pub(crate) struct NotClean<'a> { #[primary_span] pub span: Span, @@ -99,7 +99,7 @@ pub(crate) struct NotClean<'a> { } #[derive(Diagnostic)] -#[diag(incremental_not_loaded)] +#[diag("`{$dep_node_str}` should have been loaded from disk but it was not")] pub(crate) struct NotLoaded<'a> { #[primary_span] pub span: Span, @@ -107,21 +107,21 @@ pub(crate) struct NotLoaded<'a> { } #[derive(Diagnostic)] -#[diag(incremental_unknown_rustc_clean_argument)] +#[diag("unknown `rustc_clean` argument")] pub(crate) struct UnknownRustcCleanArgument { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_no_cfg)] +#[diag("no cfg attribute")] pub(crate) struct NoCfg { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_associated_value_expected_for)] +#[diag("associated value expected for `{$ident}`")] pub(crate) struct AssociatedValueExpectedFor { #[primary_span] pub span: Span, @@ -129,21 +129,21 @@ pub(crate) struct AssociatedValueExpectedFor { } #[derive(Diagnostic)] -#[diag(incremental_associated_value_expected)] +#[diag("expected an associated value")] pub(crate) struct AssociatedValueExpected { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_unchecked_clean)] +#[diag("found unchecked `#[rustc_clean]` attribute")] pub(crate) struct UncheckedClean { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(incremental_delete_old)] +#[diag("unable to delete old {$name} at `{$path}`: {$err}")] pub(crate) struct DeleteOld<'a> { pub name: &'a str, pub path: PathBuf, @@ -151,7 +151,7 @@ pub(crate) struct DeleteOld<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_new)] +#[diag("failed to create {$name} at `{$path}`: {$err}")] pub(crate) struct CreateNew<'a> { pub name: &'a str, pub path: PathBuf, @@ -159,7 +159,7 @@ pub(crate) struct CreateNew<'a> { } #[derive(Diagnostic)] -#[diag(incremental_write_new)] +#[diag("failed to write {$name} to `{$path}`: {$err}")] pub(crate) struct WriteNew<'a> { pub name: &'a str, pub path: PathBuf, @@ -167,14 +167,14 @@ pub(crate) struct WriteNew<'a> { } #[derive(Diagnostic)] -#[diag(incremental_canonicalize_path)] +#[diag("incremental compilation: error canonicalizing path `{$path}`: {$err}")] pub(crate) struct CanonicalizePath { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_create_incr_comp_dir)] +#[diag("could not create incremental compilation {$tag} directory `{$path}`: {$err}")] pub(crate) struct CreateIncrCompDir<'a> { pub tag: &'a str, pub path: &'a Path, @@ -182,96 +182,112 @@ pub(crate) struct CreateIncrCompDir<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_lock)] +#[diag("incremental compilation: could not create session directory lock file: {$lock_err}")] pub(crate) struct CreateLock<'a> { pub lock_err: std::io::Error, pub session_dir: &'a Path, - #[note(incremental_lock_unsupported)] + #[note( + "the filesystem for the incremental path at {$session_dir} does not appear to support locking, consider changing the incremental path to a filesystem that supports locking or disable incremental compilation" + )] pub is_unsupported_lock: bool, - #[help(incremental_cargo_help_1)] - #[help(incremental_cargo_help_2)] + #[help( + "incremental compilation can be disabled by setting the environment variable CARGO_INCREMENTAL=0 (see https://doc.rust-lang.org/cargo/reference/profiles.html#incremental)" + )] + #[help( + "the entire build directory can be changed to a different filesystem by setting the environment variable CARGO_TARGET_DIR to a different path (see https://doc.rust-lang.org/cargo/reference/config.html#buildtarget-dir)" + )] pub is_cargo: bool, } #[derive(Diagnostic)] -#[diag(incremental_delete_lock)] +#[diag("error deleting lock file for incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct DeleteLock<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_hard_link_failed)] +#[diag( + "hard linking files in the incremental compilation cache failed. copying files instead. consider moving the cache directory to a file system which supports hard linking in session dir `{$path}`" +)] pub(crate) struct HardLinkFailed<'a> { pub path: &'a Path, } #[derive(Diagnostic)] -#[diag(incremental_delete_partial)] +#[diag("failed to delete partly initialized session dir `{$path}`: {$err}")] pub(crate) struct DeletePartial<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_delete_full)] +#[diag("error deleting incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct DeleteFull<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_finalize)] +#[diag("error finalizing incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct Finalize<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_invalid_gc_failed)] +#[diag( + "failed to garbage collect invalid incremental compilation session directory `{$path}`: {$err}" +)] pub(crate) struct InvalidGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_finalized_gc_failed)] +#[diag( + "failed to garbage collect finalized incremental compilation session directory `{$path}`: {$err}" +)] pub(crate) struct FinalizedGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_session_gc_failed)] +#[diag("failed to garbage collect incremental compilation session directory `{$path}`: {$err}")] pub(crate) struct SessionGcFailed<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_assert_not_loaded)] +#[diag("we asserted that the incremental cache should not be loaded, but it was loaded")] pub(crate) struct AssertNotLoaded; #[derive(Diagnostic)] -#[diag(incremental_assert_loaded)] +#[diag( + "we asserted that an existing incremental cache directory should be successfully loaded, but it was not" +)] pub(crate) struct AssertLoaded; #[derive(Diagnostic)] -#[diag(incremental_delete_incompatible)] +#[diag( + "failed to delete invalidated or incompatible incremental compilation session directory contents `{$path}`: {$err}" +)] pub(crate) struct DeleteIncompatible { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_load_dep_graph)] +#[diag("could not load dep-graph from `{$path}`: {$err}")] pub(crate) struct LoadDepGraph { pub path: PathBuf, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_move_dep_graph)] +#[diag("failed to move dependency graph from `{$from}` to `{$to}`: {$err}")] pub(crate) struct MoveDepGraph<'a> { pub from: &'a Path, pub to: &'a Path, @@ -279,14 +295,14 @@ pub(crate) struct MoveDepGraph<'a> { } #[derive(Diagnostic)] -#[diag(incremental_create_dep_graph)] +#[diag("failed to create dependency graph at `{$path}`: {$err}")] pub(crate) struct CreateDepGraph<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_copy_workproduct_to_cache)] +#[diag("error copying object file `{$from}` to incremental directory as `{$to}`: {$err}")] pub(crate) struct CopyWorkProductToCache<'a> { pub from: &'a Path, pub to: &'a Path, @@ -294,14 +310,16 @@ pub(crate) struct CopyWorkProductToCache<'a> { } #[derive(Diagnostic)] -#[diag(incremental_delete_workproduct)] +#[diag("file-system error deleting outdated file `{$path}`: {$err}")] pub(crate) struct DeleteWorkProduct<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(incremental_corrupt_file)] +#[diag( + "corrupt incremental compilation artifact found at `{$path}`. This file will automatically be ignored and deleted. If you see this message repeatedly or can provoke it without manually manipulating the compiler's artifacts, please file an issue. The incremental compilation system relies on hardlinks and filesystem locks behaving correctly, and may not deal well with OS crashes, so whatever information you can provide about your filesystem or other state may be very relevant." +)] pub(crate) struct CorruptFile<'a> { pub path: &'a Path, } diff --git a/compiler/rustc_incremental/src/lib.rs b/compiler/rustc_incremental/src/lib.rs index e750810c0119..591ade379e6a 100644 --- a/compiler/rustc_incremental/src/lib.rs +++ b/compiler/rustc_incremental/src/lib.rs @@ -21,5 +21,3 @@ pub fn provide(providers: &mut Providers) { providers.hooks.save_dep_graph = |tcx| tcx.sess.time("serialize_dep_graph", || persist::save_dep_graph(tcx)); } - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From 4fdd085e2879417094636f252c476f5d1f7737be Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 23:01:11 +0100 Subject: [PATCH 306/319] Convert to inline diagnostics in `rustc_mir_dataflow` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_mir_dataflow/Cargo.toml | 1 - compiler/rustc_mir_dataflow/messages.ftl | 17 ----------------- compiler/rustc_mir_dataflow/src/errors.rs | 12 ++++++------ compiler/rustc_mir_dataflow/src/lib.rs | 2 -- 7 files changed, 6 insertions(+), 30 deletions(-) delete mode 100644 compiler/rustc_mir_dataflow/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 2be69cf0b23b..659118e47ff5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3799,7 +3799,6 @@ dependencies = [ "rustc_metadata", "rustc_middle", "rustc_mir_build", - "rustc_mir_dataflow", "rustc_mir_transform", "rustc_monomorphize", "rustc_parse", @@ -4333,7 +4332,6 @@ dependencies = [ "rustc_abi", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_graphviz", "rustc_hir", "rustc_index", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index bdc577622b24..7f01b0e5bd96 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -33,7 +33,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_metadata = { path = "../rustc_metadata" } rustc_middle = { path = "../rustc_middle" } rustc_mir_build = { path = "../rustc_mir_build" } -rustc_mir_dataflow = { path = "../rustc_mir_dataflow" } rustc_mir_transform = { path = "../rustc_mir_transform" } rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index cdc9959605c1..55f78f0e01ab 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -130,7 +130,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_metadata::DEFAULT_LOCALE_RESOURCE, rustc_middle::DEFAULT_LOCALE_RESOURCE, rustc_mir_build::DEFAULT_LOCALE_RESOURCE, - rustc_mir_dataflow::DEFAULT_LOCALE_RESOURCE, rustc_mir_transform::DEFAULT_LOCALE_RESOURCE, rustc_monomorphize::DEFAULT_LOCALE_RESOURCE, rustc_parse::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_mir_dataflow/Cargo.toml b/compiler/rustc_mir_dataflow/Cargo.toml index e41d5da71397..156caa7030a0 100644 --- a/compiler/rustc_mir_dataflow/Cargo.toml +++ b/compiler/rustc_mir_dataflow/Cargo.toml @@ -10,7 +10,6 @@ regex = "1" rustc_abi = { path = "../rustc_abi" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_graphviz = { path = "../rustc_graphviz" } rustc_hir = { path = "../rustc_hir" } rustc_index = { path = "../rustc_index" } diff --git a/compiler/rustc_mir_dataflow/messages.ftl b/compiler/rustc_mir_dataflow/messages.ftl deleted file mode 100644 index 3783c647b03e..000000000000 --- a/compiler/rustc_mir_dataflow/messages.ftl +++ /dev/null @@ -1,17 +0,0 @@ -mir_dataflow_peek_argument_not_a_local = - rustc_peek: argument was not a local - -mir_dataflow_peek_argument_untracked = - rustc_peek: argument untracked - -mir_dataflow_peek_bit_not_set = - rustc_peek: bit not set - -mir_dataflow_peek_must_be_not_temporary = - dataflow::sanity_check cannot feed a non-temp to rustc_peek - -mir_dataflow_peek_must_be_place_or_ref_place = - rustc_peek: argument expression must be either `place` or `&place` - -mir_dataflow_stop_after_dataflow_ended_compilation = - stop_after_dataflow ended compilation diff --git a/compiler/rustc_mir_dataflow/src/errors.rs b/compiler/rustc_mir_dataflow/src/errors.rs index 9d8c34c8a1f3..051c0305cf2b 100644 --- a/compiler/rustc_mir_dataflow/src/errors.rs +++ b/compiler/rustc_mir_dataflow/src/errors.rs @@ -2,39 +2,39 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(mir_dataflow_stop_after_dataflow_ended_compilation)] +#[diag("stop_after_dataflow ended compilation")] pub(crate) struct StopAfterDataFlowEndedCompilation; #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_must_be_place_or_ref_place)] +#[diag("rustc_peek: argument expression must be either `place` or `&place`")] pub(crate) struct PeekMustBePlaceOrRefPlace { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_must_be_not_temporary)] +#[diag("dataflow::sanity_check cannot feed a non-temp to rustc_peek")] pub(crate) struct PeekMustBeNotTemporary { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_bit_not_set)] +#[diag("rustc_peek: bit not set")] pub(crate) struct PeekBitNotSet { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_argument_not_a_local)] +#[diag("rustc_peek: argument was not a local")] pub(crate) struct PeekArgumentNotALocal { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(mir_dataflow_peek_argument_untracked)] +#[diag("rustc_peek: argument untracked")] pub(crate) struct PeekArgumentUntracked { #[primary_span] pub span: Span, diff --git a/compiler/rustc_mir_dataflow/src/lib.rs b/compiler/rustc_mir_dataflow/src/lib.rs index 485925e7b50c..5f1cf1250152 100644 --- a/compiler/rustc_mir_dataflow/src/lib.rs +++ b/compiler/rustc_mir_dataflow/src/lib.rs @@ -34,8 +34,6 @@ pub mod rustc_peek; mod un_derefer; pub mod value_analysis; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub struct MoveDataTypingEnv<'tcx> { pub move_data: MoveData<'tcx>, pub typing_env: ty::TypingEnv<'tcx>, From 828bead9e33014793e32ba8d32eab270cc03a58d Mon Sep 17 00:00:00 2001 From: Jamie Hill-Daniel Date: Tue, 3 Feb 2026 14:16:06 +0000 Subject: [PATCH 307/319] Use glob imports for attribute parsers --- compiler/rustc_attr_parsing/src/context.rs | 118 ++++++--------------- 1 file changed, 35 insertions(+), 83 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 322e189e6d12..fa9f5b585926 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -16,90 +16,42 @@ use rustc_session::lint::{Lint, LintId}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use crate::AttributeParser; -use crate::attributes::allow_unstable::{ - AllowConstFnUnstableParser, AllowInternalUnstableParser, UnstableFeatureBoundParser, -}; -use crate::attributes::body::CoroutineParser; -use crate::attributes::cfi_encoding::CfiEncodingParser; -use crate::attributes::codegen_attrs::{ - ColdParser, CoverageParser, EiiForeignItemParser, ExportNameParser, ForceTargetFeatureParser, - NakedParser, NoMangleParser, ObjcClassParser, ObjcSelectorParser, OptimizeParser, - PatchableFunctionEntryParser, RustcPassIndirectlyInNonRusticAbisParser, SanitizeParser, - TargetFeatureParser, ThreadLocalParser, TrackCallerParser, UsedParser, -}; -use crate::attributes::confusables::ConfusablesParser; -use crate::attributes::crate_level::{ - CrateNameParser, CrateTypeParser, MoveSizeLimitParser, NeedsPanicRuntimeParser, - NoBuiltinsParser, NoCoreParser, NoMainParser, NoStdParser, PanicRuntimeParser, - PatternComplexityLimitParser, ProfilerRuntimeParser, RecursionLimitParser, - RustcCoherenceIsCoreParser, RustcPreserveUbChecksParser, TypeLengthLimitParser, - WindowsSubsystemParser, -}; -use crate::attributes::debugger::DebuggerViualizerParser; -use crate::attributes::deprecation::DeprecationParser; -use crate::attributes::do_not_recommend::DoNotRecommendParser; -use crate::attributes::doc::DocParser; -use crate::attributes::dummy::DummyParser; -use crate::attributes::inline::{InlineParser, RustcForceInlineParser}; -use crate::attributes::instruction_set::InstructionSetParser; -use crate::attributes::link_attrs::{ - CompilerBuiltinsParser, ExportStableParser, FfiConstParser, FfiPureParser, LinkNameParser, - LinkOrdinalParser, LinkParser, LinkSectionParser, LinkageParser, NeedsAllocatorParser, - StdInternalSymbolParser, -}; -use crate::attributes::lint_helpers::{ - AsPtrParser, AutomaticallyDerivedParser, PassByValueParser, PubTransparentParser, - RustcShouldNotBeCalledOnConstItems, -}; -use crate::attributes::loop_match::{ConstContinueParser, LoopMatchParser}; -use crate::attributes::macro_attrs::{ - AllowInternalUnsafeParser, CollapseDebugInfoParser, MacroEscapeParser, MacroExportParser, - MacroUseParser, -}; -use crate::attributes::must_not_suspend::MustNotSuspendParser; -use crate::attributes::must_use::MustUseParser; -use crate::attributes::no_implicit_prelude::NoImplicitPreludeParser; -use crate::attributes::no_link::NoLinkParser; -use crate::attributes::non_exhaustive::NonExhaustiveParser; +// Glob imports to avoid big, bitrotty import lists +use crate::attributes::allow_unstable::*; +use crate::attributes::body::*; +use crate::attributes::cfi_encoding::*; +use crate::attributes::codegen_attrs::*; +use crate::attributes::confusables::*; +use crate::attributes::crate_level::*; +use crate::attributes::debugger::*; +use crate::attributes::deprecation::*; +use crate::attributes::do_not_recommend::*; +use crate::attributes::doc::*; +use crate::attributes::dummy::*; +use crate::attributes::inline::*; +use crate::attributes::instruction_set::*; +use crate::attributes::link_attrs::*; +use crate::attributes::lint_helpers::*; +use crate::attributes::loop_match::*; +use crate::attributes::macro_attrs::*; +use crate::attributes::must_not_suspend::*; +use crate::attributes::must_use::*; +use crate::attributes::no_implicit_prelude::*; +use crate::attributes::no_link::*; +use crate::attributes::non_exhaustive::*; use crate::attributes::path::PathParser as PathAttributeParser; -use crate::attributes::pin_v2::PinV2Parser; -use crate::attributes::proc_macro_attrs::{ - ProcMacroAttributeParser, ProcMacroDeriveParser, ProcMacroParser, RustcBuiltinMacroParser, -}; -use crate::attributes::prototype::CustomMirParser; -use crate::attributes::repr::{AlignParser, AlignStaticParser, ReprParser}; -use crate::attributes::rustc_allocator::{ - RustcAllocatorParser, RustcAllocatorZeroedParser, RustcAllocatorZeroedVariantParser, - RustcDeallocatorParser, RustcReallocatorParser, -}; -use crate::attributes::rustc_dump::{ - RustcDumpDefParentsParser, RustcDumpItemBoundsParser, RustcDumpPredicatesParser, - RustcDumpUserArgsParser, RustcDumpVtableParser, -}; -use crate::attributes::rustc_internal::{ - RustcHasIncoherentInherentImplsParser, RustcHiddenTypeOfOpaquesParser, RustcLayoutParser, - RustcLayoutScalarValidRangeEndParser, RustcLayoutScalarValidRangeStartParser, - RustcLegacyConstGenericsParser, RustcLintOptDenyFieldAccessParser, RustcLintOptTyParser, - RustcLintQueryInstabilityParser, RustcLintUntrackedQueryInformationParser, RustcMainParser, - RustcMirParser, RustcMustImplementOneOfParser, RustcNeverReturnsNullPointerParser, - RustcNoImplicitAutorefsParser, RustcNonConstTraitMethodParser, RustcNounwindParser, - RustcObjectLifetimeDefaultParser, RustcOffloadKernelParser, RustcScalableVectorParser, - RustcSimdMonomorphizeLaneLimitParser, -}; -use crate::attributes::semantics::MayDangleParser; -use crate::attributes::stability::{ - BodyStabilityParser, ConstStabilityIndirectParser, ConstStabilityParser, StabilityParser, -}; -use crate::attributes::test_attrs::{ - IgnoreParser, RustcVarianceOfOpaquesParser, RustcVarianceParser, ShouldPanicParser, -}; -use crate::attributes::traits::{ - AllowIncoherentImplParser, CoinductiveParser, DenyExplicitImplParser, - DynIncompatibleTraitParser, FundamentalParser, MarkerParser, ParenSugarParser, PointeeParser, - SkipDuringMethodDispatchParser, SpecializationTraitParser, TypeConstParser, - UnsafeSpecializationMarkerParser, -}; -use crate::attributes::transparency::TransparencyParser; +use crate::attributes::pin_v2::*; +use crate::attributes::proc_macro_attrs::*; +use crate::attributes::prototype::*; +use crate::attributes::repr::*; +use crate::attributes::rustc_allocator::*; +use crate::attributes::rustc_dump::*; +use crate::attributes::rustc_internal::*; +use crate::attributes::semantics::*; +use crate::attributes::stability::*; +use crate::attributes::test_attrs::*; +use crate::attributes::traits::*; +use crate::attributes::transparency::*; use crate::attributes::{AttributeParser as _, Combine, Single, WithoutArgs}; use crate::parser::{ArgParser, RefPathParser}; use crate::session_diagnostics::{ From 07e4a994d61dccda69135a7b50ce69f6d93deec8 Mon Sep 17 00:00:00 2001 From: Usman Akinyemi Date: Thu, 29 Jan 2026 14:07:41 +0530 Subject: [PATCH 308/319] citool: report debuginfo test statistics Extend CI postprocessing to aggregate pass/fail/ignored counts for debuginfo tests. Tests are identified via their reported suite names (e.g. debuginfo-gdb, debuginfo-lldb or debuginfo-cdb). Signed-off-by: Usman Akinyemi --- src/build_helper/src/metrics.rs | 23 +++++++++++++++++++++++ src/ci/citool/src/analysis.rs | 30 +++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/build_helper/src/metrics.rs b/src/build_helper/src/metrics.rs index 07157e364158..98dd952033e5 100644 --- a/src/build_helper/src/metrics.rs +++ b/src/build_helper/src/metrics.rs @@ -111,6 +111,29 @@ pub struct JsonStepSystemStats { pub cpu_utilization_percent: f64, } +#[derive(Eq, Hash, PartialEq, Debug)] +pub enum DebuggerKind { + Gdb, + Lldb, + Cdb, +} + +impl DebuggerKind { + pub fn debuginfo_kind(name: &str) -> Option { + let name = name.to_ascii_lowercase(); + + if name.contains("debuginfo-gdb") { + Some(DebuggerKind::Gdb) + } else if name.contains("debuginfo-lldb") { + Some(DebuggerKind::Lldb) + } else if name.contains("debuginfo-cdb") { + Some(DebuggerKind::Cdb) + } else { + None + } + } +} + fn null_as_f64_nan<'de, D: serde::Deserializer<'de>>(d: D) -> Result { use serde::Deserialize as _; Option::::deserialize(d).map(|f| f.unwrap_or(f64::NAN)) diff --git a/src/ci/citool/src/analysis.rs b/src/ci/citool/src/analysis.rs index 39b115154f9f..ec65d9dcb528 100644 --- a/src/ci/citool/src/analysis.rs +++ b/src/ci/citool/src/analysis.rs @@ -3,7 +3,7 @@ use std::fmt::Debug; use std::time::Duration; use build_helper::metrics::{ - BuildStep, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name, + BuildStep, DebuggerKind, JsonRoot, TestOutcome, TestSuite, TestSuiteMetadata, escape_step_name, format_build_steps, }; @@ -139,11 +139,39 @@ fn record_test_suites(metrics: &JsonRoot) { let table = render_table(aggregated); println!("\n# Test results\n"); println!("{table}"); + report_debuginfo_statistics(&suites); } else { eprintln!("No test suites found in metrics"); } } +fn report_debuginfo_statistics(suites: &[&TestSuite]) { + let mut debugger_test_record: HashMap = HashMap::new(); + for suite in suites { + if let TestSuiteMetadata::Compiletest { .. } = suite.metadata { + for test in &suite.tests { + if let Some(kind) = DebuggerKind::debuginfo_kind(&test.name) { + let record = + debugger_test_record.entry(kind).or_insert(TestSuiteRecord::default()); + match test.outcome { + TestOutcome::Passed => record.passed += 1, + TestOutcome::Ignored { .. } => record.ignored += 1, + TestOutcome::Failed => record.failed += 1, + } + } + } + } + } + + println!("## DebugInfo Test Statistics"); + for (kind, record) in debugger_test_record { + println!( + "- {:?}: Passed ✅={}, Failed ❌={}, Ignored 🚫={}", + kind, record.passed, record.failed, record.ignored + ); + } +} + fn render_table(suites: BTreeMap) -> String { use std::fmt::Write; From dcdffe8d808b84f2208c0dcb817bc46b0242e34a Mon Sep 17 00:00:00 2001 From: cezarbbb Date: Mon, 12 Jan 2026 14:48:05 +0800 Subject: [PATCH 309/319] link modifier `export-symbols`: export all global symbols from selected uptream c static libraries --- .../src/attributes/link_attrs.rs | 21 +++- .../src/session_diagnostics.rs | 9 +- compiler/rustc_codegen_ssa/src/back/link.rs | 95 +++++++++++++++++-- .../rustc_hir/src/attrs/data_structures.rs | 6 +- compiler/rustc_interface/src/tests.rs | 18 ++-- compiler/rustc_metadata/src/native_libs.rs | 2 +- .../rustc_session/src/config/native_libs.rs | 15 ++- compiler/rustc_span/src/symbol.rs | 1 + .../cdylib-export-c-library-symbols/foo.c | 1 + .../cdylib-export-c-library-symbols/foo.rs | 10 ++ .../foo_export.rs | 10 ++ .../cdylib-export-c-library-symbols/rmake.rs | 36 +++++++ ...ure-gate-link-arg-attribute.in_flag.stderr | 2 +- tests/ui/link-native-libs/link-arg-error2.rs | 5 + .../link-native-libs/link-arg-error2.stderr | 2 + .../ui/link-native-libs/link-arg-from-rs2.rs | 7 ++ .../link-native-libs/link-arg-from-rs2.stderr | 8 ++ .../link-attr-validation-late.stderr | 6 +- .../modifiers-bad.blank.stderr | 2 +- .../modifiers-bad.no-prefix.stderr | 2 +- .../modifiers-bad.prefix-only.stderr | 2 +- .../modifiers-bad.unknown.stderr | 2 +- 22 files changed, 226 insertions(+), 36 deletions(-) create mode 100644 tests/run-make/cdylib-export-c-library-symbols/foo.c create mode 100644 tests/run-make/cdylib-export-c-library-symbols/foo.rs create mode 100644 tests/run-make/cdylib-export-c-library-symbols/foo_export.rs create mode 100644 tests/run-make/cdylib-export-c-library-symbols/rmake.rs create mode 100644 tests/ui/link-native-libs/link-arg-error2.rs create mode 100644 tests/ui/link-native-libs/link-arg-error2.stderr create mode 100644 tests/ui/link-native-libs/link-arg-from-rs2.rs create mode 100644 tests/ui/link-native-libs/link-arg-from-rs2.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs index c9da2f3b14bf..6c52be9bd229 100644 --- a/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs +++ b/compiler/rustc_attr_parsing/src/attributes/link_attrs.rs @@ -12,10 +12,10 @@ use super::prelude::*; use super::util::parse_single_integer; use crate::attributes::cfg::parse_cfg_entry; use crate::session_diagnostics::{ - AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ImportNameTypeRaw, ImportNameTypeX86, - IncompatibleWasmLink, InvalidLinkModifier, LinkFrameworkApple, LinkOrdinalOutOfRange, - LinkRequiresName, MultipleModifiers, NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, - WholeArchiveNeedsStatic, + AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, ExportSymbolsNeedsStatic, + ImportNameTypeRaw, ImportNameTypeX86, IncompatibleWasmLink, InvalidLinkModifier, + LinkFrameworkApple, LinkOrdinalOutOfRange, LinkRequiresName, MultipleModifiers, + NullOnLinkSection, RawDylibNoNul, RawDylibOnlyWindows, WholeArchiveNeedsStatic, }; pub(crate) struct LinkNameParser; @@ -165,6 +165,14 @@ impl CombineAttributeParser for LinkParser { cx.emit_err(BundleNeedsStatic { span }); } + (sym::export_symbols, Some(NativeLibKind::Static { export_symbols, .. })) => { + assign_modifier(export_symbols) + } + + (sym::export_symbols, _) => { + cx.emit_err(ExportSymbolsNeedsStatic { span }); + } + (sym::verbatim, _) => assign_modifier(&mut verbatim), ( @@ -190,6 +198,7 @@ impl CombineAttributeParser for LinkParser { span, &[ sym::bundle, + sym::export_symbols, sym::verbatim, sym::whole_dash_archive, sym::as_dash_needed, @@ -285,7 +294,9 @@ impl LinkParser { }; let link_kind = match link_kind { - kw::Static => NativeLibKind::Static { bundle: None, whole_archive: None }, + kw::Static => { + NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None } + } sym::dylib => NativeLibKind::Dylib { as_needed: None }, sym::framework => { if !sess.target.is_like_darwin { diff --git a/compiler/rustc_attr_parsing/src/session_diagnostics.rs b/compiler/rustc_attr_parsing/src/session_diagnostics.rs index fb4d9c660a19..b0a334210f74 100644 --- a/compiler/rustc_attr_parsing/src/session_diagnostics.rs +++ b/compiler/rustc_attr_parsing/src/session_diagnostics.rs @@ -909,7 +909,7 @@ pub(crate) struct RawDylibOnlyWindows { #[derive(Diagnostic)] #[diag( - "invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed" + "invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols" )] pub(crate) struct InvalidLinkModifier { #[primary_span] @@ -938,6 +938,13 @@ pub(crate) struct BundleNeedsStatic { pub span: Span, } +#[derive(Diagnostic)] +#[diag("linking modifier `export-symbols` is only compatible with `static` linking kind")] +pub(crate) struct ExportSymbolsNeedsStatic { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("linking modifier `whole-archive` is only compatible with `static` linking kind")] pub(crate) struct WholeArchiveNeedsStatic { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index c8109db86e2f..5a732382c981 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -11,10 +11,11 @@ use std::{env, fmt, fs, io, mem, str}; use find_msvc_tools; use itertools::Itertools; +use object::{Object, ObjectSection, ObjectSymbol}; use regex::Regex; use rustc_arena::TypedArena; use rustc_attr_parsing::eval_config_entry; -use rustc_data_structures::fx::FxIndexSet; +use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; @@ -2185,6 +2186,71 @@ fn add_rpath_args( } } +fn add_c_staticlib_symbols( + sess: &Session, + lib: &NativeLib, + out: &mut Vec<(String, SymbolExportKind)>, +) -> io::Result<()> { + let file_path = find_native_static_library(lib.name.as_str(), lib.verbatim, sess); + + let archive_map = unsafe { Mmap::map(File::open(&file_path)?)? }; + + let archive = object::read::archive::ArchiveFile::parse(&*archive_map) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + for member in archive.members() { + let member = member.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + let data = member + .data(&*archive_map) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + // clang LTO: raw LLVM bitcode + if data.starts_with(b"BC\xc0\xde") { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "LLVM bitcode object in C static library (LTO not supported)", + )); + } + + let object = object::File::parse(&*data) + .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + + // gcc / clang ELF / Mach-O LTO + if object.sections().any(|s| { + s.name().map(|n| n.starts_with(".gnu.lto_") || n == ".llvm.lto").unwrap_or(false) + }) { + return Err(io::Error::new( + io::ErrorKind::InvalidData, + "LTO object in C static library is not supported", + )); + } + + for symbol in object.symbols() { + if symbol.scope() != object::SymbolScope::Dynamic { + continue; + } + + let name = match symbol.name() { + Ok(n) => n, + Err(_) => continue, + }; + + let export_kind = match symbol.kind() { + object::SymbolKind::Text => SymbolExportKind::Text, + object::SymbolKind::Data => SymbolExportKind::Data, + _ => continue, + }; + + // FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. + // Need to be resolved. + out.push((name.to_string(), export_kind)); + } + } + + Ok(()) +} + /// Produce the linker command line containing linker path and arguments. /// /// When comments in the function say "order-(in)dependent" they mean order-dependence between @@ -2217,6 +2283,25 @@ fn linker_with_args( ); let link_output_kind = link_output_kind(sess, crate_type); + let mut export_symbols = codegen_results.crate_info.exported_symbols[&crate_type].clone(); + + if crate_type == CrateType::Cdylib { + let mut seen = FxHashSet::default(); + + for lib in &codegen_results.crate_info.used_libraries { + if let NativeLibKind::Static { export_symbols: Some(true), .. } = lib.kind + && seen.insert((lib.name, lib.verbatim)) + { + if let Err(err) = add_c_staticlib_symbols(&sess, lib, &mut export_symbols) { + sess.dcx().fatal(format!( + "failed to process C static library `{}`: {}", + lib.name, err + )); + } + } + } + } + // ------------ Early order-dependent options ------------ // If we're building something like a dynamic library then some platforms @@ -2224,11 +2309,7 @@ fn linker_with_args( // dynamic library. // Must be passed before any libraries to prevent the symbols to export from being thrown away, // at least on some platforms (e.g. windows-gnu). - cmd.export_symbols( - tmpdir, - crate_type, - &codegen_results.crate_info.exported_symbols[&crate_type], - ); + cmd.export_symbols(tmpdir, crate_type, &export_symbols); // Can be used for adding custom CRT objects or overriding order-dependent options above. // FIXME: In practice built-in target specs use this for arbitrary order-independent options, @@ -2678,7 +2759,7 @@ fn add_native_libs_from_crate( let name = lib.name.as_str(); let verbatim = lib.verbatim; match lib.kind { - NativeLibKind::Static { bundle, whole_archive } => { + NativeLibKind::Static { bundle, whole_archive, .. } => { if link_static { let bundle = bundle.unwrap_or(true); let whole_archive = whole_archive == Some(true); diff --git a/compiler/rustc_hir/src/attrs/data_structures.rs b/compiler/rustc_hir/src/attrs/data_structures.rs index 6138ffc8d954..a53eff4637ff 100644 --- a/compiler/rustc_hir/src/attrs/data_structures.rs +++ b/compiler/rustc_hir/src/attrs/data_structures.rs @@ -331,6 +331,8 @@ pub enum NativeLibKind { bundle: Option, /// Whether to link static library without throwing any object files away whole_archive: Option, + /// Whether to export c static library symbols + export_symbols: Option, }, /// Dynamic library (e.g. `libfoo.so` on Linux) /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC). @@ -363,8 +365,8 @@ pub enum NativeLibKind { impl NativeLibKind { pub fn has_modifiers(&self) -> bool { match self { - NativeLibKind::Static { bundle, whole_archive } => { - bundle.is_some() || whole_archive.is_some() + NativeLibKind::Static { bundle, whole_archive, export_symbols } => { + bundle.is_some() || whole_archive.is_some() || export_symbols.is_some() } NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 0f60e86e0ca3..b0272d726bc3 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -379,7 +379,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -401,7 +401,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -423,13 +423,13 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { name: String::from("b"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -445,7 +445,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -467,7 +467,7 @@ fn test_native_libs_tracking_hash_different_values() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -501,7 +501,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -528,7 +528,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { @@ -549,7 +549,7 @@ fn test_native_libs_tracking_hash_different_order() { NativeLib { name: String::from("a"), new_name: None, - kind: NativeLibKind::Static { bundle: None, whole_archive: None }, + kind: NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None }, verbatim: None, }, NativeLib { diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs index b160b3fe9bb3..0c06d1be9a3f 100644 --- a/compiler/rustc_metadata/src/native_libs.rs +++ b/compiler/rustc_metadata/src/native_libs.rs @@ -161,7 +161,7 @@ fn find_bundled_library( tcx: TyCtxt<'_>, ) -> Option { let sess = tcx.sess; - if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive } = kind + if let NativeLibKind::Static { bundle: Some(true) | None, whole_archive, .. } = kind && tcx.crate_types().iter().any(|t| matches!(t, &CrateType::Rlib | CrateType::StaticLib)) && (sess.opts.unstable_opts.packed_bundled_libs || has_cfg || whole_archive == Some(true)) { diff --git a/compiler/rustc_session/src/config/native_libs.rs b/compiler/rustc_session/src/config/native_libs.rs index 71d3e222c8a1..28e2d0f94104 100644 --- a/compiler/rustc_session/src/config/native_libs.rs +++ b/compiler/rustc_session/src/config/native_libs.rs @@ -53,7 +53,9 @@ fn parse_native_lib(cx: &ParseNativeLibCx<'_>, value: &str) -> NativeLib { let NativeLibParts { kind, modifiers, name, new_name } = split_native_lib_value(value); let kind = kind.map_or(NativeLibKind::Unspecified, |kind| match kind { - "static" => NativeLibKind::Static { bundle: None, whole_archive: None }, + "static" => { + NativeLibKind::Static { bundle: None, whole_archive: None, export_symbols: None } + } "dylib" => NativeLibKind::Dylib { as_needed: None }, "framework" => NativeLibKind::Framework { as_needed: None }, "link-arg" => { @@ -105,7 +107,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li Some(("-", m)) => (m, false), _ => cx.early_dcx.early_fatal( "invalid linking modifier syntax, expected '+' or '-' prefix \ - before one of: bundle, verbatim, whole-archive, as-needed", + before one of: bundle, verbatim, whole-archive, as-needed, export-symbols", ), }; @@ -125,6 +127,13 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li ("bundle", _) => early_dcx .early_fatal("linking modifier `bundle` is only compatible with `static` linking kind"), + ("export-symbols", NativeLibKind::Static { export_symbols, .. }) => { + assign_modifier(export_symbols) + } + ("export-symbols", _) => early_dcx.early_fatal( + "linking modifier `export-symbols` is only compatible with `static` linking kind", + ), + ("verbatim", _) => assign_modifier(&mut native_lib.verbatim), ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => { @@ -151,7 +160,7 @@ fn parse_and_apply_modifier(cx: &ParseNativeLibCx<'_>, modifier: &str, native_li _ => early_dcx.early_fatal(format!( "unknown linking modifier `{modifier}`, expected one \ - of: bundle, verbatim, whole-archive, as-needed" + of: bundle, verbatim, whole-archive, as-needed, export-symbols" )), } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index b0ef95d10ffa..c88dd0948b2a 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1002,6 +1002,7 @@ symbols! { explicit_tail_calls, export_name, export_stable, + export_symbols: "export-symbols", expr, expr_2021, expr_fragment_specifier_2024, diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo.c b/tests/run-make/cdylib-export-c-library-symbols/foo.c new file mode 100644 index 000000000000..a062aca03b31 --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo.c @@ -0,0 +1 @@ +void my_function() {} diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo.rs b/tests/run-make/cdylib-export-c-library-symbols/foo.rs new file mode 100644 index 000000000000..ac641aaed00f --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo.rs @@ -0,0 +1,10 @@ +extern "C" { + pub fn my_function(); +} + +#[no_mangle] +pub extern "C" fn rust_entry() { + unsafe { + my_function(); + } +} diff --git a/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs b/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs new file mode 100644 index 000000000000..1eda294ef41c --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/foo_export.rs @@ -0,0 +1,10 @@ +extern "C" { + fn my_function(); +} + +#[no_mangle] +pub extern "C" fn rust_entry() { + unsafe { + my_function(); + } +} diff --git a/tests/run-make/cdylib-export-c-library-symbols/rmake.rs b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs new file mode 100644 index 000000000000..cb237eceedad --- /dev/null +++ b/tests/run-make/cdylib-export-c-library-symbols/rmake.rs @@ -0,0 +1,36 @@ +//@ ignore-nvptx64 +//@ ignore-wasm +//@ ignore-cross-compile +// FIXME:The symbol mangle rules are slightly different in Windows(32-bit) and Apple. +// Need to be resolved. +//@ ignore-windows +//@ ignore-apple +// Reason: the compiled binary is executed + +use run_make_support::{build_native_static_lib, cc, dynamic_lib_name, is_darwin, llvm_nm, rustc}; + +fn main() { + cc().input("foo.c").arg("-c").out_exe("foo.o").run(); + build_native_static_lib("foo"); + + rustc().input("foo.rs").arg("-lstatic=foo").crate_type("cdylib").run(); + + let out = llvm_nm() + .input(dynamic_lib_name("foo")) + .run() + .assert_stdout_not_contains_regex("T *my_function"); + + rustc().input("foo_export.rs").arg("-lstatic:+export-symbols=foo").crate_type("cdylib").run(); + + if is_darwin() { + let out = llvm_nm() + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("T _my_function"); + } else { + let out = llvm_nm() + .input(dynamic_lib_name("foo_export")) + .run() + .assert_stdout_contains("T my_function"); + } +} diff --git a/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr b/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr index 4d65db3c66d0..218a4769d954 100644 --- a/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr +++ b/tests/ui/feature-gates/feature-gate-link-arg-attribute.in_flag.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier `link-arg`, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier `link-arg`, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/link-arg-error2.rs b/tests/ui/link-native-libs/link-arg-error2.rs new file mode 100644 index 000000000000..a51dec0614b5 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-error2.rs @@ -0,0 +1,5 @@ +//@ compile-flags: -l link-arg:+export-symbols=arg -Z unstable-options + +fn main() {} + +//~? ERROR linking modifier `export-symbols` is only compatible with `static` linking kind diff --git a/tests/ui/link-native-libs/link-arg-error2.stderr b/tests/ui/link-native-libs/link-arg-error2.stderr new file mode 100644 index 000000000000..61bcf7dba282 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-error2.stderr @@ -0,0 +1,2 @@ +error: linking modifier `export-symbols` is only compatible with `static` linking kind + diff --git a/tests/ui/link-native-libs/link-arg-from-rs2.rs b/tests/ui/link-native-libs/link-arg-from-rs2.rs new file mode 100644 index 000000000000..3074fec6c1c8 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-from-rs2.rs @@ -0,0 +1,7 @@ +#![feature(link_arg_attribute)] + +#[link(kind = "link-arg", name = "arg", modifiers = "+export-symbols")] +//~^ ERROR linking modifier `export-symbols` is only compatible with `static` linking kind +extern "C" {} + +pub fn main() {} diff --git a/tests/ui/link-native-libs/link-arg-from-rs2.stderr b/tests/ui/link-native-libs/link-arg-from-rs2.stderr new file mode 100644 index 000000000000..af3b25253e05 --- /dev/null +++ b/tests/ui/link-native-libs/link-arg-from-rs2.stderr @@ -0,0 +1,8 @@ +error: linking modifier `export-symbols` is only compatible with `static` linking kind + --> $DIR/link-arg-from-rs2.rs:3:53 + | +LL | #[link(kind = "link-arg", name = "arg", modifiers = "+export-symbols")] + | ^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/link-native-libs/link-attr-validation-late.stderr b/tests/ui/link-native-libs/link-attr-validation-late.stderr index b09431f923aa..4a4a19375207 100644 --- a/tests/ui/link-native-libs/link-attr-validation-late.stderr +++ b/tests/ui/link-native-libs/link-attr-validation-late.stderr @@ -178,13 +178,13 @@ LL | #[link(name = "...", wasm_import_module())] | = note: for more information, visit -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols --> $DIR/link-attr-validation-late.rs:31:34 | LL | #[link(name = "...", modifiers = "")] | ^^ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols --> $DIR/link-attr-validation-late.rs:32:34 | LL | #[link(name = "...", modifiers = "no-plus-minus")] @@ -196,7 +196,7 @@ error[E0539]: malformed `link` attribute input LL | #[link(name = "...", modifiers = "+unknown")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^----------^^ | | - | valid arguments are "bundle", "verbatim", "whole-archive" or "as-needed" + | valid arguments are "bundle", "export-symbols", "verbatim", "whole-archive" or "as-needed" | = note: for more information, visit diff --git a/tests/ui/link-native-libs/modifiers-bad.blank.stderr b/tests/ui/link-native-libs/modifiers-bad.blank.stderr index ea36af0b4cfa..6a1953e008ee 100644 --- a/tests/ui/link-native-libs/modifiers-bad.blank.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.blank.stderr @@ -1,2 +1,2 @@ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr b/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr index ea36af0b4cfa..6a1953e008ee 100644 --- a/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.no-prefix.stderr @@ -1,2 +1,2 @@ -error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed +error: invalid linking modifier syntax, expected '+' or '-' prefix before one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr b/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr index 1e701374688f..46720cf0b15e 100644 --- a/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.prefix-only.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier ``, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier ``, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols diff --git a/tests/ui/link-native-libs/modifiers-bad.unknown.stderr b/tests/ui/link-native-libs/modifiers-bad.unknown.stderr index 75950ad9c64c..d47694a5aeca 100644 --- a/tests/ui/link-native-libs/modifiers-bad.unknown.stderr +++ b/tests/ui/link-native-libs/modifiers-bad.unknown.stderr @@ -1,2 +1,2 @@ -error: unknown linking modifier `ferris`, expected one of: bundle, verbatim, whole-archive, as-needed +error: unknown linking modifier `ferris`, expected one of: bundle, verbatim, whole-archive, as-needed, export-symbols From ac160bba1244aa1309cfa60cf0abd3c1a0aa3668 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Tue, 3 Feb 2026 21:19:37 +0800 Subject: [PATCH 310/319] ci: Optimize loongarch64-linux dist builders Tune the build configuration for loongarch64-linux targets to speed up rustc. Changes include: - Enable jemalloc and rust thin-lto. - Set codegen-units=1 and disable debug assertions. These changes reduce rustc-perf compile time by ~17%. --- src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile | 5 ++++- src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile index 4b86ed32fd55..96acc5e97e94 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-linux/Dockerfile @@ -50,6 +50,9 @@ ENV RUST_CONFIGURE_ARGS \ --enable-full-tools \ --enable-profiler \ --enable-sanitizers \ - --disable-docs + --disable-docs \ + --set rust.jemalloc \ + --set rust.lto=thin \ + --set rust.codegen-units=1 ENV SCRIPT python3 ../x.py dist --host $HOSTS --target $TARGETS diff --git a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile index 763b29ae1c5e..44efc1089017 100644 --- a/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-loongarch64-musl/Dockerfile @@ -33,6 +33,9 @@ ENV RUST_CONFIGURE_ARGS \ --enable-profiler \ --enable-sanitizers \ --disable-docs \ + --set rust.jemalloc \ + --set rust.lto=thin \ + --set rust.codegen-units=1 \ --set target.loongarch64-unknown-linux-musl.crt-static=false \ --musl-root-loongarch64=/x-tools/loongarch64-unknown-linux-musl/loongarch64-unknown-linux-musl/sysroot/usr From 8e62b1d0e119e498ad51f44475b376b20d60fea8 Mon Sep 17 00:00:00 2001 From: yukang Date: Wed, 4 Feb 2026 03:55:50 +0000 Subject: [PATCH 311/319] Fix set_times_nofollow for directory on windows --- library/std/src/fs/tests.rs | 51 +++++++++++++++++++++++++++++++ library/std/src/sys/fs/windows.rs | 4 +-- 2 files changed, 53 insertions(+), 2 deletions(-) diff --git a/library/std/src/fs/tests.rs b/library/std/src/fs/tests.rs index 6d0b261dcadc..42f3ccc340b2 100644 --- a/library/std/src/fs/tests.rs +++ b/library/std/src/fs/tests.rs @@ -2301,6 +2301,57 @@ fn test_fs_set_times() { } } +#[test] +fn test_fs_set_times_on_dir() { + #[cfg(target_vendor = "apple")] + use crate::os::darwin::fs::FileTimesExt; + #[cfg(windows)] + use crate::os::windows::fs::FileTimesExt; + + let tmp = tmpdir(); + let dir_path = tmp.join("testdir"); + fs::create_dir(&dir_path).unwrap(); + + let mut times = FileTimes::new(); + let accessed = SystemTime::UNIX_EPOCH + Duration::from_secs(12345); + let modified = SystemTime::UNIX_EPOCH + Duration::from_secs(54321); + times = times.set_accessed(accessed).set_modified(modified); + + #[cfg(any(windows, target_vendor = "apple"))] + let created = SystemTime::UNIX_EPOCH + Duration::from_secs(32123); + #[cfg(any(windows, target_vendor = "apple"))] + { + times = times.set_created(created); + } + + match fs::set_times(&dir_path, times) { + // Allow unsupported errors on platforms which don't support setting times. + #[cfg(not(any( + windows, + all( + unix, + not(any( + target_os = "android", + target_os = "redox", + target_os = "espidf", + target_os = "horizon" + )) + ) + )))] + Err(e) if e.kind() == ErrorKind::Unsupported => return, + Err(e) => panic!("error setting directory times: {e:?}"), + Ok(_) => {} + } + + let metadata = fs::metadata(&dir_path).unwrap(); + assert_eq!(metadata.accessed().unwrap(), accessed); + assert_eq!(metadata.modified().unwrap(), modified); + #[cfg(any(windows, target_vendor = "apple"))] + { + assert_eq!(metadata.created().unwrap(), created); + } +} + #[test] fn test_fs_set_times_follows_symlink() { #[cfg(target_vendor = "apple")] diff --git a/library/std/src/sys/fs/windows.rs b/library/std/src/sys/fs/windows.rs index fc8aec2f3f7c..74854cdeb498 100644 --- a/library/std/src/sys/fs/windows.rs +++ b/library/std/src/sys/fs/windows.rs @@ -1556,7 +1556,7 @@ pub fn set_perm(p: &WCStr, perm: FilePermissions) -> io::Result<()> { pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS); let file = File::open_native(p, &opts)?; file.set_times(times) @@ -1564,7 +1564,7 @@ pub fn set_times(p: &WCStr, times: FileTimes) -> io::Result<()> { pub fn set_times_nofollow(p: &WCStr, times: FileTimes) -> io::Result<()> { let mut opts = OpenOptions::new(); - opts.write(true); + opts.access_mode(c::FILE_WRITE_ATTRIBUTES); // `FILE_FLAG_OPEN_REPARSE_POINT` for no_follow behavior opts.custom_flags(c::FILE_FLAG_BACKUP_SEMANTICS | c::FILE_FLAG_OPEN_REPARSE_POINT); let file = File::open_native(p, &opts)?; From c77779e5294722c372ee834306118810e930f96e Mon Sep 17 00:00:00 2001 From: Daniel Scherzer Date: Tue, 3 Feb 2026 22:15:36 -0800 Subject: [PATCH 312/319] rustbook/README.md: add missing `)` To match the opening `(` before the reference to PR 127786 --- src/tools/rustbook/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/rustbook/README.md b/src/tools/rustbook/README.md index d9570c23ead1..a9fd1b75b572 100644 --- a/src/tools/rustbook/README.md +++ b/src/tools/rustbook/README.md @@ -10,7 +10,7 @@ This is invoked automatically when building mdbook-style documentation, for exam ## Cargo workspace -This package defines a separate cargo workspace from the main Rust workspace for a few reasons (ref [#127786](https://github.com/rust-lang/rust/pull/127786): +This package defines a separate cargo workspace from the main Rust workspace for a few reasons (ref [#127786](https://github.com/rust-lang/rust/pull/127786)): - Avoids requiring checking out submodules for developers who are not working on the documentation. Otherwise, some submodules such as those that have custom preprocessors would be required for cargo to find the dependencies. - Avoids problems with updating dependencies. Unfortunately this workspace has a rather large set of dependencies, which can make coordinating updates difficult (see [#127890](https://github.com/rust-lang/rust/issues/127890)). From e7c142cc895f4a56e71d5e477bcb562b47cd1d16 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 15:22:54 +0100 Subject: [PATCH 313/319] Convert to inline diagnostics in `rustc_interface` --- Cargo.lock | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_interface/Cargo.toml | 1 - compiler/rustc_interface/messages.ftl | 56 -------------------- compiler/rustc_interface/src/errors.rs | 62 ++++++++++++++--------- compiler/rustc_interface/src/interface.rs | 12 +---- compiler/rustc_interface/src/lib.rs | 2 - 7 files changed, 41 insertions(+), 94 deletions(-) delete mode 100644 compiler/rustc_interface/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 4cbc98beae11..1631d5362612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4114,7 +4114,6 @@ dependencies = [ "rustc_errors", "rustc_expand", "rustc_feature", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hir", "rustc_hir_analysis", diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 0ab71ad9981b..3059a4fefc61 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -124,7 +124,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_expand::DEFAULT_LOCALE_RESOURCE, rustc_hir_analysis::DEFAULT_LOCALE_RESOURCE, rustc_hir_typeck::DEFAULT_LOCALE_RESOURCE, - rustc_interface::DEFAULT_LOCALE_RESOURCE, rustc_lint::DEFAULT_LOCALE_RESOURCE, rustc_metadata::DEFAULT_LOCALE_RESOURCE, rustc_middle::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_interface/Cargo.toml b/compiler/rustc_interface/Cargo.toml index d785030b5f2c..3a3b4d59db86 100644 --- a/compiler/rustc_interface/Cargo.toml +++ b/compiler/rustc_interface/Cargo.toml @@ -19,7 +19,6 @@ rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_expand = { path = "../rustc_expand" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hir = { path = "../rustc_hir" } rustc_hir_analysis = { path = "../rustc_hir_analysis" } diff --git a/compiler/rustc_interface/messages.ftl b/compiler/rustc_interface/messages.ftl deleted file mode 100644 index d898e9bf7d1e..000000000000 --- a/compiler/rustc_interface/messages.ftl +++ /dev/null @@ -1,56 +0,0 @@ -interface_abi_required_feature = - target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly - .note = this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -interface_abi_required_feature_issue = for more information, see issue #116344 - -interface_crate_name_does_not_match = `--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}` - -interface_crate_name_invalid = crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen - -interface_emoji_identifier = - identifiers cannot contain emoji: `{$ident}` - -interface_error_writing_dependencies = - error writing dependencies to `{$path}`: {$error} - -interface_failed_writing_file = - failed to write file {$path}: {$error}" - -interface_ferris_identifier = - Ferris cannot be used as an identifier - .suggestion = try using their name instead - -interface_generated_file_conflicts_with_directory = - the generated executable for the input file "{$input_path}" conflicts with the existing directory "{$dir_path}" - -interface_ignoring_extra_filename = ignoring -C extra-filename flag due to -o flag - -interface_ignoring_out_dir = ignoring --out-dir flag due to -o flag - -interface_input_file_would_be_overwritten = - the input file "{$path}" would be overwritten by the generated executable - -interface_mixed_bin_crate = - cannot mix `bin` crate type with others - -interface_mixed_proc_macro_crate = - cannot mix `proc-macro` crate type with others - -interface_multiple_output_types_adaption = - due to multiple output types requested, the explicitly specified output file name will be adapted for each output type - -interface_multiple_output_types_to_stdout = can't use option `-o` or `--emit` to write multiple output types to stdout -interface_out_dir_error = - failed to find or create the directory specified by `--out-dir` - -interface_proc_macro_crate_panic_abort = - building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic - -interface_temps_dir_error = - failed to find or create the directory specified by `--temps-dir` - -interface_unsupported_crate_type_for_codegen_backend = - dropping unsupported crate type `{$crate_type}` for codegen backend `{$codegen_backend}` - -interface_unsupported_crate_type_for_target = - dropping unsupported crate type `{$crate_type}` for target `{$target_triple}` diff --git a/compiler/rustc_interface/src/errors.rs b/compiler/rustc_interface/src/errors.rs index aee8ec20e14d..7e4671889f57 100644 --- a/compiler/rustc_interface/src/errors.rs +++ b/compiler/rustc_interface/src/errors.rs @@ -7,7 +7,9 @@ use rustc_span::{Span, Symbol}; use rustc_target::spec::TargetTuple; #[derive(Diagnostic)] -#[diag(interface_crate_name_does_not_match)] +#[diag( + "`--crate-name` and `#[crate_name]` are required to match, but `{$crate_name}` != `{$attr_crate_name}`" +)] pub(crate) struct CrateNameDoesNotMatch { #[primary_span] pub(crate) span: Span, @@ -16,23 +18,27 @@ pub(crate) struct CrateNameDoesNotMatch { } #[derive(Diagnostic)] -#[diag(interface_crate_name_invalid)] +#[diag("crate names cannot start with a `-`, but `{$crate_name}` has a leading hyphen")] pub(crate) struct CrateNameInvalid<'a> { pub(crate) crate_name: &'a str, } #[derive(Diagnostic)] -#[diag(interface_ferris_identifier)] +#[diag("Ferris cannot be used as an identifier")] pub struct FerrisIdentifier { #[primary_span] pub spans: Vec, - #[suggestion(code = "{ferris_fix}", applicability = "maybe-incorrect")] + #[suggestion( + "try using their name instead", + code = "{ferris_fix}", + applicability = "maybe-incorrect" + )] pub first_span: Span, pub ferris_fix: &'static str, } #[derive(Diagnostic)] -#[diag(interface_emoji_identifier)] +#[diag("identifiers cannot contain emoji: `{$ident}`")] pub struct EmojiIdentifier { #[primary_span] pub spans: Vec, @@ -40,86 +46,96 @@ pub struct EmojiIdentifier { } #[derive(Diagnostic)] -#[diag(interface_mixed_bin_crate)] +#[diag("cannot mix `bin` crate type with others")] pub struct MixedBinCrate; #[derive(Diagnostic)] -#[diag(interface_mixed_proc_macro_crate)] +#[diag("cannot mix `proc-macro` crate type with others")] pub struct MixedProcMacroCrate; #[derive(Diagnostic)] -#[diag(interface_error_writing_dependencies)] +#[diag("error writing dependencies to `{$path}`: {$error}")] pub struct ErrorWritingDependencies<'a> { pub path: &'a Path, pub error: io::Error, } #[derive(Diagnostic)] -#[diag(interface_input_file_would_be_overwritten)] +#[diag("the input file \"{$path}\" would be overwritten by the generated executable")] pub struct InputFileWouldBeOverWritten<'a> { pub path: &'a Path, } #[derive(Diagnostic)] -#[diag(interface_generated_file_conflicts_with_directory)] +#[diag( + "the generated executable for the input file \"{$input_path}\" conflicts with the existing directory \"{$dir_path}\"" +)] pub struct GeneratedFileConflictsWithDirectory<'a> { pub input_path: &'a Path, pub dir_path: &'a Path, } #[derive(Diagnostic)] -#[diag(interface_temps_dir_error)] +#[diag("failed to find or create the directory specified by `--temps-dir`")] pub struct TempsDirError; #[derive(Diagnostic)] -#[diag(interface_out_dir_error)] +#[diag("failed to find or create the directory specified by `--out-dir`")] pub struct OutDirError; #[derive(Diagnostic)] -#[diag(interface_failed_writing_file)] +#[diag("failed to write file {$path}: {$error}\"")] pub struct FailedWritingFile<'a> { pub path: &'a Path, pub error: io::Error, } #[derive(Diagnostic)] -#[diag(interface_proc_macro_crate_panic_abort)] +#[diag( + "building proc macro crate with `panic=abort` or `panic=immediate-abort` may crash the compiler should the proc-macro panic" +)] pub struct ProcMacroCratePanicAbort; #[derive(Diagnostic)] -#[diag(interface_multiple_output_types_adaption)] +#[diag( + "due to multiple output types requested, the explicitly specified output file name will be adapted for each output type" +)] pub struct MultipleOutputTypesAdaption; #[derive(Diagnostic)] -#[diag(interface_ignoring_extra_filename)] +#[diag("ignoring -C extra-filename flag due to -o flag")] pub struct IgnoringExtraFilename; #[derive(Diagnostic)] -#[diag(interface_ignoring_out_dir)] +#[diag("ignoring --out-dir flag due to -o flag")] pub struct IgnoringOutDir; #[derive(Diagnostic)] -#[diag(interface_multiple_output_types_to_stdout)] +#[diag("can't use option `-o` or `--emit` to write multiple output types to stdout")] pub struct MultipleOutputTypesToStdout; #[derive(Diagnostic)] -#[diag(interface_abi_required_feature)] -#[note] -#[note(interface_abi_required_feature_issue)] +#[diag( + "target feature `{$feature}` must be {$enabled} to ensure that the ABI of the current target can be implemented correctly" +)] +#[note( + "this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!" +)] +#[note("for more information, see issue #116344 ")] pub(crate) struct AbiRequiredTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, } #[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_codegen_backend)] +#[diag("dropping unsupported crate type `{$crate_type}` for codegen backend `{$codegen_backend}`")] pub(crate) struct UnsupportedCrateTypeForCodegenBackend { pub(crate) crate_type: CrateType, pub(crate) codegen_backend: &'static str, } #[derive(Diagnostic)] -#[diag(interface_unsupported_crate_type_for_target)] +#[diag("dropping unsupported crate type `{$crate_type}` for target `{$target_triple}`")] pub(crate) struct UnsupportedCrateTypeForTarget<'a> { pub(crate) crate_type: CrateType, pub(crate) target_triple: &'a TargetTuple, diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index f76e8d4632fc..916618cb5049 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -55,11 +55,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { let psess = ParseSess::emitter_with_note( - vec![ - crate::DEFAULT_LOCALE_RESOURCE, - rustc_parse::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, - ], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -131,11 +127,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch for s in specs { let psess = ParseSess::emitter_with_note( - vec![ - crate::DEFAULT_LOCALE_RESOURCE, - rustc_parse::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, - ], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--check-cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); diff --git a/compiler/rustc_interface/src/lib.rs b/compiler/rustc_interface/src/lib.rs index ce2398fab919..b5e4a384861f 100644 --- a/compiler/rustc_interface/src/lib.rs +++ b/compiler/rustc_interface/src/lib.rs @@ -21,5 +21,3 @@ pub use queries::Linker; #[cfg(test)] mod tests; - -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } From ae21d43933419d80a2471afbd26d6e288e6666a7 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 22:28:34 +0100 Subject: [PATCH 314/319] Convert to inline diagnostics in `rustc_privacy` --- Cargo.lock | 2 -- compiler/rustc_driver_impl/Cargo.toml | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_privacy/Cargo.toml | 1 - compiler/rustc_privacy/messages.ftl | 39 --------------------- compiler/rustc_privacy/src/errors.rs | 50 ++++++++++++++++++--------- compiler/rustc_privacy/src/lib.rs | 2 -- 7 files changed, 33 insertions(+), 63 deletions(-) delete mode 100644 compiler/rustc_privacy/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 4cbc98beae11..e209821c5d70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3803,7 +3803,6 @@ dependencies = [ "rustc_parse", "rustc_passes", "rustc_pattern_analysis", - "rustc_privacy", "rustc_public", "rustc_resolve", "rustc_session", @@ -4487,7 +4486,6 @@ dependencies = [ "rustc_ast", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_hir", "rustc_macros", "rustc_middle", diff --git a/compiler/rustc_driver_impl/Cargo.toml b/compiler/rustc_driver_impl/Cargo.toml index d184b6c8947c..ee6ffcca29ad 100644 --- a/compiler/rustc_driver_impl/Cargo.toml +++ b/compiler/rustc_driver_impl/Cargo.toml @@ -37,7 +37,6 @@ rustc_monomorphize = { path = "../rustc_monomorphize" } rustc_parse = { path = "../rustc_parse" } rustc_passes = { path = "../rustc_passes" } rustc_pattern_analysis = { path = "../rustc_pattern_analysis" } -rustc_privacy = { path = "../rustc_privacy" } rustc_public = { path = "../rustc_public", features = ["rustc_internal"] } rustc_resolve = { path = "../rustc_resolve" } rustc_session = { path = "../rustc_session" } diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 0ab71ad9981b..ffed24d72c41 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -134,7 +134,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_passes::DEFAULT_LOCALE_RESOURCE, rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, - rustc_privacy::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, diff --git a/compiler/rustc_privacy/Cargo.toml b/compiler/rustc_privacy/Cargo.toml index c8bfdb913041..ff59c19b6bb6 100644 --- a/compiler/rustc_privacy/Cargo.toml +++ b/compiler/rustc_privacy/Cargo.toml @@ -8,7 +8,6 @@ edition = "2024" rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_hir = { path = "../rustc_hir" } rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } diff --git a/compiler/rustc_privacy/messages.ftl b/compiler/rustc_privacy/messages.ftl deleted file mode 100644 index 43c34a109d72..000000000000 --- a/compiler/rustc_privacy/messages.ftl +++ /dev/null @@ -1,39 +0,0 @@ -privacy_field_is_private = - {$len -> - [1] field - *[other] fields - } {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> - [1] is - *[other] are - } private - .label = in this type -privacy_field_is_private_is_update_syntax_label = {$rest_len -> - [1] field - *[other] fields - } {$rest_field_names} {$rest_len -> - [1] is - *[other] are - } private -privacy_field_is_private_label = private field - -privacy_from_private_dep_in_public_interface = - {$kind} `{$descr}` from private dependency '{$krate}' in public interface - -privacy_in_public_interface = {$vis_descr} {$kind} `{$descr}` in public interface - .label = can't leak {$vis_descr} {$kind} - .visibility_label = `{$descr}` declared as {$vis_descr} - -privacy_item_is_private = {$kind} `{$descr}` is private - .label = private {$kind} - -privacy_private_interface_or_bounds_lint = {$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}` - .item_label = {$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}` - .ty_note = but {$ty_kind} `{$ty_descr}` is only usable at visibility `{$ty_vis_descr}` - -privacy_report_effective_visibility = {$descr} - -privacy_unnameable_types_lint = {$kind} `{$descr}` is reachable but cannot be named - .label = reachable at visibility `{$reachable_vis}`, but can only be named at visibility `{$reexported_vis}` - -privacy_unnamed_item_is_private = {$kind} is private - .label = private {$kind} diff --git a/compiler/rustc_privacy/src/errors.rs b/compiler/rustc_privacy/src/errors.rs index 4d1d58c08528..af4f0d61aa11 100644 --- a/compiler/rustc_privacy/src/errors.rs +++ b/compiler/rustc_privacy/src/errors.rs @@ -4,11 +4,17 @@ use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic}; use rustc_span::{Span, Symbol}; #[derive(Diagnostic)] -#[diag(privacy_field_is_private, code = E0451)] +#[diag("{$len -> + [1] field + *[other] fields +} {$field_names} of {$variant_descr} `{$def_path_str}` {$len -> + [1] is + *[other] are +} private", code = E0451)] pub(crate) struct FieldIsPrivate { #[primary_span] pub span: MultiSpan, - #[label] + #[label("in this type")] pub struct_span: Option, pub field_names: String, pub variant_descr: &'static str, @@ -20,14 +26,22 @@ pub(crate) struct FieldIsPrivate { #[derive(Subdiagnostic)] pub(crate) enum FieldIsPrivateLabel { - #[label(privacy_field_is_private_is_update_syntax_label)] + #[label( + "{$rest_len -> + [1] field + *[other] fields + } {$rest_field_names} {$rest_len -> + [1] is + *[other] are + } private" + )] IsUpdateSyntax { #[primary_span] span: Span, rest_field_names: String, rest_len: usize, }, - #[label(privacy_field_is_private_label)] + #[label("private field")] Other { #[primary_span] span: Span, @@ -35,17 +49,17 @@ pub(crate) enum FieldIsPrivateLabel { } #[derive(Diagnostic)] -#[diag(privacy_item_is_private)] +#[diag("{$kind} `{$descr}` is private")] pub(crate) struct ItemIsPrivate<'a> { #[primary_span] - #[label] + #[label("private {$kind}")] pub span: Span, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, } #[derive(Diagnostic)] -#[diag(privacy_unnamed_item_is_private)] +#[diag("{$kind} is private")] pub(crate) struct UnnamedItemIsPrivate { #[primary_span] pub span: Span, @@ -53,20 +67,20 @@ pub(crate) struct UnnamedItemIsPrivate { } #[derive(Diagnostic)] -#[diag(privacy_in_public_interface, code = E0446)] +#[diag("{$vis_descr} {$kind} `{$descr}` in public interface", code = E0446)] pub(crate) struct InPublicInterface<'a> { #[primary_span] - #[label] + #[label("can't leak {$vis_descr} {$kind}")] pub span: Span, pub vis_descr: &'static str, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, - #[label(privacy_visibility_label)] + #[label("`{$descr}` declared as {$vis_descr}")] pub vis_span: Span, } #[derive(Diagnostic)] -#[diag(privacy_report_effective_visibility)] +#[diag("{$descr}")] pub(crate) struct ReportEffectiveVisibility { #[primary_span] pub span: Span, @@ -74,7 +88,7 @@ pub(crate) struct ReportEffectiveVisibility { } #[derive(LintDiagnostic)] -#[diag(privacy_from_private_dep_in_public_interface)] +#[diag("{$kind} `{$descr}` from private dependency '{$krate}' in public interface")] pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, @@ -82,9 +96,11 @@ pub(crate) struct FromPrivateDependencyInPublicInterface<'a> { } #[derive(LintDiagnostic)] -#[diag(privacy_unnameable_types_lint)] +#[diag("{$kind} `{$descr}` is reachable but cannot be named")] pub(crate) struct UnnameableTypesLint<'a> { - #[label] + #[label( + "reachable at visibility `{$reachable_vis}`, but can only be named at visibility `{$reexported_vis}`" + )] pub span: Span, pub kind: &'a str, pub descr: DiagArgFromDisplay<'a>, @@ -96,14 +112,14 @@ pub(crate) struct UnnameableTypesLint<'a> { // They will replace private-in-public errors and compatibility lints in future. // See https://rust-lang.github.io/rfcs/2145-type-privacy.html for more details. #[derive(LintDiagnostic)] -#[diag(privacy_private_interface_or_bounds_lint)] +#[diag("{$ty_kind} `{$ty_descr}` is more private than the item `{$item_descr}`")] pub(crate) struct PrivateInterfacesOrBoundsLint<'a> { - #[label(privacy_item_label)] + #[label("{$item_kind} `{$item_descr}` is reachable at visibility `{$item_vis_descr}`")] pub item_span: Span, pub item_kind: &'a str, pub item_descr: DiagArgFromDisplay<'a>, pub item_vis_descr: &'a str, - #[note(privacy_ty_note)] + #[note("but {$ty_kind} `{$ty_descr}` is only usable at visibility `{$ty_vis_descr}`")] pub ty_span: Span, pub ty_kind: &'a str, pub ty_descr: DiagArgFromDisplay<'a>, diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 333677521023..4a88ea0cc445 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -39,8 +39,6 @@ use rustc_span::hygiene::Transparency; use rustc_span::{Ident, Span, Symbol, sym}; use tracing::debug; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - //////////////////////////////////////////////////////////////////////////////// // Generic infrastructure used to implement specific visitors below. //////////////////////////////////////////////////////////////////////////////// From 7dbbab63e242e2e09902930b630cc0898bc55537 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 31 Jan 2026 23:27:51 +0100 Subject: [PATCH 315/319] Emit a future error warning for duplicate doc attribute --- .../rustc_attr_parsing/src/attributes/doc.rs | 13 ++++++++-- compiler/rustc_lint_defs/src/builtin.rs | 2 +- tests/rustdoc-ui/deprecated-attrs.rs | 4 ++- tests/rustdoc-ui/deprecated-attrs.stderr | 12 ++++++--- tests/rustdoc-ui/doc-cfg-2.rs | 1 + tests/rustdoc-ui/doc-cfg-2.stderr | 26 +++++++++++-------- tests/rustdoc-ui/doc-include-suggestion.rs | 4 ++- .../rustdoc-ui/doc-include-suggestion.stderr | 8 ++++-- tests/rustdoc-ui/doctest/doc-test-attr.rs | 1 + tests/rustdoc-ui/doctest/doc-test-attr.stderr | 12 ++++++--- tests/rustdoc-ui/lints/doc-attr-2.rs | 1 + tests/rustdoc-ui/lints/doc-attr-2.stderr | 14 ++++++---- tests/rustdoc-ui/lints/doc-spotlight.fixed | 1 + tests/rustdoc-ui/lints/doc-spotlight.rs | 1 + tests/rustdoc-ui/lints/doc-spotlight.stderr | 8 ++++-- tests/rustdoc-ui/lints/doc_cfg_hide.rs | 1 + tests/rustdoc-ui/lints/doc_cfg_hide.stderr | 12 ++++++--- tests/rustdoc-ui/lints/duplicated-attr.rs | 6 +++++ tests/rustdoc-ui/lints/duplicated-attr.stderr | 20 ++++++++++++++ .../lints/invalid-crate-level-lint.rs | 1 + .../lints/invalid-crate-level-lint.stderr | 12 ++++++--- tests/rustdoc-ui/lints/invalid-doc-attr.rs | 1 + .../rustdoc-ui/lints/invalid-doc-attr.stderr | 21 +++++++++------ tests/ui/attributes/doc-attr.rs | 1 + tests/ui/attributes/doc-attr.stderr | 20 ++++++++------ ...atures-note-version-and-pr-issue-141619.rs | 1 + ...es-note-version-and-pr-issue-141619.stderr | 10 ++++--- tests/ui/repr/invalid_repr_list_help.rs | 1 + tests/ui/repr/invalid_repr_list_help.stderr | 18 ++++++++----- 29 files changed, 166 insertions(+), 67 deletions(-) create mode 100644 tests/rustdoc-ui/lints/duplicated-attr.rs create mode 100644 tests/rustdoc-ui/lints/duplicated-attr.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index a287c0c5a930..8b1966a836f1 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -131,8 +131,17 @@ impl DocParser { return; } - if self.attribute.no_crate_inject.is_some() { - cx.duplicate_key(path.span(), sym::no_crate_inject); + if let Some(used_span) = self.attribute.no_crate_inject { + let unused_span = path.span(); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::UnusedDuplicate { + this: unused_span, + other: used_span, + warning: true, + }, + unused_span, + ); return; } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index d1d5d0a56ead..f4e6e93356c7 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -3458,7 +3458,7 @@ declare_lint! { /// but this lint was introduced to avoid breaking any existing /// crates which included them. pub INVALID_DOC_ATTRIBUTES, - Deny, + Warn, "detects invalid `#[doc(...)]` attributes", } diff --git a/tests/rustdoc-ui/deprecated-attrs.rs b/tests/rustdoc-ui/deprecated-attrs.rs index 26aaf0d46808..dcca3114fb7d 100644 --- a/tests/rustdoc-ui/deprecated-attrs.rs +++ b/tests/rustdoc-ui/deprecated-attrs.rs @@ -1,11 +1,13 @@ //@ compile-flags: --passes unknown-pass +#![deny(invalid_doc_attributes)] +//~^ NOTE + #![doc(no_default_passes)] //~^ ERROR unknown `doc` attribute `no_default_passes` //~| NOTE no longer functions //~| NOTE see issue #44136 //~| NOTE `doc(no_default_passes)` is now a no-op -//~| NOTE `#[deny(invalid_doc_attributes)]` on by default #![doc(passes = "collapse-docs unindent-comments")] //~^ ERROR unknown `doc` attribute `passes` //~| NOTE no longer functions diff --git a/tests/rustdoc-ui/deprecated-attrs.stderr b/tests/rustdoc-ui/deprecated-attrs.stderr index 6135b1496925..e32a1eb3370d 100644 --- a/tests/rustdoc-ui/deprecated-attrs.stderr +++ b/tests/rustdoc-ui/deprecated-attrs.stderr @@ -4,17 +4,21 @@ warning: the `passes` flag no longer functions = help: you may want to use --document-private-items error: unknown `doc` attribute `no_default_passes` - --> $DIR/deprecated-attrs.rs:3:8 + --> $DIR/deprecated-attrs.rs:6:8 | LL | #![doc(no_default_passes)] | ^^^^^^^^^^^^^^^^^ no longer functions | = note: `doc` attribute `no_default_passes` no longer functions; see issue #44136 = note: `doc(no_default_passes)` is now a no-op - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/deprecated-attrs.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `passes` - --> $DIR/deprecated-attrs.rs:9:8 + --> $DIR/deprecated-attrs.rs:11:8 | LL | #![doc(passes = "collapse-docs unindent-comments")] | ^^^^^^ no longer functions @@ -23,7 +27,7 @@ LL | #![doc(passes = "collapse-docs unindent-comments")] = note: `doc(passes)` is now a no-op error: unknown `doc` attribute `plugins` - --> $DIR/deprecated-attrs.rs:14:8 + --> $DIR/deprecated-attrs.rs:16:8 | LL | #![doc(plugins = "xxx")] | ^^^^^^^ no longer functions diff --git a/tests/rustdoc-ui/doc-cfg-2.rs b/tests/rustdoc-ui/doc-cfg-2.rs index 7a5d1f3e3dbb..f615e96bbc6b 100644 --- a/tests/rustdoc-ui/doc-cfg-2.rs +++ b/tests/rustdoc-ui/doc-cfg-2.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #[doc(cfg(foo), cfg(bar))] diff --git a/tests/rustdoc-ui/doc-cfg-2.stderr b/tests/rustdoc-ui/doc-cfg-2.stderr index 1272e569897b..a842cbc40288 100644 --- a/tests/rustdoc-ui/doc-cfg-2.stderr +++ b/tests/rustdoc-ui/doc-cfg-2.stderr @@ -1,5 +1,5 @@ warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:3:11 + --> $DIR/doc-cfg-2.rs:4:11 | LL | #[doc(cfg(foo), cfg(bar))] | ^^^ @@ -10,7 +10,7 @@ LL | #[doc(cfg(foo), cfg(bar))] = note: `#[warn(unexpected_cfgs)]` on by default warning: unexpected `cfg` condition name: `bar` - --> $DIR/doc-cfg-2.rs:3:21 + --> $DIR/doc-cfg-2.rs:4:21 | LL | #[doc(cfg(foo), cfg(bar))] | ^^^ @@ -19,45 +19,49 @@ LL | #[doc(cfg(foo), cfg(bar))] = note: see for more information about checking conditional configuration error: only `hide` or `show` are allowed in `#[doc(auto_cfg(...))]` - --> $DIR/doc-cfg-2.rs:6:16 + --> $DIR/doc-cfg-2.rs:7:16 | LL | #[doc(auto_cfg(42))] | ^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-cfg-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:7:21 + --> $DIR/doc-cfg-2.rs:8:21 | LL | #[doc(auto_cfg(hide(true)))] | ^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:8:21 + --> $DIR/doc-cfg-2.rs:9:21 | LL | #[doc(auto_cfg(hide(42)))] | ^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc-cfg-2.rs:9:21 + --> $DIR/doc-cfg-2.rs:10:21 | LL | #[doc(auto_cfg(hide("a")))] | ^^^ error: expected boolean for `#[doc(auto_cfg = ...)]` - --> $DIR/doc-cfg-2.rs:10:18 + --> $DIR/doc-cfg-2.rs:11:18 | LL | #[doc(auto_cfg = 42)] | ^^ error: expected boolean for `#[doc(auto_cfg = ...)]` - --> $DIR/doc-cfg-2.rs:11:18 + --> $DIR/doc-cfg-2.rs:12:18 | LL | #[doc(auto_cfg = "a")] | ^^^ warning: unexpected `cfg` condition name: `feature` - --> $DIR/doc-cfg-2.rs:14:21 + --> $DIR/doc-cfg-2.rs:15:21 | LL | #[doc(auto_cfg(hide(feature = "windows")))] | ^^^^^^^^^^^^^^^^^^^ @@ -66,7 +70,7 @@ LL | #[doc(auto_cfg(hide(feature = "windows")))] = note: see for more information about checking conditional configuration warning: unexpected `cfg` condition name: `foo` - --> $DIR/doc-cfg-2.rs:16:21 + --> $DIR/doc-cfg-2.rs:17:21 | LL | #[doc(auto_cfg(hide(foo)))] | ^^^ diff --git a/tests/rustdoc-ui/doc-include-suggestion.rs b/tests/rustdoc-ui/doc-include-suggestion.rs index aff0a24ace86..1295805ae1a0 100644 --- a/tests/rustdoc-ui/doc-include-suggestion.rs +++ b/tests/rustdoc-ui/doc-include-suggestion.rs @@ -1,6 +1,8 @@ +#![deny(invalid_doc_attributes)] +//~^ NOTE + #[doc(include = "external-cross-doc.md")] //~^ ERROR unknown `doc` attribute `include` //~| HELP use `doc = include_str!` instead // FIXME(#85497): make this a deny instead so it's more clear what's happening -//~| NOTE on by default pub struct NeedMoreDocs; diff --git a/tests/rustdoc-ui/doc-include-suggestion.stderr b/tests/rustdoc-ui/doc-include-suggestion.stderr index ea5261e5bbd4..1587984b6e6e 100644 --- a/tests/rustdoc-ui/doc-include-suggestion.stderr +++ b/tests/rustdoc-ui/doc-include-suggestion.stderr @@ -1,10 +1,14 @@ error: unknown `doc` attribute `include` - --> $DIR/doc-include-suggestion.rs:1:7 + --> $DIR/doc-include-suggestion.rs:4:7 | LL | #[doc(include = "external-cross-doc.md")] | ^^^^^^^ help: use `doc = include_str!` instead: `#[doc = include_str!("external-cross-doc.md")]` | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-include-suggestion.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/doctest/doc-test-attr.rs b/tests/rustdoc-ui/doctest/doc-test-attr.rs index 8570252c4493..e664f75a9cf7 100644 --- a/tests/rustdoc-ui/doctest/doc-test-attr.rs +++ b/tests/rustdoc-ui/doctest/doc-test-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![doc(test)] diff --git a/tests/rustdoc-ui/doctest/doc-test-attr.stderr b/tests/rustdoc-ui/doctest/doc-test-attr.stderr index cf7bce66ef40..98e7b315ea57 100644 --- a/tests/rustdoc-ui/doctest/doc-test-attr.stderr +++ b/tests/rustdoc-ui/doctest/doc-test-attr.stderr @@ -1,19 +1,23 @@ error: `#[doc(test(...)]` takes a list of attributes - --> $DIR/doc-test-attr.rs:3:8 + --> $DIR/doc-test-attr.rs:4:8 | LL | #![doc(test)] | ^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-test-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#[doc(test(...)]` takes a list of attributes - --> $DIR/doc-test-attr.rs:5:13 + --> $DIR/doc-test-attr.rs:6:13 | LL | #![doc(test = "hello")] | ^^^^^^^^^ error: unknown `doc(test)` attribute `a` - --> $DIR/doc-test-attr.rs:7:13 + --> $DIR/doc-test-attr.rs:8:13 | LL | #![doc(test(a))] | ^ diff --git a/tests/rustdoc-ui/lints/doc-attr-2.rs b/tests/rustdoc-ui/lints/doc-attr-2.rs index e5198e347523..9802fb5d95a5 100644 --- a/tests/rustdoc-ui/lints/doc-attr-2.rs +++ b/tests/rustdoc-ui/lints/doc-attr-2.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute `as_ptr` diff --git a/tests/rustdoc-ui/lints/doc-attr-2.stderr b/tests/rustdoc-ui/lints/doc-attr-2.stderr index c2bb45c5785e..f96eab1aba61 100644 --- a/tests/rustdoc-ui/lints/doc-attr-2.stderr +++ b/tests/rustdoc-ui/lints/doc-attr-2.stderr @@ -1,25 +1,29 @@ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr-2.rs:4:7 + --> $DIR/doc-attr-2.rs:5:7 | LL | #[doc(as_ptr)] | ^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-attr-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr-2.rs:8:7 + --> $DIR/doc-attr-2.rs:9:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr-2.rs:8:17 + --> $DIR/doc-attr-2.rs:9:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr-2.rs:1:8 + --> $DIR/doc-attr-2.rs:2:8 | LL | #![doc(as_ptr)] | ^^^^^^ diff --git a/tests/rustdoc-ui/lints/doc-spotlight.fixed b/tests/rustdoc-ui/lints/doc-spotlight.fixed index 0f8f11a9430e..98b0fac87898 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.fixed +++ b/tests/rustdoc-ui/lints/doc-spotlight.fixed @@ -1,5 +1,6 @@ //@ run-rustfix #![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] #[doc(notable_trait)] //~^ ERROR unknown `doc` attribute `spotlight` diff --git a/tests/rustdoc-ui/lints/doc-spotlight.rs b/tests/rustdoc-ui/lints/doc-spotlight.rs index c1f90dd442b2..a0d874bdcb21 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.rs +++ b/tests/rustdoc-ui/lints/doc-spotlight.rs @@ -1,5 +1,6 @@ //@ run-rustfix #![feature(doc_notable_trait)] +#![deny(invalid_doc_attributes)] #[doc(spotlight)] //~^ ERROR unknown `doc` attribute `spotlight` diff --git a/tests/rustdoc-ui/lints/doc-spotlight.stderr b/tests/rustdoc-ui/lints/doc-spotlight.stderr index 9682a3c0c8be..8c6f9738bdc0 100644 --- a/tests/rustdoc-ui/lints/doc-spotlight.stderr +++ b/tests/rustdoc-ui/lints/doc-spotlight.stderr @@ -1,12 +1,16 @@ error: unknown `doc` attribute `spotlight` - --> $DIR/doc-spotlight.rs:4:7 + --> $DIR/doc-spotlight.rs:5:7 | LL | #[doc(spotlight)] | ^^^^^^^^^ help: use `notable_trait` instead | = note: `doc(spotlight)` was renamed to `doc(notable_trait)` = note: `doc(spotlight)` is now a no-op - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-spotlight.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.rs b/tests/rustdoc-ui/lints/doc_cfg_hide.rs index 397b21393e5c..6c190f9befac 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.rs +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #![doc(auto_cfg(hide = "test"))] //~ ERROR #![doc(auto_cfg(hide))] //~ ERROR diff --git a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr index acbe6ef69dd5..a5ec8fdf5d34 100644 --- a/tests/rustdoc-ui/lints/doc_cfg_hide.stderr +++ b/tests/rustdoc-ui/lints/doc_cfg_hide.stderr @@ -1,19 +1,23 @@ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items - --> $DIR/doc_cfg_hide.rs:2:17 + --> $DIR/doc_cfg_hide.rs:3:17 | LL | #![doc(auto_cfg(hide = "test"))] | ^^^^^^^^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc_cfg_hide.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: `#![doc(auto_cfg(hide(...)))]` expects a list of items - --> $DIR/doc_cfg_hide.rs:3:17 + --> $DIR/doc_cfg_hide.rs:4:17 | LL | #![doc(auto_cfg(hide))] | ^^^^ error: `#![doc(auto_cfg(hide(...)))]` only accepts identifiers or key/value items - --> $DIR/doc_cfg_hide.rs:4:22 + --> $DIR/doc_cfg_hide.rs:5:22 | LL | #![doc(auto_cfg(hide(not(windows))))] | ^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/duplicated-attr.rs b/tests/rustdoc-ui/lints/duplicated-attr.rs new file mode 100644 index 000000000000..b89908d700f4 --- /dev/null +++ b/tests/rustdoc-ui/lints/duplicated-attr.rs @@ -0,0 +1,6 @@ +#![deny(invalid_doc_attributes)] +#![expect(unused_attributes)] +#![doc(test(no_crate_inject))] +#![doc(test(no_crate_inject))] +//~^ ERROR +//~| WARN diff --git a/tests/rustdoc-ui/lints/duplicated-attr.stderr b/tests/rustdoc-ui/lints/duplicated-attr.stderr new file mode 100644 index 000000000000..3682710bb54a --- /dev/null +++ b/tests/rustdoc-ui/lints/duplicated-attr.stderr @@ -0,0 +1,20 @@ +error: unused attribute + --> $DIR/duplicated-attr.rs:4:13 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^ help: remove this attribute + | +note: attribute also specified here + --> $DIR/duplicated-attr.rs:3:13 + | +LL | #![doc(test(no_crate_inject))] + | ^^^^^^^^^^^^^^^ + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/duplicated-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs index 275e20e80a17..afb0a5987deb 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #[doc(test(no_crate_inject))] diff --git a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr index fdb95e7de41c..7569cf575e51 100644 --- a/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr +++ b/tests/rustdoc-ui/lints/invalid-crate-level-lint.stderr @@ -1,14 +1,18 @@ error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:3:12 + --> $DIR/invalid-crate-level-lint.rs:4:12 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ | = note: read for more information - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid-crate-level-lint.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:7:17 + --> $DIR/invalid-crate-level-lint.rs:8:17 | LL | #![doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ @@ -16,7 +20,7 @@ LL | #![doc(test(no_crate_inject))] = note: read for more information error: this attribute can only be applied at the crate level - --> $DIR/invalid-crate-level-lint.rs:10:16 + --> $DIR/invalid-crate-level-lint.rs:11:16 | LL | #[doc(test(no_crate_inject))] | ^^^^^^^^^^^^^^^ diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.rs b/tests/rustdoc-ui/lints/invalid-doc-attr.rs index 169d092d7e17..60c215222d7f 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr.rs +++ b/tests/rustdoc-ui/lints/invalid-doc-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![feature(doc_masked)] diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr.stderr index e431b8df2263..2e9b9f0a8cfb 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr.stderr +++ b/tests/rustdoc-ui/lints/invalid-doc-attr.stderr @@ -1,5 +1,5 @@ error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:7:7 + --> $DIR/invalid-doc-attr.rs:8:7 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items @@ -8,10 +8,14 @@ LL | pub fn foo() {} | ------------ not a `use` item | = note: read for more information - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid-doc-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: conflicting doc inlining attributes - --> $DIR/invalid-doc-attr.rs:17:7 + --> $DIR/invalid-doc-attr.rs:18:7 | LL | #[doc(inline)] | ^^^^^^ this attribute... @@ -21,7 +25,7 @@ LL | #[doc(no_inline)] = help: remove one of the conflicting attributes error: this attribute can only be applied to an `extern crate` item - --> $DIR/invalid-doc-attr.rs:23:7 + --> $DIR/invalid-doc-attr.rs:24:7 | LL | #[doc(masked)] | ^^^^^^ only applicable on `extern crate` items @@ -32,7 +36,7 @@ LL | pub struct Masked; = note: read for more information error: this attribute cannot be applied to an `extern crate self` item - --> $DIR/invalid-doc-attr.rs:27:7 + --> $DIR/invalid-doc-attr.rs:28:7 | LL | #[doc(masked)] | ^^^^^^ not applicable on `extern crate self` items @@ -41,9 +45,10 @@ LL | pub extern crate self as reexport; | --------------------------------- `extern crate self` defined here error: this attribute can only be applied to an `extern crate` item - --> $DIR/invalid-doc-attr.rs:4:8 + --> $DIR/invalid-doc-attr.rs:5:8 | -LL | / #![crate_type = "lib"] +LL | / #![deny(invalid_doc_attributes)] +LL | | #![crate_type = "lib"] LL | | #![feature(doc_masked)] LL | | LL | | #![doc(masked)] @@ -55,7 +60,7 @@ LL | | pub extern crate self as reexport; = note: read for more information error: this attribute can only be applied to a `use` item - --> $DIR/invalid-doc-attr.rs:12:11 + --> $DIR/invalid-doc-attr.rs:13:11 | LL | #[doc(inline)] | ^^^^^^ only applicable on `use` items diff --git a/tests/ui/attributes/doc-attr.rs b/tests/ui/attributes/doc-attr.rs index 8d733be93567..b45f211f6222 100644 --- a/tests/ui/attributes/doc-attr.rs +++ b/tests/ui/attributes/doc-attr.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #![doc(as_ptr)] //~^ ERROR unknown `doc` attribute diff --git a/tests/ui/attributes/doc-attr.stderr b/tests/ui/attributes/doc-attr.stderr index dfc0e8ad5b6f..005810fa23fa 100644 --- a/tests/ui/attributes/doc-attr.stderr +++ b/tests/ui/attributes/doc-attr.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:9:1 + --> $DIR/doc-attr.rs:10:1 | LL | #[doc(123)] | ^^^^^^---^^ @@ -7,7 +7,7 @@ LL | #[doc(123)] | expected this to be of the form `... = "..."` error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:11:1 + --> $DIR/doc-attr.rs:12:1 | LL | #[doc("hello", "bar")] | ^^^^^^-------^^^^^^^^^ @@ -15,7 +15,7 @@ LL | #[doc("hello", "bar")] | expected this to be of the form `... = "..."` error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:11:1 + --> $DIR/doc-attr.rs:12:1 | LL | #[doc("hello", "bar")] | ^^^^^^^^^^^^^^^-----^^ @@ -23,27 +23,31 @@ LL | #[doc("hello", "bar")] | expected this to be of the form `... = "..."` error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:5:7 + --> $DIR/doc-attr.rs:6:7 | LL | #[doc(as_ptr)] | ^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/doc-attr.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr.rs:14:7 + --> $DIR/doc-attr.rs:15:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr.rs:14:17 + --> $DIR/doc-attr.rs:15:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ error: unknown `doc` attribute `as_ptr` - --> $DIR/doc-attr.rs:2:8 + --> $DIR/doc-attr.rs:3:8 | LL | #![doc(as_ptr)] | ^^^^^^ diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs index d8c5f48f9fd9..a7dde7c97f6a 100644 --- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs +++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![feature(external_doc)] //~ ERROR feature has been removed #![doc(include("README.md"))] //~ ERROR unknown `doc` attribute `include` diff --git a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr index d9556560b01c..a009898761df 100644 --- a/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr +++ b/tests/ui/feature-gates/removed-features-note-version-and-pr-issue-141619.stderr @@ -1,5 +1,5 @@ error[E0557]: feature has been removed - --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:12 + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:12 | LL | #![feature(external_doc)] | ^^^^^^^^^^^^ feature has been removed @@ -8,12 +8,16 @@ LL | #![feature(external_doc)] = note: use #[doc = include_str!("filename")] instead, which handles macro invocations error: unknown `doc` attribute `include` - --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:2:8 + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:3:8 | LL | #![doc(include("README.md"))] | ^^^^^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/removed-features-note-version-and-pr-issue-141619.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 2 previous errors diff --git a/tests/ui/repr/invalid_repr_list_help.rs b/tests/ui/repr/invalid_repr_list_help.rs index 77f2a68537b3..e8bf5fdcd3fd 100644 --- a/tests/ui/repr/invalid_repr_list_help.rs +++ b/tests/ui/repr/invalid_repr_list_help.rs @@ -1,3 +1,4 @@ +#![deny(invalid_doc_attributes)] #![crate_type = "lib"] #[repr(uwu)] //~ERROR: unrecognized representation hint diff --git a/tests/ui/repr/invalid_repr_list_help.stderr b/tests/ui/repr/invalid_repr_list_help.stderr index f9d1593275ee..322650814828 100644 --- a/tests/ui/repr/invalid_repr_list_help.stderr +++ b/tests/ui/repr/invalid_repr_list_help.stderr @@ -1,5 +1,5 @@ error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:3:8 + --> $DIR/invalid_repr_list_help.rs:4:8 | LL | #[repr(uwu)] | ^^^ @@ -8,7 +8,7 @@ LL | #[repr(uwu)] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:6:8 + --> $DIR/invalid_repr_list_help.rs:7:8 | LL | #[repr(uwu = "a")] | ^^^^^^^^^ @@ -17,7 +17,7 @@ LL | #[repr(uwu = "a")] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:9:8 + --> $DIR/invalid_repr_list_help.rs:10:8 | LL | #[repr(uwu(4))] | ^^^^^^ @@ -26,7 +26,7 @@ LL | #[repr(uwu(4))] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:14:8 + --> $DIR/invalid_repr_list_help.rs:15:8 | LL | #[repr(uwu, u8)] | ^^^ @@ -35,7 +35,7 @@ LL | #[repr(uwu, u8)] = note: for more information, visit error[E0552]: unrecognized representation hint - --> $DIR/invalid_repr_list_help.rs:19:8 + --> $DIR/invalid_repr_list_help.rs:20:8 | LL | #[repr(uwu)] | ^^^ @@ -44,12 +44,16 @@ LL | #[repr(uwu)] = note: for more information, visit error: unknown `doc` attribute `owo` - --> $DIR/invalid_repr_list_help.rs:20:7 + --> $DIR/invalid_repr_list_help.rs:21:7 | LL | #[doc(owo)] | ^^^ | - = note: `#[deny(invalid_doc_attributes)]` on by default +note: the lint level is defined here + --> $DIR/invalid_repr_list_help.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 6 previous errors From 6d713489d00a87a683f253a242d71e717c8fcf26 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 1 Feb 2026 00:58:51 +0100 Subject: [PATCH 316/319] Make more doc attribute parsing error into future warnings --- .../rustc_attr_parsing/src/attributes/doc.rs | 19 ++++++++-- compiler/rustc_lint/messages.ftl | 4 +++ compiler/rustc_lint/src/early/diagnostics.rs | 2 ++ compiler/rustc_lint/src/lints.rs | 5 +++ compiler/rustc_lint_defs/src/lib.rs | 1 + tests/rustdoc-ui/lints/doc-attr.rs | 4 +++ tests/rustdoc-ui/lints/doc-attr.stderr | 36 ++++++++++--------- tests/rustdoc-ui/lints/invalid-doc-attr-2.rs | 7 ++++ .../lints/invalid-doc-attr-2.stderr | 21 +++++++++++ 9 files changed, 80 insertions(+), 19 deletions(-) create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-2.rs create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 8b1966a836f1..1bf5352ee838 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -591,7 +591,7 @@ impl DocParser { let suggestions = cx.suggestions(); let span = cx.attr_span; cx.emit_lint( - rustc_session::lint::builtin::ILL_FORMED_ATTRIBUTE_INPUT, + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, AttributeLintKind::IllFormedAttributeInput { suggestions, docs: None }, span, ); @@ -604,14 +604,27 @@ impl DocParser { self.parse_single_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - cx.expected_name_value(lit.span, None); + // FIXME: Remove the lint and uncomment line after beta backport is + // done. + // cx.expected_name_value(lit.span, None); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + lit.span, + ); } } } } ArgParser::NameValue(nv) => { if nv.value_as_str().is_none() { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + // FIXME: Remove the lint and uncomment line after beta backport is done. + // cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + nv.value_span, + ); } else { unreachable!( "Should have been handled at the same time as sugar-syntaxed doc comments" diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index 867b937d4090..ebee380765c6 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -558,6 +558,10 @@ lint_macro_expr_fragment_specifier_2024_migration = lint_malformed_attribute = malformed lint attribute input +lint_malformed_doc = + malformed `doc` attribute input + .warn = {-lint_previously_accepted} + lint_map_unit_fn = `Iterator::map` call that discard the iterator's values .note = `Iterator::map`, like many of the methods on `Iterator`, gets executed lazily, meaning that its effects won't be visible until it is iterated .function_label = this function returns `()`, which is likely not what you wanted diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 377b3c439453..1360ba2d9c1c 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -428,5 +428,7 @@ pub fn decorate_attribute_lint( sugg: suggested.map(|s| lints::UnknownCrateTypesSuggestion { span, snippet: s }), } .decorate_lint(diag), + + &AttributeLintKind::MalformedDoc => lints::MalformedDoc.decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index a20d90e1227e..0b5197d7ebdf 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3185,6 +3185,11 @@ pub(crate) struct UnusedDuplicate { pub warning: bool, } +#[derive(LintDiagnostic)] +#[diag(lint_malformed_doc)] +#[warning] +pub(crate) struct MalformedDoc; + #[derive(LintDiagnostic)] #[diag(lint_unsafe_attr_outside_unsafe)] pub(crate) struct UnsafeAttrOutsideUnsafeLint { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 4c78287b7784..ea2e67b8869b 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -826,6 +826,7 @@ pub enum AttributeLintKind { span: Span, suggested: Option, }, + MalformedDoc, } pub type RegisteredTools = FxIndexSet; diff --git a/tests/rustdoc-ui/lints/doc-attr.rs b/tests/rustdoc-ui/lints/doc-attr.rs index b27faa81cb92..f04961aa930a 100644 --- a/tests/rustdoc-ui/lints/doc-attr.rs +++ b/tests/rustdoc-ui/lints/doc-attr.rs @@ -1,8 +1,12 @@ #![crate_type = "lib"] +#![deny(invalid_doc_attributes)] #[doc(123)] //~^ ERROR malformed `doc` attribute +//~| WARN #[doc("hello", "bar")] //~^ ERROR malformed `doc` attribute //~| ERROR malformed `doc` attribute +//~| WARN +//~| WARN fn bar() {} diff --git a/tests/rustdoc-ui/lints/doc-attr.stderr b/tests/rustdoc-ui/lints/doc-attr.stderr index 8f8c6000b364..5b6f48acb3be 100644 --- a/tests/rustdoc-ui/lints/doc-attr.stderr +++ b/tests/rustdoc-ui/lints/doc-attr.stderr @@ -1,27 +1,31 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:3:1 +error: malformed `doc` attribute input + --> $DIR/doc-attr.rs:4:7 | LL | #[doc(123)] - | ^^^^^^---^^ - | | - | expected this to be of the form `... = "..."` + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/doc-attr.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:5:1 +error: malformed `doc` attribute input + --> $DIR/doc-attr.rs:7:7 | LL | #[doc("hello", "bar")] - | ^^^^^^-------^^^^^^^^^ - | | - | expected this to be of the form `... = "..."` + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:5:1 +error: malformed `doc` attribute input + --> $DIR/doc-attr.rs:7:16 | LL | #[doc("hello", "bar")] - | ^^^^^^^^^^^^^^^-----^^ - | | - | expected this to be of the form `... = "..."` + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: aborting due to 3 previous errors -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs b/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs new file mode 100644 index 000000000000..de99d5a9e078 --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.rs @@ -0,0 +1,7 @@ +#![deny(invalid_doc_attributes)] + +#![doc("other attribute")] +//~^ ERROR +//~| WARN +#![doc] +//~^ ERROR diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr new file mode 100644 index 000000000000..bfd926a2a517 --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr @@ -0,0 +1,21 @@ +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-2.rs:3:8 + | +LL | #![doc("other attribute")] + | ^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/invalid-doc-attr-2.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: valid forms for the attribute are `#![doc = "string"]`, `#![doc(alias)]`, `#![doc(attribute)]`, `#![doc(auto_cfg)]`, `#![doc(cfg)]`, `#![doc(fake_variadic)]`, `#![doc(hidden)]`, `#![doc(html_favicon_url)]`, `#![doc(html_logo_url)]`, `#![doc(html_no_source)]`, `#![doc(html_playground_url)]`, `#![doc(html_root_url)]`, `#![doc(include)]`, `#![doc(inline)]`, `#![doc(issue_tracker_base_url)]`, `#![doc(keyword)]`, `#![doc(masked)]`, `#![doc(no_default_passes)]`, `#![doc(no_inline)]`, `#![doc(notable_trait)]`, `#![doc(passes)]`, `#![doc(plugins)]`, `#![doc(rust_logo)]`, `#![doc(search_unbox)]`, `#![doc(spotlight)]`, and `#![doc(test)]` + --> $DIR/invalid-doc-attr-2.rs:6:1 + | +LL | #![doc] + | ^^^^^^^ + +error: aborting due to 2 previous errors + From c910511cab40e005fffbd5c31939f89448d030a8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 1 Feb 2026 14:59:29 +0100 Subject: [PATCH 317/319] Move remaining doc attribute parsing errors to warnings --- .../rustc_attr_parsing/src/attributes/doc.rs | 98 +++++++--- compiler/rustc_lint/messages.ftl | 8 + compiler/rustc_lint/src/early/diagnostics.rs | 4 + compiler/rustc_lint/src/lints.rs | 10 + compiler/rustc_lint_defs/src/lib.rs | 2 + tests/rustdoc-ui/bad-render-options.rs | 37 ++-- tests/rustdoc-ui/bad-render-options.stderr | 97 +++++----- tests/rustdoc-ui/doc-cfg.rs | 7 +- tests/rustdoc-ui/doc-cfg.stderr | 21 +-- tests/rustdoc-ui/lints/doc-attr.rs | 6 +- tests/rustdoc-ui/lints/doc-attr.stderr | 6 +- .../lints/invalid-doc-attr-2.stderr | 2 +- tests/rustdoc-ui/lints/invalid-doc-attr-3.rs | 22 +++ .../lints/invalid-doc-attr-3.stderr | 55 ++++++ tests/ui/attributes/doc-attr.rs | 9 +- tests/ui/attributes/doc-attr.stderr | 53 +++--- tests/ui/attributes/doc-test-literal.rs | 5 +- tests/ui/attributes/doc-test-literal.stderr | 16 +- tests/ui/attributes/malformed-attrs.rs | 7 +- tests/ui/attributes/malformed-attrs.stderr | 172 ++++++++---------- tests/ui/malformed/malformed-regressions.rs | 3 +- .../ui/malformed/malformed-regressions.stderr | 36 ++-- tests/ui/malformed/malformed-special-attrs.rs | 2 + .../malformed/malformed-special-attrs.stderr | 8 +- 24 files changed, 404 insertions(+), 282 deletions(-) create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-3.rs create mode 100644 tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/doc.rs b/compiler/rustc_attr_parsing/src/attributes/doc.rs index 1bf5352ee838..f8968639f98c 100644 --- a/compiler/rustc_attr_parsing/src/attributes/doc.rs +++ b/compiler/rustc_attr_parsing/src/attributes/doc.rs @@ -70,6 +70,42 @@ fn check_attr_crate_level(cx: &mut AcceptContext<'_, '_, S>, span: Spa true } +// FIXME: To be removed once merged and replace with `cx.expected_name_value(span, _name)`. +fn expected_name_value( + cx: &mut AcceptContext<'_, '_, S>, + span: Span, + _name: Option, +) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::ExpectedNameValue, + span, + ); +} + +// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. +fn expected_no_args(cx: &mut AcceptContext<'_, '_, S>, span: Span) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::ExpectedNoArgs, + span, + ); +} + +// FIXME: remove this method once merged and use `cx.expected_no_args(span)` instead. +// cx.expected_string_literal(span, _actual_literal); +fn expected_string_literal( + cx: &mut AcceptContext<'_, '_, S>, + span: Span, + _actual_literal: Option<&MetaItemLit>, +) { + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + span, + ); +} + fn parse_keyword_and_attribute( cx: &mut AcceptContext<'_, '_, S>, path: &OwnedPathParser, @@ -78,12 +114,12 @@ fn parse_keyword_and_attribute( attr_name: Symbol, ) { let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; let Some(value) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); return; }; @@ -127,7 +163,7 @@ impl DocParser { match path.word_sym() { Some(sym::no_crate_inject) => { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -153,7 +189,14 @@ impl DocParser { } Some(sym::attr) => { let Some(list) = args.list() else { - cx.expected_list(cx.attr_span, args); + // FIXME: remove this method once merged and uncomment the line below instead. + // cx.expected_list(cx.attr_span, args); + let span = cx.attr_span; + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + span, + ); return; }; @@ -255,7 +298,7 @@ impl DocParser { inline: DocInline, ) { if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -337,7 +380,14 @@ impl DocParser { match sub_item.args() { a @ (ArgParser::NoArgs | ArgParser::NameValue(_)) => { let Some(name) = sub_item.path().word_sym() else { - cx.expected_identifier(sub_item.path().span()); + // FIXME: remove this method once merged and uncomment the line + // below instead. + // cx.expected_identifier(sub_item.path().span()); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + sub_item.path().span(), + ); continue; }; if let Ok(CfgEntry::NameValue { name, value, .. }) = @@ -400,7 +450,7 @@ impl DocParser { macro_rules! no_args { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } @@ -419,7 +469,7 @@ impl DocParser { macro_rules! no_args_and_not_crate_level { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } let span = path.span(); @@ -432,7 +482,7 @@ impl DocParser { macro_rules! no_args_and_crate_level { ($ident: ident) => {{ if let Err(span) = args.no_args() { - cx.expected_no_args(span); + expected_no_args(cx, span); return; } let span = path.span(); @@ -445,12 +495,12 @@ impl DocParser { macro_rules! string_arg_and_crate_level { ($ident: ident) => {{ let Some(nv) = args.name_value() else { - cx.expected_name_value(args.span().unwrap_or(path.span()), path.word_sym()); + expected_name_value(cx, args.span().unwrap_or(path.span()), path.word_sym()); return; }; let Some(s) = nv.value_as_str() else { - cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); return; }; @@ -521,7 +571,14 @@ impl DocParser { self.parse_single_test_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - cx.unexpected_literal(lit.span); + // FIXME: remove this method once merged and uncomment the line + // below instead. + // cx.unexpected_literal(lit.span); + cx.emit_lint( + rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, + AttributeLintKind::MalformedDoc, + lit.span, + ); } } } @@ -604,27 +661,14 @@ impl DocParser { self.parse_single_doc_attr_item(cx, mip); } MetaItemOrLitParser::Lit(lit) => { - // FIXME: Remove the lint and uncomment line after beta backport is - // done. - // cx.expected_name_value(lit.span, None); - cx.emit_lint( - rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::MalformedDoc, - lit.span, - ); + expected_name_value(cx, lit.span, None); } } } } ArgParser::NameValue(nv) => { if nv.value_as_str().is_none() { - // FIXME: Remove the lint and uncomment line after beta backport is done. - // cx.expected_string_literal(nv.value_span, Some(nv.value_as_lit())); - cx.emit_lint( - rustc_session::lint::builtin::INVALID_DOC_ATTRIBUTES, - AttributeLintKind::MalformedDoc, - nv.value_span, - ); + expected_string_literal(cx, nv.value_span, Some(nv.value_as_lit())); } else { unreachable!( "Should have been handled at the same time as sugar-syntaxed doc comments" diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index ebee380765c6..2f5b7ed26952 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -326,6 +326,14 @@ lint_expectation = this lint expectation is unfulfilled .note = the `unfulfilled_lint_expectations` lint can't be expected and will always produce this message .rationale = {$rationale} +lint_expected_name_value = + expected this to be of the form `... = "..."` + .warn = {-lint_previously_accepted} + +lint_expected_no_args = + didn't expect any arguments here + .warn = {-lint_previously_accepted} + lint_for_loops_over_fallibles = for loop over {$article} `{$ref_prefix}{$ty}`. This is more readably written as an `if let` statement .suggestion = consider using `if let` to clear intent diff --git a/compiler/rustc_lint/src/early/diagnostics.rs b/compiler/rustc_lint/src/early/diagnostics.rs index 1360ba2d9c1c..c0ab0d1f0040 100644 --- a/compiler/rustc_lint/src/early/diagnostics.rs +++ b/compiler/rustc_lint/src/early/diagnostics.rs @@ -430,5 +430,9 @@ pub fn decorate_attribute_lint( .decorate_lint(diag), &AttributeLintKind::MalformedDoc => lints::MalformedDoc.decorate_lint(diag), + + &AttributeLintKind::ExpectedNoArgs => lints::ExpectedNoArgs.decorate_lint(diag), + + &AttributeLintKind::ExpectedNameValue => lints::ExpectedNameValue.decorate_lint(diag), } } diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 0b5197d7ebdf..1a87cc013e79 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -3190,6 +3190,16 @@ pub(crate) struct UnusedDuplicate { #[warning] pub(crate) struct MalformedDoc; +#[derive(LintDiagnostic)] +#[diag(lint_expected_no_args)] +#[warning] +pub(crate) struct ExpectedNoArgs; + +#[derive(LintDiagnostic)] +#[diag(lint_expected_name_value)] +#[warning] +pub(crate) struct ExpectedNameValue; + #[derive(LintDiagnostic)] #[diag(lint_unsafe_attr_outside_unsafe)] pub(crate) struct UnsafeAttrOutsideUnsafeLint { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index ea2e67b8869b..b3e5b93cf2fc 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -827,6 +827,8 @@ pub enum AttributeLintKind { suggested: Option, }, MalformedDoc, + ExpectedNoArgs, + ExpectedNameValue, } pub type RegisteredTools = FxIndexSet; diff --git a/tests/rustdoc-ui/bad-render-options.rs b/tests/rustdoc-ui/bad-render-options.rs index 0522f68cb6c2..c85a818511d7 100644 --- a/tests/rustdoc-ui/bad-render-options.rs +++ b/tests/rustdoc-ui/bad-render-options.rs @@ -1,29 +1,30 @@ // regression test for https://github.com/rust-lang/rust/issues/149187 +#![deny(invalid_doc_attributes)] #![doc(html_favicon_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_favicon_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_logo_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_logo_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_playground_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `html_playground_url = "..."` +//~^ ERROR +//~| WARN #![doc(issue_tracker_base_url)] -//~^ ERROR: malformed `doc` attribute -//~| NOTE expected this to be of the form `issue_tracker_base_url = "..."` +//~^ ERROR +//~| WARN #![doc(html_favicon_url = 1)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_logo_url = 2)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_playground_url = 3)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(issue_tracker_base_url = 4)] -//~^ ERROR malformed `doc` attribute -//~| NOTE expected a string literal +//~^ ERROR +//~| WARN #![doc(html_no_source = "asdf")] -//~^ ERROR malformed `doc` attribute -//~| NOTE didn't expect any arguments here +//~^ ERROR +//~| WARN diff --git a/tests/rustdoc-ui/bad-render-options.stderr b/tests/rustdoc-ui/bad-render-options.stderr index 28d4533a6edb..dac6bc231c37 100644 --- a/tests/rustdoc-ui/bad-render-options.stderr +++ b/tests/rustdoc-ui/bad-render-options.stderr @@ -1,76 +1,79 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:3:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:4:8 | LL | #![doc(html_favicon_url)] - | ^^^^^^^----------------^^ - | | - | expected this to be of the form `html_favicon_url = "..."` + | ^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/bad-render-options.rs:2:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:6:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:7:8 | LL | #![doc(html_logo_url)] - | ^^^^^^^-------------^^ - | | - | expected this to be of the form `html_logo_url = "..."` + | ^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:9:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:10:8 | LL | #![doc(html_playground_url)] - | ^^^^^^^-------------------^^ - | | - | expected this to be of the form `html_playground_url = "..."` + | ^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:12:1 +error: expected this to be of the form `... = "..."` + --> $DIR/bad-render-options.rs:13:8 | LL | #![doc(issue_tracker_base_url)] - | ^^^^^^^----------------------^^ - | | - | expected this to be of the form `issue_tracker_base_url = "..."` + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:15:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:16:27 | LL | #![doc(html_favicon_url = 1)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:18:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:19:24 | LL | #![doc(html_logo_url = 2)] - | ^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:21:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:22:30 | LL | #![doc(html_playground_url = 3)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0539]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:24:1 +error: malformed `doc` attribute input + --> $DIR/bad-render-options.rs:25:33 | LL | #![doc(issue_tracker_base_url = 4)] - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^-^^ - | | - | expected a string literal here + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error[E0565]: malformed `doc` attribute input - --> $DIR/bad-render-options.rs:27:1 +error: didn't expect any arguments here + --> $DIR/bad-render-options.rs:28:23 | LL | #![doc(html_no_source = "asdf")] - | ^^^^^^^^^^^^^^^^^^^^^^--------^^ - | | - | didn't expect any arguments here + | ^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! error: aborting due to 9 previous errors -Some errors have detailed explanations: E0539, E0565. -For more information about an error, try `rustc --explain E0539`. diff --git a/tests/rustdoc-ui/doc-cfg.rs b/tests/rustdoc-ui/doc-cfg.rs index f30d80aa9cda..abaea9719280 100644 --- a/tests/rustdoc-ui/doc-cfg.rs +++ b/tests/rustdoc-ui/doc-cfg.rs @@ -1,9 +1,10 @@ +#![deny(invalid_doc_attributes)] #![feature(doc_cfg)] #[doc(cfg(), cfg(foo, bar))] -//~^ ERROR malformed `doc` attribute input -//~| ERROR malformed `doc` attribute input +//~^ ERROR +//~| ERROR #[doc(cfg())] //~ ERROR #[doc(cfg(foo, bar))] //~ ERROR -#[doc(auto_cfg(hide(foo::bar)))] //~ ERROR +#[doc(auto_cfg(hide(foo::bar)))] pub fn foo() {} diff --git a/tests/rustdoc-ui/doc-cfg.stderr b/tests/rustdoc-ui/doc-cfg.stderr index ce16ec31d875..fa25a441e9b7 100644 --- a/tests/rustdoc-ui/doc-cfg.stderr +++ b/tests/rustdoc-ui/doc-cfg.stderr @@ -1,5 +1,5 @@ error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:3:1 + --> $DIR/doc-cfg.rs:4:1 | LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^--^^^^^^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | #[doc(cfg(), cfg(foo, bar))] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:3:1 + --> $DIR/doc-cfg.rs:4:1 | LL | #[doc(cfg(), cfg(foo, bar))] | ^^^^^^^^^^^^^^^^----------^^ @@ -15,7 +15,7 @@ LL | #[doc(cfg(), cfg(foo, bar))] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:6:1 + --> $DIR/doc-cfg.rs:7:1 | LL | #[doc(cfg())] | ^^^^^^^^^--^^ @@ -23,22 +23,13 @@ LL | #[doc(cfg())] | expected a single argument here error[E0805]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:7:1 + --> $DIR/doc-cfg.rs:8:1 | LL | #[doc(cfg(foo, bar))] | ^^^^^^^^^----------^^ | | | expected a single argument here -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-cfg.rs:8:1 - | -LL | #[doc(auto_cfg(hide(foo::bar)))] - | ^^^^^^^^^^^^^^^^^^^^--------^^^^ - | | - | expected a valid identifier here +error: aborting due to 4 previous errors -error: aborting due to 5 previous errors - -Some errors have detailed explanations: E0539, E0805. -For more information about an error, try `rustc --explain E0539`. +For more information about this error, try `rustc --explain E0805`. diff --git a/tests/rustdoc-ui/lints/doc-attr.rs b/tests/rustdoc-ui/lints/doc-attr.rs index f04961aa930a..46d56e7f5962 100644 --- a/tests/rustdoc-ui/lints/doc-attr.rs +++ b/tests/rustdoc-ui/lints/doc-attr.rs @@ -2,11 +2,11 @@ #![deny(invalid_doc_attributes)] #[doc(123)] -//~^ ERROR malformed `doc` attribute +//~^ ERROR //~| WARN #[doc("hello", "bar")] -//~^ ERROR malformed `doc` attribute -//~| ERROR malformed `doc` attribute +//~^ ERROR +//~| ERROR //~| WARN //~| WARN fn bar() {} diff --git a/tests/rustdoc-ui/lints/doc-attr.stderr b/tests/rustdoc-ui/lints/doc-attr.stderr index 5b6f48acb3be..263b068e092d 100644 --- a/tests/rustdoc-ui/lints/doc-attr.stderr +++ b/tests/rustdoc-ui/lints/doc-attr.stderr @@ -1,4 +1,4 @@ -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/doc-attr.rs:4:7 | LL | #[doc(123)] @@ -11,7 +11,7 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/doc-attr.rs:7:7 | LL | #[doc("hello", "bar")] @@ -19,7 +19,7 @@ LL | #[doc("hello", "bar")] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/doc-attr.rs:7:16 | LL | #[doc("hello", "bar")] diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr index bfd926a2a517..2457352bb342 100644 --- a/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-2.stderr @@ -1,4 +1,4 @@ -error: malformed `doc` attribute input +error: expected this to be of the form `... = "..."` --> $DIR/invalid-doc-attr-2.rs:3:8 | LL | #![doc("other attribute")] diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs b/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs new file mode 100644 index 000000000000..1d2e4445140a --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-3.rs @@ -0,0 +1,22 @@ +#![deny(invalid_doc_attributes)] + +#![doc(test(no_crate_inject = 1))] +//~^ ERROR +//~| WARN +#![doc(test(attr = 1))] +//~^ ERROR +//~| WARN + +#[doc(hidden = true)] +//~^ ERROR +//~| WARN +#[doc(hidden("or you will be fired"))] +//~^ ERROR +//~| WARN +#[doc(hidden = "handled transparently by codegen")] +//~^ ERROR +//~| WARN +#[doc = 1] +//~^ ERROR +//~| WARN +pub struct X; diff --git a/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr b/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr new file mode 100644 index 000000000000..9cec930174ce --- /dev/null +++ b/tests/rustdoc-ui/lints/invalid-doc-attr-3.stderr @@ -0,0 +1,55 @@ +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:10:14 + | +LL | #[doc(hidden = true)] + | ^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/invalid-doc-attr-3.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:13:13 + | +LL | #[doc(hidden("or you will be fired"))] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:16:14 + | +LL | #[doc(hidden = "handled transparently by codegen")] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-3.rs:19:9 + | +LL | #[doc = 1] + | ^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: didn't expect any arguments here + --> $DIR/invalid-doc-attr-3.rs:3:29 + | +LL | #![doc(test(no_crate_inject = 1))] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: malformed `doc` attribute input + --> $DIR/invalid-doc-attr-3.rs:6:1 + | +LL | #![doc(test(attr = 1))] + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: aborting due to 6 previous errors + diff --git a/tests/ui/attributes/doc-attr.rs b/tests/ui/attributes/doc-attr.rs index b45f211f6222..8c81bc18d6ae 100644 --- a/tests/ui/attributes/doc-attr.rs +++ b/tests/ui/attributes/doc-attr.rs @@ -8,10 +8,13 @@ pub fn foo() {} #[doc(123)] -//~^ ERROR malformed `doc` attribute +//~^ ERROR +//~| WARN #[doc("hello", "bar")] -//~^ ERROR malformed `doc` attribute -//~| ERROR malformed `doc` attribute +//~^ ERROR +//~| ERROR +//~| WARN +//~| WARN #[doc(foo::bar, crate::bar::baz = "bye")] //~^ ERROR unknown `doc` attribute //~| ERROR unknown `doc` attribute diff --git a/tests/ui/attributes/doc-attr.stderr b/tests/ui/attributes/doc-attr.stderr index 005810fa23fa..79d9fb5bea7e 100644 --- a/tests/ui/attributes/doc-attr.stderr +++ b/tests/ui/attributes/doc-attr.stderr @@ -1,27 +1,3 @@ -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:10:1 - | -LL | #[doc(123)] - | ^^^^^^---^^ - | | - | expected this to be of the form `... = "..."` - -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:12:1 - | -LL | #[doc("hello", "bar")] - | ^^^^^^-------^^^^^^^^^ - | | - | expected this to be of the form `... = "..."` - -error[E0539]: malformed `doc` attribute input - --> $DIR/doc-attr.rs:12:1 - | -LL | #[doc("hello", "bar")] - | ^^^^^^^^^^^^^^^-----^^ - | | - | expected this to be of the form `... = "..."` - error: unknown `doc` attribute `as_ptr` --> $DIR/doc-attr.rs:6:7 | @@ -34,14 +10,38 @@ note: the lint level is defined here LL | #![deny(invalid_doc_attributes)] | ^^^^^^^^^^^^^^^^^^^^^^ +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:10:7 + | +LL | #[doc(123)] + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:13:7 + | +LL | #[doc("hello", "bar")] + | ^^^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + +error: expected this to be of the form `... = "..."` + --> $DIR/doc-attr.rs:13:16 + | +LL | #[doc("hello", "bar")] + | ^^^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + error: unknown `doc` attribute `foo::bar` - --> $DIR/doc-attr.rs:15:7 + --> $DIR/doc-attr.rs:18:7 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^ error: unknown `doc` attribute `crate::bar::baz` - --> $DIR/doc-attr.rs:15:17 + --> $DIR/doc-attr.rs:18:17 | LL | #[doc(foo::bar, crate::bar::baz = "bye")] | ^^^^^^^^^^^^^^^ @@ -54,4 +54,3 @@ LL | #![doc(as_ptr)] error: aborting due to 7 previous errors -For more information about this error, try `rustc --explain E0539`. diff --git a/tests/ui/attributes/doc-test-literal.rs b/tests/ui/attributes/doc-test-literal.rs index f9776e9924bd..ceb8967ea229 100644 --- a/tests/ui/attributes/doc-test-literal.rs +++ b/tests/ui/attributes/doc-test-literal.rs @@ -1,4 +1,7 @@ +#![deny(invalid_doc_attributes)] + #![doc(test(""))] -//~^ ERROR malformed `doc` attribute input +//~^ ERROR +//~| WARN fn main() {} diff --git a/tests/ui/attributes/doc-test-literal.stderr b/tests/ui/attributes/doc-test-literal.stderr index 2d70d5d206f0..c26aa94ec1af 100644 --- a/tests/ui/attributes/doc-test-literal.stderr +++ b/tests/ui/attributes/doc-test-literal.stderr @@ -1,11 +1,15 @@ -error[E0565]: malformed `doc` attribute input - --> $DIR/doc-test-literal.rs:1:1 +error: malformed `doc` attribute input + --> $DIR/doc-test-literal.rs:3:13 | LL | #![doc(test(""))] - | ^^^^^^^^^^^^--^^^ - | | - | didn't expect a literal here + | ^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! +note: the lint level is defined here + --> $DIR/doc-test-literal.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: aborting due to 1 previous error -For more information about this error, try `rustc --explain E0565`. diff --git a/tests/ui/attributes/malformed-attrs.rs b/tests/ui/attributes/malformed-attrs.rs index b0d8fd774f74..6dc3086b63e1 100644 --- a/tests/ui/attributes/malformed-attrs.rs +++ b/tests/ui/attributes/malformed-attrs.rs @@ -1,5 +1,6 @@ // This file contains a bunch of malformed attributes. // We enable a bunch of features to not get feature-gate errs in this test. +#![deny(invalid_doc_attributes)] #![feature(rustc_attrs)] #![feature(rustc_allow_const_fn_unstable)] #![feature(allow_internal_unstable)] @@ -39,8 +40,7 @@ #[deprecated = 5] //~^ ERROR malformed #[doc] -//~^ ERROR valid forms for the attribute are -//~| WARN this was previously accepted by the compiler +//~^ ERROR #[rustc_macro_transparency] //~^ ERROR malformed //~| ERROR attribute cannot be used on @@ -75,8 +75,7 @@ //~^ ERROR malformed //~| WARN crate-level attribute should be an inner attribute #[doc] -//~^ ERROR valid forms for the attribute are -//~| WARN this was previously accepted by the compiler +//~^ ERROR #[target_feature] //~^ ERROR malformed #[export_stable = 1] diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index f817a0b0d91b..22e222efa435 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `cfg` attribute input - --> $DIR/malformed-attrs.rs:108:1 + --> $DIR/malformed-attrs.rs:107:1 | LL | #[cfg] | ^^^^^^ @@ -10,7 +10,7 @@ LL | #[cfg] = note: for more information, visit error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-attrs.rs:110:1 + --> $DIR/malformed-attrs.rs:109:1 | LL | #[cfg_attr] | ^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL | #[cfg_attr] = note: for more information, visit error[E0463]: can't find crate for `wloop` - --> $DIR/malformed-attrs.rs:218:1 + --> $DIR/malformed-attrs.rs:217:1 | LL | extern crate wloop; | ^^^^^^^^^^^^^^^^^^^ can't find crate error: malformed `allow` attribute input - --> $DIR/malformed-attrs.rs:184:1 + --> $DIR/malformed-attrs.rs:183:1 | LL | #[allow] | ^^^^^^^^ @@ -43,7 +43,7 @@ LL | #[allow(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `expect` attribute input - --> $DIR/malformed-attrs.rs:186:1 + --> $DIR/malformed-attrs.rs:185:1 | LL | #[expect] | ^^^^^^^^^ @@ -59,7 +59,7 @@ LL | #[expect(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `warn` attribute input - --> $DIR/malformed-attrs.rs:188:1 + --> $DIR/malformed-attrs.rs:187:1 | LL | #[warn] | ^^^^^^^ @@ -75,7 +75,7 @@ LL | #[warn(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `deny` attribute input - --> $DIR/malformed-attrs.rs:190:1 + --> $DIR/malformed-attrs.rs:189:1 | LL | #[deny] | ^^^^^^^ @@ -91,7 +91,7 @@ LL | #[deny(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: malformed `forbid` attribute input - --> $DIR/malformed-attrs.rs:192:1 + --> $DIR/malformed-attrs.rs:191:1 | LL | #[forbid] | ^^^^^^^^^ @@ -107,25 +107,25 @@ LL | #[forbid(lint1, lint2, lint3, reason = "...")] | +++++++++++++++++++++++++++++++++++++ error: the `#[proc_macro]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:105:1 + --> $DIR/malformed-attrs.rs:104:1 | LL | #[proc_macro = 18] | ^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_attribute]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:122:1 + --> $DIR/malformed-attrs.rs:121:1 | LL | #[proc_macro_attribute = 19] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: the `#[proc_macro_derive]` attribute is only usable with crates of the `proc-macro` crate type - --> $DIR/malformed-attrs.rs:129:1 + --> $DIR/malformed-attrs.rs:128:1 | LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ error[E0658]: allow_internal_unsafe side-steps the unsafe_code lint - --> $DIR/malformed-attrs.rs:223:1 + --> $DIR/malformed-attrs.rs:222:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -134,7 +134,7 @@ LL | #[allow_internal_unsafe = 1] = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date error[E0539]: malformed `windows_subsystem` attribute input - --> $DIR/malformed-attrs.rs:26:1 + --> $DIR/malformed-attrs.rs:27:1 | LL | #![windows_subsystem] | ^^^-----------------^ @@ -150,25 +150,25 @@ LL | #![windows_subsystem = "windows"] | +++++++++++ error[E0539]: malformed `export_name` attribute input - --> $DIR/malformed-attrs.rs:29:1 + --> $DIR/malformed-attrs.rs:30:1 | LL | #[unsafe(export_name)] | ^^^^^^^^^^^^^^^^^^^^^^ help: must be of the form: `#[export_name = "name"]` error: `rustc_allow_const_fn_unstable` expects a list of feature names - --> $DIR/malformed-attrs.rs:31:1 + --> $DIR/malformed-attrs.rs:32:1 | LL | #[rustc_allow_const_fn_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: `allow_internal_unstable` expects a list of feature names - --> $DIR/malformed-attrs.rs:34:1 + --> $DIR/malformed-attrs.rs:35:1 | LL | #[allow_internal_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0539]: malformed `rustc_confusables` attribute input - --> $DIR/malformed-attrs.rs:36:1 + --> $DIR/malformed-attrs.rs:37:1 | LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ @@ -177,7 +177,7 @@ LL | #[rustc_confusables] | help: must be of the form: `#[rustc_confusables("name1", "name2", ...)]` error: `#[rustc_confusables]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:36:1 + --> $DIR/malformed-attrs.rs:37:1 | LL | #[rustc_confusables] | ^^^^^^^^^^^^^^^^^^^^ @@ -185,7 +185,7 @@ LL | #[rustc_confusables] = help: `#[rustc_confusables]` can only be applied to inherent methods error[E0539]: malformed `deprecated` attribute input - --> $DIR/malformed-attrs.rs:39:1 + --> $DIR/malformed-attrs.rs:40:1 | LL | #[deprecated = 5] | ^^^^^^^^^^^^^^^-^ @@ -349,7 +349,7 @@ LL | #[crate_name] | ^^^^^^^^^^^^^ help: must be of the form: `#[crate_name = "name"]` error[E0539]: malformed `target_feature` attribute input - --> $DIR/malformed-attrs.rs:80:1 + --> $DIR/malformed-attrs.rs:79:1 | LL | #[target_feature] | ^^^^^^^^^^^^^^^^^ @@ -358,7 +358,7 @@ LL | #[target_feature] | help: must be of the form: `#[target_feature(enable = "feat1, feat2")]` error[E0565]: malformed `export_stable` attribute input - --> $DIR/malformed-attrs.rs:82:1 + --> $DIR/malformed-attrs.rs:81:1 | LL | #[export_stable = 1] | ^^^^^^^^^^^^^^^^---^ @@ -367,7 +367,7 @@ LL | #[export_stable = 1] | help: must be of the form: `#[export_stable]` error[E0539]: malformed `link` attribute input - --> $DIR/malformed-attrs.rs:84:1 + --> $DIR/malformed-attrs.rs:83:1 | LL | #[link] | ^^^^^^^ expected this to be a list @@ -375,7 +375,7 @@ LL | #[link] = note: for more information, visit error[E0539]: malformed `link_name` attribute input - --> $DIR/malformed-attrs.rs:88:1 + --> $DIR/malformed-attrs.rs:87:1 | LL | #[link_name] | ^^^^^^^^^^^^ help: must be of the form: `#[link_name = "name"]` @@ -383,7 +383,7 @@ LL | #[link_name] = note: for more information, visit error[E0539]: malformed `link_section` attribute input - --> $DIR/malformed-attrs.rs:92:1 + --> $DIR/malformed-attrs.rs:91:1 | LL | #[link_section] | ^^^^^^^^^^^^^^^ help: must be of the form: `#[link_section = "name"]` @@ -391,7 +391,7 @@ LL | #[link_section] = note: for more information, visit error[E0539]: malformed `coverage` attribute input - --> $DIR/malformed-attrs.rs:94:1 + --> $DIR/malformed-attrs.rs:93:1 | LL | #[coverage] | ^^^^^^^^^^^ this attribute is only valid with either `on` or `off` as an argument @@ -404,13 +404,13 @@ LL | #[coverage(on)] | ++++ error[E0539]: malformed `sanitize` attribute input - --> $DIR/malformed-attrs.rs:96:1 + --> $DIR/malformed-attrs.rs:95:1 | LL | #[sanitize] | ^^^^^^^^^^^ expected this to be a list error[E0565]: malformed `no_implicit_prelude` attribute input - --> $DIR/malformed-attrs.rs:101:1 + --> $DIR/malformed-attrs.rs:100:1 | LL | #[no_implicit_prelude = 23] | ^^^^^^^^^^^^^^^^^^^^^^----^ @@ -419,7 +419,7 @@ LL | #[no_implicit_prelude = 23] | help: must be of the form: `#[no_implicit_prelude]` error[E0565]: malformed `proc_macro` attribute input - --> $DIR/malformed-attrs.rs:105:1 + --> $DIR/malformed-attrs.rs:104:1 | LL | #[proc_macro = 18] | ^^^^^^^^^^^^^----^ @@ -428,7 +428,7 @@ LL | #[proc_macro = 18] | help: must be of the form: `#[proc_macro]` error[E0539]: malformed `instruction_set` attribute input - --> $DIR/malformed-attrs.rs:112:1 + --> $DIR/malformed-attrs.rs:111:1 | LL | #[instruction_set] | ^^^^^^^^^^^^^^^^^^ @@ -439,7 +439,7 @@ LL | #[instruction_set] = note: for more information, visit error[E0539]: malformed `patchable_function_entry` attribute input - --> $DIR/malformed-attrs.rs:114:1 + --> $DIR/malformed-attrs.rs:113:1 | LL | #[patchable_function_entry] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -448,7 +448,7 @@ LL | #[patchable_function_entry] | help: must be of the form: `#[patchable_function_entry(prefix_nops = m, entry_nops = n)]` error[E0565]: malformed `coroutine` attribute input - --> $DIR/malformed-attrs.rs:117:5 + --> $DIR/malformed-attrs.rs:116:5 | LL | #[coroutine = 63] || {} | ^^^^^^^^^^^^----^ @@ -457,7 +457,7 @@ LL | #[coroutine = 63] || {} | help: must be of the form: `#[coroutine]` error[E0565]: malformed `proc_macro_attribute` attribute input - --> $DIR/malformed-attrs.rs:122:1 + --> $DIR/malformed-attrs.rs:121:1 | LL | #[proc_macro_attribute = 19] | ^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -466,7 +466,7 @@ LL | #[proc_macro_attribute = 19] | help: must be of the form: `#[proc_macro_attribute]` error[E0539]: malformed `must_use` attribute input - --> $DIR/malformed-attrs.rs:125:1 + --> $DIR/malformed-attrs.rs:124:1 | LL | #[must_use = 1] | ^^^^^^^^^^^^^-^ @@ -484,7 +484,7 @@ LL + #[must_use] | error[E0539]: malformed `proc_macro_derive` attribute input - --> $DIR/malformed-attrs.rs:129:1 + --> $DIR/malformed-attrs.rs:128:1 | LL | #[proc_macro_derive] | ^^^^^^^^^^^^^^^^^^^^ expected this to be a list @@ -498,7 +498,7 @@ LL | #[proc_macro_derive(TraitName, attributes(name1, name2, ...))] | ++++++++++++++++++++++++++++++++++++++++++ error[E0539]: malformed `rustc_layout_scalar_valid_range_start` attribute input - --> $DIR/malformed-attrs.rs:134:1 + --> $DIR/malformed-attrs.rs:133:1 | LL | #[rustc_layout_scalar_valid_range_start] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -507,7 +507,7 @@ LL | #[rustc_layout_scalar_valid_range_start] | help: must be of the form: `#[rustc_layout_scalar_valid_range_start(start)]` error[E0539]: malformed `rustc_layout_scalar_valid_range_end` attribute input - --> $DIR/malformed-attrs.rs:136:1 + --> $DIR/malformed-attrs.rs:135:1 | LL | #[rustc_layout_scalar_valid_range_end] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -516,7 +516,7 @@ LL | #[rustc_layout_scalar_valid_range_end] | help: must be of the form: `#[rustc_layout_scalar_valid_range_end(end)]` error[E0539]: malformed `must_not_suspend` attribute input - --> $DIR/malformed-attrs.rs:138:1 + --> $DIR/malformed-attrs.rs:137:1 | LL | #[must_not_suspend()] | ^^^^^^^^^^^^^^^^^^--^ @@ -532,7 +532,7 @@ LL + #[must_not_suspend] | error[E0539]: malformed `cfi_encoding` attribute input - --> $DIR/malformed-attrs.rs:140:1 + --> $DIR/malformed-attrs.rs:139:1 | LL | #[cfi_encoding = ""] | ^^^^^^^^^^^^^^^^^--^ @@ -541,7 +541,7 @@ LL | #[cfi_encoding = ""] | help: must be of the form: `#[cfi_encoding = "encoding"]` error[E0565]: malformed `marker` attribute input - --> $DIR/malformed-attrs.rs:161:1 + --> $DIR/malformed-attrs.rs:160:1 | LL | #[marker = 3] | ^^^^^^^^^---^ @@ -550,7 +550,7 @@ LL | #[marker = 3] | help: must be of the form: `#[marker]` error[E0565]: malformed `fundamental` attribute input - --> $DIR/malformed-attrs.rs:163:1 + --> $DIR/malformed-attrs.rs:162:1 | LL | #[fundamental()] | ^^^^^^^^^^^^^--^ @@ -559,7 +559,7 @@ LL | #[fundamental()] | help: must be of the form: `#[fundamental]` error[E0565]: malformed `ffi_pure` attribute input - --> $DIR/malformed-attrs.rs:171:5 + --> $DIR/malformed-attrs.rs:170:5 | LL | #[unsafe(ffi_pure = 1)] | ^^^^^^^^^^^^^^^^^^---^^ @@ -568,7 +568,7 @@ LL | #[unsafe(ffi_pure = 1)] | help: must be of the form: `#[ffi_pure]` error[E0539]: malformed `link_ordinal` attribute input - --> $DIR/malformed-attrs.rs:173:5 + --> $DIR/malformed-attrs.rs:172:5 | LL | #[link_ordinal] | ^^^^^^^^^^^^^^^ @@ -579,7 +579,7 @@ LL | #[link_ordinal] = note: for more information, visit error[E0565]: malformed `ffi_const` attribute input - --> $DIR/malformed-attrs.rs:177:5 + --> $DIR/malformed-attrs.rs:176:5 | LL | #[unsafe(ffi_const = 1)] | ^^^^^^^^^^^^^^^^^^^---^^ @@ -588,13 +588,13 @@ LL | #[unsafe(ffi_const = 1)] | help: must be of the form: `#[ffi_const]` error[E0539]: malformed `linkage` attribute input - --> $DIR/malformed-attrs.rs:179:5 + --> $DIR/malformed-attrs.rs:178:5 | LL | #[linkage] | ^^^^^^^^^^ expected this to be of the form `linkage = "..."` error[E0539]: malformed `debugger_visualizer` attribute input - --> $DIR/malformed-attrs.rs:194:1 + --> $DIR/malformed-attrs.rs:193:1 | LL | #[debugger_visualizer] | ^^^^^^^^^^^^^^^^^^^^^^ @@ -605,7 +605,7 @@ LL | #[debugger_visualizer] = note: for more information, visit error[E0565]: malformed `automatically_derived` attribute input - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^----^ @@ -614,7 +614,7 @@ LL | #[automatically_derived = 18] | help: must be of the form: `#[automatically_derived]` error[E0565]: malformed `non_exhaustive` attribute input - --> $DIR/malformed-attrs.rs:204:1 + --> $DIR/malformed-attrs.rs:203:1 | LL | #[non_exhaustive = 1] | ^^^^^^^^^^^^^^^^^---^ @@ -623,7 +623,7 @@ LL | #[non_exhaustive = 1] | help: must be of the form: `#[non_exhaustive]` error[E0565]: malformed `thread_local` attribute input - --> $DIR/malformed-attrs.rs:210:1 + --> $DIR/malformed-attrs.rs:209:1 | LL | #[thread_local()] | ^^^^^^^^^^^^^^--^ @@ -632,7 +632,7 @@ LL | #[thread_local()] | help: must be of the form: `#[thread_local]` error[E0565]: malformed `no_link` attribute input - --> $DIR/malformed-attrs.rs:214:1 + --> $DIR/malformed-attrs.rs:213:1 | LL | #[no_link()] | ^^^^^^^^^--^ @@ -641,7 +641,7 @@ LL | #[no_link()] | help: must be of the form: `#[no_link]` error[E0539]: malformed `macro_use` attribute input - --> $DIR/malformed-attrs.rs:216:1 + --> $DIR/malformed-attrs.rs:215:1 | LL | #[macro_use = 1] | ^^^^^^^^^^^^---^ @@ -659,7 +659,7 @@ LL + #[macro_use] | error[E0539]: malformed `macro_export` attribute input - --> $DIR/malformed-attrs.rs:221:1 + --> $DIR/malformed-attrs.rs:220:1 | LL | #[macro_export = 18] | ^^^^^^^^^^^^^^^----^ @@ -676,7 +676,7 @@ LL + #[macro_export] | error[E0565]: malformed `allow_internal_unsafe` attribute input - --> $DIR/malformed-attrs.rs:223:1 + --> $DIR/malformed-attrs.rs:222:1 | LL | #[allow_internal_unsafe = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^---^ @@ -685,7 +685,7 @@ LL | #[allow_internal_unsafe = 1] | help: must be of the form: `#[allow_internal_unsafe]` error[E0565]: malformed `type_const` attribute input - --> $DIR/malformed-attrs.rs:149:5 + --> $DIR/malformed-attrs.rs:148:5 | LL | #[type_const = 1] | ^^^^^^^^^^^^^---^ @@ -694,7 +694,7 @@ LL | #[type_const = 1] | help: must be of the form: `#[type_const]` error: attribute should be applied to `const fn` - --> $DIR/malformed-attrs.rs:31:1 + --> $DIR/malformed-attrs.rs:32:1 | LL | #[rustc_allow_const_fn_unstable] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -706,7 +706,7 @@ LL | | } | |_- not a `const fn` warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/malformed-attrs.rs:84:1 + --> $DIR/malformed-attrs.rs:83:1 | LL | #[link] | ^^^^^^^ @@ -733,7 +733,7 @@ LL | #[repr] | ^^^^^^^ warning: missing options for `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:144:1 + --> $DIR/malformed-attrs.rs:143:1 | LL | #[diagnostic::on_unimplemented] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -742,7 +742,7 @@ LL | #[diagnostic::on_unimplemented] = note: `#[warn(malformed_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default warning: malformed `on_unimplemented` attribute - --> $DIR/malformed-attrs.rs:146:1 + --> $DIR/malformed-attrs.rs:145:1 | LL | #[diagnostic::on_unimplemented = 1] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ invalid option found here @@ -750,14 +750,16 @@ LL | #[diagnostic::on_unimplemented = 1] = help: only `message`, `note` and `label` are allowed as options error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:41:1 + --> $DIR/malformed-attrs.rs:42:1 | LL | #[doc] | ^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default +note: the lint level is defined here + --> $DIR/malformed-attrs.rs:3:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` --> $DIR/malformed-attrs.rs:52:1 @@ -767,6 +769,7 @@ LL | #[inline = 5] | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default warning: crate-level attribute should be an inner attribute: add an exclamation mark: `#![crate_name]` --> $DIR/malformed-attrs.rs:74:1 @@ -775,7 +778,7 @@ LL | #[crate_name] | ^^^^^^^^^^^^^ | note: this attribute does not have an `!`, which means it is applied to this function - --> $DIR/malformed-attrs.rs:116:1 + --> $DIR/malformed-attrs.rs:115:1 | LL | / fn test() { LL | | #[coroutine = 63] || {} @@ -788,12 +791,9 @@ error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, ` | LL | #[doc] | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 warning: `#[link_name]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:88:1 + --> $DIR/malformed-attrs.rs:87:1 | LL | #[link_name] | ^^^^^^^^^^^^ @@ -802,7 +802,7 @@ LL | #[link_name] = help: `#[link_name]` can be applied to foreign functions and foreign statics error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:98:1 + --> $DIR/malformed-attrs.rs:97:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -811,7 +811,7 @@ LL | #[ignore()] = note: for more information, see issue #57571 warning: `#[no_implicit_prelude]` attribute cannot be used on functions - --> $DIR/malformed-attrs.rs:101:1 + --> $DIR/malformed-attrs.rs:100:1 | LL | #[no_implicit_prelude = 23] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -820,13 +820,13 @@ LL | #[no_implicit_prelude = 23] = help: `#[no_implicit_prelude]` can be applied to crates and modules warning: `#[diagnostic::do_not_recommend]` does not expect any arguments - --> $DIR/malformed-attrs.rs:155:1 + --> $DIR/malformed-attrs.rs:154:1 | LL | #[diagnostic::do_not_recommend()] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: `#[automatically_derived]` attribute cannot be used on modules - --> $DIR/malformed-attrs.rs:196:1 + --> $DIR/malformed-attrs.rs:195:1 | LL | #[automatically_derived = 18] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -835,7 +835,7 @@ LL | #[automatically_derived = 18] = help: `#[automatically_derived]` can only be applied to trait impl blocks error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:230:1 + --> $DIR/malformed-attrs.rs:229:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ @@ -844,7 +844,7 @@ LL | #[ignore = 1] = note: for more information, see issue #57571 error[E0308]: mismatched types - --> $DIR/malformed-attrs.rs:117:23 + --> $DIR/malformed-attrs.rs:116:23 | LL | fn test() { | - help: a return type might be missing here: `-> _` @@ -852,24 +852,13 @@ LL | #[coroutine = 63] || {} | ^^^^^ expected `()`, found coroutine | = note: expected unit type `()` - found coroutine `{coroutine@$DIR/malformed-attrs.rs:117:23: 117:25}` + found coroutine `{coroutine@$DIR/malformed-attrs.rs:116:23: 116:25}` error: aborting due to 76 previous errors; 8 warnings emitted Some errors have detailed explanations: E0308, E0463, E0539, E0565, E0658, E0805. For more information about an error, try `rustc --explain E0308`. Future incompatibility report: Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:41:1 - | -LL | #[doc] - | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - -Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` --> $DIR/malformed-attrs.rs:52:1 | @@ -880,20 +869,9 @@ LL | #[inline = 5] = note: for more information, see issue #57571 = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default -Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-attrs.rs:77:1 - | -LL | #[doc] - | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:98:1 + --> $DIR/malformed-attrs.rs:97:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -904,7 +882,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-attrs.rs:230:1 + --> $DIR/malformed-attrs.rs:229:1 | LL | #[ignore = 1] | ^^^^^^^^^^^^^ diff --git a/tests/ui/malformed/malformed-regressions.rs b/tests/ui/malformed/malformed-regressions.rs index c0f8c0d15bb8..63b918520ec0 100644 --- a/tests/ui/malformed/malformed-regressions.rs +++ b/tests/ui/malformed/malformed-regressions.rs @@ -1,5 +1,6 @@ +#![deny(invalid_doc_attributes)] + #[doc] //~ ERROR valid forms for the attribute are -//~^ WARN this was previously accepted #[ignore()] //~ ERROR valid forms for the attribute are //~^ WARN this was previously accepted #[inline = ""] //~ ERROR valid forms for the attribute are diff --git a/tests/ui/malformed/malformed-regressions.stderr b/tests/ui/malformed/malformed-regressions.stderr index 2bf6ff3a9e7a..283834a48552 100644 --- a/tests/ui/malformed/malformed-regressions.stderr +++ b/tests/ui/malformed/malformed-regressions.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `link` attribute input - --> $DIR/malformed-regressions.rs:7:1 + --> $DIR/malformed-regressions.rs:8:1 | LL | #[link] | ^^^^^^^ expected this to be a list @@ -7,7 +7,7 @@ LL | #[link] = note: for more information, visit error[E0539]: malformed `link` attribute input - --> $DIR/malformed-regressions.rs:10:1 + --> $DIR/malformed-regressions.rs:11:1 | LL | #[link = ""] | ^^^^^^^----^ @@ -17,7 +17,7 @@ LL | #[link = ""] = note: for more information, visit warning: attribute should be applied to an `extern` block with non-Rust ABI - --> $DIR/malformed-regressions.rs:7:1 + --> $DIR/malformed-regressions.rs:8:1 | LL | #[link] | ^^^^^^^ @@ -29,26 +29,29 @@ LL | fn main() {} = note: requested on the command line with `-W unused-attributes` error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-regressions.rs:1:1 + --> $DIR/malformed-regressions.rs:3:1 | LL | #[doc] | ^^^^^^ | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default +note: the lint level is defined here + --> $DIR/malformed-regressions.rs:1:9 + | +LL | #![deny(invalid_doc_attributes)] + | ^^^^^^^^^^^^^^^^^^^^^^ error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-regressions.rs:3:1 + --> $DIR/malformed-regressions.rs:4:1 | LL | #[ignore()] | ^^^^^^^^^^^ | = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! = note: for more information, see issue #57571 + = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/malformed-regressions.rs:5:1 + --> $DIR/malformed-regressions.rs:6:1 | LL | #[inline = ""] | ^^^^^^^^^^^^^^ @@ -60,19 +63,8 @@ error: aborting due to 5 previous errors; 1 warning emitted For more information about this error, try `rustc --explain E0539`. Future incompatibility report: Future breakage diagnostic: -error: valid forms for the attribute are `#[doc = "string"]`, `#[doc(alias)]`, `#[doc(attribute)]`, `#[doc(auto_cfg)]`, `#[doc(cfg)]`, `#[doc(fake_variadic)]`, `#[doc(hidden)]`, `#[doc(html_favicon_url)]`, `#[doc(html_logo_url)]`, `#[doc(html_no_source)]`, `#[doc(html_playground_url)]`, `#[doc(html_root_url)]`, `#[doc(include)]`, `#[doc(inline)]`, `#[doc(issue_tracker_base_url)]`, `#[doc(keyword)]`, `#[doc(masked)]`, `#[doc(no_default_passes)]`, `#[doc(no_inline)]`, `#[doc(notable_trait)]`, `#[doc(passes)]`, `#[doc(plugins)]`, `#[doc(rust_logo)]`, `#[doc(search_unbox)]`, `#[doc(spotlight)]`, and `#[doc(test)]` - --> $DIR/malformed-regressions.rs:1:1 - | -LL | #[doc] - | ^^^^^^ - | - = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! - = note: for more information, see issue #57571 - = note: `#[deny(ill_formed_attribute_input)]` (part of `#[deny(future_incompatible)]`) on by default - -Future breakage diagnostic: error: valid forms for the attribute are `#[ignore = "reason"]` and `#[ignore]` - --> $DIR/malformed-regressions.rs:3:1 + --> $DIR/malformed-regressions.rs:4:1 | LL | #[ignore()] | ^^^^^^^^^^^ @@ -83,7 +75,7 @@ LL | #[ignore()] Future breakage diagnostic: error: valid forms for the attribute are `#[inline(always)]`, `#[inline(never)]`, and `#[inline]` - --> $DIR/malformed-regressions.rs:5:1 + --> $DIR/malformed-regressions.rs:6:1 | LL | #[inline = ""] | ^^^^^^^^^^^^^^ diff --git a/tests/ui/malformed/malformed-special-attrs.rs b/tests/ui/malformed/malformed-special-attrs.rs index 05b7ebe46666..f0e66ef0f835 100644 --- a/tests/ui/malformed/malformed-special-attrs.rs +++ b/tests/ui/malformed/malformed-special-attrs.rs @@ -1,3 +1,5 @@ +#![deny(invalid_doc_attributes)] + #[cfg_attr] //~ ERROR malformed `cfg_attr` attribute struct S1; diff --git a/tests/ui/malformed/malformed-special-attrs.stderr b/tests/ui/malformed/malformed-special-attrs.stderr index 91e5939eb1f9..a2501d2aa398 100644 --- a/tests/ui/malformed/malformed-special-attrs.stderr +++ b/tests/ui/malformed/malformed-special-attrs.stderr @@ -1,5 +1,5 @@ error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-special-attrs.rs:1:1 + --> $DIR/malformed-special-attrs.rs:3:1 | LL | #[cfg_attr] | ^^^^^^^^^^^ @@ -10,7 +10,7 @@ LL | #[cfg_attr] = note: for more information, visit error[E0539]: malformed `cfg_attr` attribute input - --> $DIR/malformed-special-attrs.rs:4:1 + --> $DIR/malformed-special-attrs.rs:6:1 | LL | #[cfg_attr = ""] | ^^^^^^^^^^^^^^^^ @@ -21,13 +21,13 @@ LL | #[cfg_attr = ""] = note: for more information, visit error: malformed `derive` attribute input - --> $DIR/malformed-special-attrs.rs:7:1 + --> $DIR/malformed-special-attrs.rs:9:1 | LL | #[derive] | ^^^^^^^^^ help: must be of the form: `#[derive(Trait1, Trait2, ...)]` error: malformed `derive` attribute input - --> $DIR/malformed-special-attrs.rs:10:1 + --> $DIR/malformed-special-attrs.rs:12:1 | LL | #[derive = ""] | ^^^^^^^^^^^^^^ help: must be of the form: `#[derive(Trait1, Trait2, ...)]` From 0f9b0d38410c1d17e6c8f3186595110a79702eb6 Mon Sep 17 00:00:00 2001 From: Jonathan Brouwer Date: Tue, 3 Feb 2026 21:50:59 +0100 Subject: [PATCH 318/319] Convert to inline diagnostics in `rustc_session` --- Cargo.lock | 1 - compiler/rustc_driver_impl/src/lib.rs | 1 - compiler/rustc_interface/src/interface.rs | 4 +- compiler/rustc_session/Cargo.toml | 1 - compiler/rustc_session/messages.ftl | 149 ----------------- compiler/rustc_session/src/errors.rs | 187 ++++++++++++---------- compiler/rustc_session/src/lib.rs | 2 - 7 files changed, 106 insertions(+), 239 deletions(-) delete mode 100644 compiler/rustc_session/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 1631d5362612..49563a61d2dc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4641,7 +4641,6 @@ dependencies = [ "rustc_data_structures", "rustc_errors", "rustc_feature", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hashes", "rustc_hir", diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 3059a4fefc61..268040d3cb7d 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -135,7 +135,6 @@ pub static DEFAULT_LOCALE_RESOURCES: &[&str] = &[ rustc_pattern_analysis::DEFAULT_LOCALE_RESOURCE, rustc_privacy::DEFAULT_LOCALE_RESOURCE, rustc_resolve::DEFAULT_LOCALE_RESOURCE, - rustc_session::DEFAULT_LOCALE_RESOURCE, rustc_trait_selection::DEFAULT_LOCALE_RESOURCE, rustc_ty_utils::DEFAULT_LOCALE_RESOURCE, // tidy-alphabetical-end diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index 916618cb5049..bb22bd20ac2a 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -55,7 +55,7 @@ pub(crate) fn parse_cfg(dcx: DiagCtxtHandle<'_>, cfgs: Vec) -> Cfg { cfgs.into_iter() .map(|s| { let psess = ParseSess::emitter_with_note( - vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); @@ -127,7 +127,7 @@ pub(crate) fn parse_check_cfg(dcx: DiagCtxtHandle<'_>, specs: Vec) -> Ch for s in specs { let psess = ParseSess::emitter_with_note( - vec![rustc_parse::DEFAULT_LOCALE_RESOURCE, rustc_session::DEFAULT_LOCALE_RESOURCE], + vec![rustc_parse::DEFAULT_LOCALE_RESOURCE], format!("this occurred on the command line: `--check-cfg={s}`"), ); let filename = FileName::cfg_spec_source_code(&s); diff --git a/compiler/rustc_session/Cargo.toml b/compiler/rustc_session/Cargo.toml index aebac3880d2f..d66e04f58106 100644 --- a/compiler/rustc_session/Cargo.toml +++ b/compiler/rustc_session/Cargo.toml @@ -12,7 +12,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } rustc_feature = { path = "../rustc_feature" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_session/messages.ftl b/compiler/rustc_session/messages.ftl deleted file mode 100644 index 5c851cb90a66..000000000000 --- a/compiler/rustc_session/messages.ftl +++ /dev/null @@ -1,149 +0,0 @@ -session_apple_deployment_target_invalid = - failed to parse deployment target specified in {$env_var}: {$error} - -session_apple_deployment_target_too_low = - deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min} - -session_binary_float_literal_not_supported = binary float literal is not supported -session_branch_protection_requires_aarch64 = `-Zbranch-protection` is only supported on aarch64 - -session_cannot_enable_crt_static_linux = sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static` - -session_cannot_mix_and_match_sanitizers = `-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}` - -session_cli_feature_diagnostic_help = - add `-Zcrate-attr="feature({$feature})"` to the command-line options to enable - -session_crate_name_empty = crate name must not be empty - -session_embed_source_insufficient_dwarf_version = `-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version} - -session_embed_source_requires_debug_info = `-Zembed-source=y` requires debug information to be enabled - -session_expr_parentheses_needed = parentheses are required to parse this as an expression - -session_failed_to_create_profiler = failed to create profiler: {$err} - -session_feature_diagnostic_for_issue = - see issue #{$n} for more information - -session_feature_diagnostic_help = - add `#![feature({$feature})]` to the crate attributes to enable - -session_feature_diagnostic_suggestion = - add `#![feature({$feature})]` to the crate attributes to enable - -session_feature_suggest_upgrade_compiler = - this compiler was built on {$date}; consider upgrading it if it is out of date - -session_file_is_not_writeable = output file {$file} is not writeable -- check its permissions - -session_file_write_fail = failed to write `{$path}` due to error `{$err}` - -session_function_return_requires_x86_or_x86_64 = `-Zfunction-return` (except `keep`) is only supported on x86 and x86_64 - -session_function_return_thunk_extern_requires_non_large_code_model = `-Zfunction-return=thunk-extern` is only supported on non-large code models - -session_hexadecimal_float_literal_not_supported = hexadecimal float literal is not supported - -session_incompatible_linker_flavor = linker flavor `{$flavor}` is incompatible with the current target - .note = compatible flavors are: {$compatible_list} - -session_indirect_branch_cs_prefix_requires_x86_or_x86_64 = `-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64 - -session_instrumentation_not_supported = {$us} instrumentation is not supported for this target - -session_int_literal_too_large = integer literal is too large - .note = value exceeds limit of `{$limit}` - -session_invalid_character_in_crate_name = invalid character {$character} in crate name: `{$crate_name}` - -session_invalid_float_literal_suffix = invalid suffix `{$suffix}` for float literal - .label = invalid suffix `{$suffix}` - .help = valid suffixes are `f32` and `f64` - -session_invalid_float_literal_width = invalid width `{$width}` for float literal - .help = valid widths are 32 and 64 - -session_invalid_int_literal_width = invalid width `{$width}` for integer literal - .help = valid widths are 8, 16, 32, 64 and 128 - -session_invalid_literal_suffix = suffixes on {$kind} literals are invalid - .label = invalid suffix `{$suffix}` - -session_invalid_num_literal_base_prefix = invalid base prefix for number literal - .note = base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase - .suggestion = try making the prefix lowercase - -session_invalid_num_literal_suffix = invalid suffix `{$suffix}` for number literal - .label = invalid suffix `{$suffix}` - .help = the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.) - -session_linker_plugin_lto_windows_not_supported = linker plugin based LTO is not supported together with `-C prefer-dynamic` when targeting Windows-like targets - -session_must_be_name_of_associated_function = must be a name of an associated function - -session_not_circumvent_feature = `-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature gates, except when testing error paths in the CTFE engine - -session_not_supported = not supported - -session_octal_float_literal_not_supported = octal float literal is not supported - -session_profile_sample_use_file_does_not_exist = file `{$path}` passed to `-C profile-sample-use` does not exist - -session_profile_use_file_does_not_exist = file `{$path}` passed to `-C profile-use` does not exist - -session_sanitizer_cfi_canonical_jump_tables_requires_cfi = `-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi` - -session_sanitizer_cfi_generalize_pointers_requires_cfi = `-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` - -session_sanitizer_cfi_normalize_integers_requires_cfi = `-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi` - -session_sanitizer_cfi_requires_lto = `-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto` - -session_sanitizer_cfi_requires_single_codegen_unit = `-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1` - -session_sanitizer_kcfi_arity_requires_kcfi = `-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi` - -session_sanitizer_kcfi_requires_panic_abort = `-Z sanitizer=kcfi` requires `-C panic=abort` - -session_sanitizer_not_supported = {$us} sanitizer is not supported for this target - -session_sanitizers_not_supported = {$us} sanitizers are not supported for this target - -session_skipping_const_checks = skipping const checks - -session_soft_float_deprecated = - `-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead - .note = it will be removed or ignored in a future version of Rust -session_soft_float_deprecated_issue = see issue #129893 for more information - -session_soft_float_ignored = - `-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets - .note = this may become a hard error in a future version of Rust - -session_split_debuginfo_unstable_platform = `-Csplit-debuginfo={$debuginfo}` is unstable on this platform - -session_split_lto_unit_requires_lto = `-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto` - -session_target_requires_unwind_tables = target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no` - -session_target_small_data_threshold_not_supported = `-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored - -session_target_stack_protector_not_supported = `-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored - -session_unexpected_builtin_cfg = unexpected `--cfg {$cfg}` flag - .controlled_by = config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}` - .incoherent = manually setting a built-in cfg can and does create incoherent behaviors - -session_unleashed_feature_help_named = skipping check for `{$gate}` feature -session_unleashed_feature_help_unnamed = skipping check that does not even have a feature gate - -session_unstable_virtual_function_elimination = `-Zvirtual-function-elimination` requires `-Clto` - -session_unsupported_dwarf_version = requested DWARF version {$dwarf_version} is not supported -session_unsupported_dwarf_version_help = supported DWARF versions are 2, 3, 4 and 5 - -session_unsupported_reg_struct_return_arch = `-Zreg-struct-return` is only supported on x86 -session_unsupported_regparm = `-Zregparm={$regparm}` is unsupported (valid values 0-3) -session_unsupported_regparm_arch = `-Zregparm=N` is only supported on x86 diff --git a/compiler/rustc_session/src/errors.rs b/compiler/rustc_session/src/errors.rs index 54e792fd7b59..0c6a33f22808 100644 --- a/compiler/rustc_session/src/errors.rs +++ b/compiler/rustc_session/src/errors.rs @@ -15,9 +15,11 @@ use crate::parse::ParseSess; #[derive(Diagnostic)] pub(crate) enum AppleDeploymentTarget { - #[diag(session_apple_deployment_target_invalid)] + #[diag("failed to parse deployment target specified in {$env_var}: {$error}")] Invalid { env_var: &'static str, error: ParseIntError }, - #[diag(session_apple_deployment_target_too_low)] + #[diag( + "deployment target in {$env_var} was set to {$version}, but the minimum supported by `rustc` is {$os_min}" + )] TooLow { env_var: &'static str, version: String, os_min: String }, } @@ -34,13 +36,13 @@ impl<'a, G: EmissionGuarantee> Diagnostic<'a, G> for FeatureGateError { } #[derive(Subdiagnostic)] -#[note(session_feature_diagnostic_for_issue)] +#[note("see issue #{$n} for more information")] pub(crate) struct FeatureDiagnosticForIssue { pub(crate) n: NonZero, } #[derive(Subdiagnostic)] -#[note(session_feature_suggest_upgrade_compiler)] +#[note("this compiler was built on {$date}; consider upgrading it if it is out of date")] pub(crate) struct SuggestUpgradeCompiler { date: &'static str, } @@ -58,14 +60,14 @@ impl SuggestUpgradeCompiler { } #[derive(Subdiagnostic)] -#[help(session_feature_diagnostic_help)] +#[help("add `#![feature({$feature})]` to the crate attributes to enable")] pub(crate) struct FeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Subdiagnostic)] #[suggestion( - session_feature_diagnostic_suggestion, + "add `#![feature({$feature})]` to the crate attributes to enable", applicability = "maybe-incorrect", code = "#![feature({feature})]\n" )] @@ -76,169 +78,181 @@ pub struct FeatureDiagnosticSuggestion { } #[derive(Subdiagnostic)] -#[help(session_cli_feature_diagnostic_help)] +#[help("add `-Zcrate-attr=\"feature({$feature})\"` to the command-line options to enable")] pub(crate) struct CliFeatureDiagnosticHelp { pub(crate) feature: Symbol, } #[derive(Diagnostic)] -#[diag(session_must_be_name_of_associated_function)] +#[diag("must be a name of an associated function")] pub struct MustBeNameOfAssociatedFunction { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(session_not_circumvent_feature)] +#[diag( + "`-Zunleash-the-miri-inside-of-you` may not be used to circumvent feature gates, except when testing error paths in the CTFE engine" +)] pub(crate) struct NotCircumventFeature; #[derive(Diagnostic)] -#[diag(session_linker_plugin_lto_windows_not_supported)] +#[diag( + "linker plugin based LTO is not supported together with `-C prefer-dynamic` when targeting Windows-like targets" +)] pub(crate) struct LinkerPluginToWindowsNotSupported; #[derive(Diagnostic)] -#[diag(session_profile_use_file_does_not_exist)] +#[diag("file `{$path}` passed to `-C profile-use` does not exist")] pub(crate) struct ProfileUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_profile_sample_use_file_does_not_exist)] +#[diag("file `{$path}` passed to `-C profile-sample-use` does not exist")] pub(crate) struct ProfileSampleUseFileDoesNotExist<'a> { pub(crate) path: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_target_requires_unwind_tables)] +#[diag("target requires unwind tables, they cannot be disabled with `-C force-unwind-tables=no`")] pub(crate) struct TargetRequiresUnwindTables; #[derive(Diagnostic)] -#[diag(session_instrumentation_not_supported)] +#[diag("{$us} instrumentation is not supported for this target")] pub(crate) struct InstrumentationNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_sanitizer_not_supported)] +#[diag("{$us} sanitizer is not supported for this target")] pub(crate) struct SanitizerNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_sanitizers_not_supported)] +#[diag("{$us} sanitizers are not supported for this target")] pub(crate) struct SanitizersNotSupported { pub(crate) us: String, } #[derive(Diagnostic)] -#[diag(session_cannot_mix_and_match_sanitizers)] +#[diag("`-Zsanitizer={$first}` is incompatible with `-Zsanitizer={$second}`")] pub(crate) struct CannotMixAndMatchSanitizers { pub(crate) first: String, pub(crate) second: String, } #[derive(Diagnostic)] -#[diag(session_cannot_enable_crt_static_linux)] +#[diag( + "sanitizer is incompatible with statically linked libc, disable it using `-C target-feature=-crt-static`" +)] pub(crate) struct CannotEnableCrtStaticLinux; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_requires_lto)] +#[diag("`-Zsanitizer=cfi` requires `-Clto` or `-Clinker-plugin-lto`")] pub(crate) struct SanitizerCfiRequiresLto; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_requires_single_codegen_unit)] +#[diag("`-Zsanitizer=cfi` with `-Clto` requires `-Ccodegen-units=1`")] pub(crate) struct SanitizerCfiRequiresSingleCodegenUnit; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_canonical_jump_tables_requires_cfi)] +#[diag("`-Zsanitizer-cfi-canonical-jump-tables` requires `-Zsanitizer=cfi`")] pub(crate) struct SanitizerCfiCanonicalJumpTablesRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_generalize_pointers_requires_cfi)] +#[diag("`-Zsanitizer-cfi-generalize-pointers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiGeneralizePointersRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_cfi_normalize_integers_requires_cfi)] +#[diag("`-Zsanitizer-cfi-normalize-integers` requires `-Zsanitizer=cfi` or `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerCfiNormalizeIntegersRequiresCfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_kcfi_arity_requires_kcfi)] +#[diag("`-Zsanitizer-kcfi-arity` requires `-Zsanitizer=kcfi`")] pub(crate) struct SanitizerKcfiArityRequiresKcfi; #[derive(Diagnostic)] -#[diag(session_sanitizer_kcfi_requires_panic_abort)] +#[diag("`-Z sanitizer=kcfi` requires `-C panic=abort`")] pub(crate) struct SanitizerKcfiRequiresPanicAbort; #[derive(Diagnostic)] -#[diag(session_split_lto_unit_requires_lto)] +#[diag("`-Zsplit-lto-unit` requires `-Clto`, `-Clto=thin`, or `-Clinker-plugin-lto`")] pub(crate) struct SplitLtoUnitRequiresLto; #[derive(Diagnostic)] -#[diag(session_unstable_virtual_function_elimination)] +#[diag("`-Zvirtual-function-elimination` requires `-Clto`")] pub(crate) struct UnstableVirtualFunctionElimination; #[derive(Diagnostic)] -#[diag(session_unsupported_dwarf_version)] -#[help(session_unsupported_dwarf_version_help)] +#[diag("requested DWARF version {$dwarf_version} is not supported")] +#[help("supported DWARF versions are 2, 3, 4 and 5")] pub(crate) struct UnsupportedDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] -#[diag(session_embed_source_insufficient_dwarf_version)] +#[diag( + "`-Zembed-source=y` requires at least `-Z dwarf-version=5` but DWARF version is {$dwarf_version}" +)] pub(crate) struct EmbedSourceInsufficientDwarfVersion { pub(crate) dwarf_version: u32, } #[derive(Diagnostic)] -#[diag(session_embed_source_requires_debug_info)] +#[diag("`-Zembed-source=y` requires debug information to be enabled")] pub(crate) struct EmbedSourceRequiresDebugInfo; #[derive(Diagnostic)] -#[diag(session_target_stack_protector_not_supported)] +#[diag( + "`-Z stack-protector={$stack_protector}` is not supported for target {$target_triple} and will be ignored" +)] pub(crate) struct StackProtectorNotSupportedForTarget<'a> { pub(crate) stack_protector: StackProtector, pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] -#[diag(session_target_small_data_threshold_not_supported)] +#[diag( + "`-Z small-data-threshold` is not supported for target {$target_triple} and will be ignored" +)] pub(crate) struct SmallDataThresholdNotSupportedForTarget<'a> { pub(crate) target_triple: &'a TargetTuple, } #[derive(Diagnostic)] -#[diag(session_branch_protection_requires_aarch64)] +#[diag("`-Zbranch-protection` is only supported on aarch64")] pub(crate) struct BranchProtectionRequiresAArch64; #[derive(Diagnostic)] -#[diag(session_split_debuginfo_unstable_platform)] +#[diag("`-Csplit-debuginfo={$debuginfo}` is unstable on this platform")] pub(crate) struct SplitDebugInfoUnstablePlatform { pub(crate) debuginfo: SplitDebuginfo, } #[derive(Diagnostic)] -#[diag(session_file_is_not_writeable)] +#[diag("output file {$file} is not writeable -- check its permissions")] pub(crate) struct FileIsNotWriteable<'a> { pub(crate) file: &'a std::path::Path, } #[derive(Diagnostic)] -#[diag(session_file_write_fail)] +#[diag("failed to write `{$path}` due to error `{$err}`")] pub(crate) struct FileWriteFail<'a> { pub(crate) path: &'a std::path::Path, pub(crate) err: String, } #[derive(Diagnostic)] -#[diag(session_crate_name_empty)] +#[diag("crate name must not be empty")] pub(crate) struct CrateNameEmpty { #[primary_span] pub(crate) span: Option, } #[derive(Diagnostic)] -#[diag(session_invalid_character_in_crate_name)] +#[diag("invalid character {$character} in crate name: `{$crate_name}`")] pub(crate) struct InvalidCharacterInCrateName { #[primary_span] pub(crate) span: Option, @@ -247,7 +261,10 @@ pub(crate) struct InvalidCharacterInCrateName { } #[derive(Subdiagnostic)] -#[multipart_suggestion(session_expr_parentheses_needed, applicability = "machine-applicable")] +#[multipart_suggestion( + "parentheses are required to parse this as an expression", + applicability = "machine-applicable" +)] pub struct ExprParenthesesNeeded { #[suggestion_part(code = "(")] left: Span, @@ -262,7 +279,7 @@ impl ExprParenthesesNeeded { } #[derive(Diagnostic)] -#[diag(session_skipping_const_checks)] +#[diag("skipping const checks")] pub(crate) struct SkippingConstChecks { #[subdiagnostic] pub(crate) unleashed_features: Vec, @@ -270,13 +287,13 @@ pub(crate) struct SkippingConstChecks { #[derive(Subdiagnostic)] pub(crate) enum UnleashedFeatureHelp { - #[help(session_unleashed_feature_help_named)] + #[help("skipping check for `{$gate}` feature")] Named { #[primary_span] span: Span, gate: Symbol, }, - #[help(session_unleashed_feature_help_unnamed)] + #[help("skipping check that does not even have a feature gate")] Unnamed { #[primary_span] span: Span, @@ -284,10 +301,10 @@ pub(crate) enum UnleashedFeatureHelp { } #[derive(Diagnostic)] -#[diag(session_invalid_literal_suffix)] +#[diag("suffixes on {$kind} literals are invalid")] struct InvalidLiteralSuffix<'a> { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, // FIXME(#100717) kind: &'a str, @@ -295,8 +312,8 @@ struct InvalidLiteralSuffix<'a> { } #[derive(Diagnostic)] -#[diag(session_invalid_int_literal_width)] -#[help] +#[diag("invalid width `{$width}` for integer literal")] +#[help("valid widths are 8, 16, 32, 64 and 128")] struct InvalidIntLiteralWidth { #[primary_span] span: Span, @@ -304,28 +321,32 @@ struct InvalidIntLiteralWidth { } #[derive(Diagnostic)] -#[diag(session_invalid_num_literal_base_prefix)] -#[note] +#[diag("invalid base prefix for number literal")] +#[note("base prefixes (`0xff`, `0b1010`, `0o755`) are lowercase")] struct InvalidNumLiteralBasePrefix { #[primary_span] - #[suggestion(applicability = "maybe-incorrect", code = "{fixed}")] + #[suggestion( + "try making the prefix lowercase", + applicability = "maybe-incorrect", + code = "{fixed}" + )] span: Span, fixed: String, } #[derive(Diagnostic)] -#[diag(session_invalid_num_literal_suffix)] -#[help] +#[diag("invalid suffix `{$suffix}` for number literal")] +#[help("the suffix must be one of the numeric types (`u32`, `isize`, `f32`, etc.)")] struct InvalidNumLiteralSuffix { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] -#[diag(session_invalid_float_literal_width)] -#[help] +#[diag("invalid width `{$width}` for float literal")] +#[help("valid widths are 32 and 64")] struct InvalidFloatLiteralWidth { #[primary_span] span: Span, @@ -333,18 +354,18 @@ struct InvalidFloatLiteralWidth { } #[derive(Diagnostic)] -#[diag(session_invalid_float_literal_suffix)] -#[help] +#[diag("invalid suffix `{$suffix}` for float literal")] +#[help("valid suffixes are `f32` and `f64`")] struct InvalidFloatLiteralSuffix { #[primary_span] - #[label] + #[label("invalid suffix `{$suffix}`")] span: Span, suffix: String, } #[derive(Diagnostic)] -#[diag(session_int_literal_too_large)] -#[note] +#[diag("integer literal is too large")] +#[note("value exceeds limit of `{$limit}`")] struct IntLiteralTooLarge { #[primary_span] span: Span, @@ -352,26 +373,26 @@ struct IntLiteralTooLarge { } #[derive(Diagnostic)] -#[diag(session_hexadecimal_float_literal_not_supported)] +#[diag("hexadecimal float literal is not supported")] struct HexadecimalFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } #[derive(Diagnostic)] -#[diag(session_octal_float_literal_not_supported)] +#[diag("octal float literal is not supported")] struct OctalFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } #[derive(Diagnostic)] -#[diag(session_binary_float_literal_not_supported)] +#[diag("binary float literal is not supported")] struct BinaryFloatLiteralNotSupported { #[primary_span] - #[label(session_not_supported)] + #[label("not supported")] span: Span, } @@ -457,60 +478,60 @@ pub fn create_lit_error(psess: &ParseSess, err: LitError, lit: token::Lit, span: } #[derive(Diagnostic)] -#[diag(session_incompatible_linker_flavor)] -#[note] +#[diag("linker flavor `{$flavor}` is incompatible with the current target")] +#[note("compatible flavors are: {$compatible_list}")] pub(crate) struct IncompatibleLinkerFlavor { pub(crate) flavor: &'static str, pub(crate) compatible_list: String, } #[derive(Diagnostic)] -#[diag(session_function_return_requires_x86_or_x86_64)] +#[diag("`-Zfunction-return` (except `keep`) is only supported on x86 and x86_64")] pub(crate) struct FunctionReturnRequiresX86OrX8664; #[derive(Diagnostic)] -#[diag(session_function_return_thunk_extern_requires_non_large_code_model)] +#[diag("`-Zfunction-return=thunk-extern` is only supported on non-large code models")] pub(crate) struct FunctionReturnThunkExternRequiresNonLargeCodeModel; #[derive(Diagnostic)] -#[diag(session_indirect_branch_cs_prefix_requires_x86_or_x86_64)] +#[diag("`-Zindirect-branch-cs-prefix` is only supported on x86 and x86_64")] pub(crate) struct IndirectBranchCsPrefixRequiresX86OrX8664; #[derive(Diagnostic)] -#[diag(session_unsupported_regparm)] +#[diag("`-Zregparm={$regparm}` is unsupported (valid values 0-3)")] pub(crate) struct UnsupportedRegparm { pub(crate) regparm: u32, } #[derive(Diagnostic)] -#[diag(session_unsupported_regparm_arch)] +#[diag("`-Zregparm=N` is only supported on x86")] pub(crate) struct UnsupportedRegparmArch; #[derive(Diagnostic)] -#[diag(session_unsupported_reg_struct_return_arch)] +#[diag("`-Zreg-struct-return` is only supported on x86")] pub(crate) struct UnsupportedRegStructReturnArch; #[derive(Diagnostic)] -#[diag(session_failed_to_create_profiler)] +#[diag("failed to create profiler: {$err}")] pub(crate) struct FailedToCreateProfiler { pub(crate) err: String, } #[derive(Diagnostic)] -#[diag(session_soft_float_ignored)] -#[note] +#[diag("`-Csoft-float` is ignored on this target; it only has an effect on *eabihf targets")] +#[note("this may become a hard error in a future version of Rust")] pub(crate) struct SoftFloatIgnored; #[derive(Diagnostic)] -#[diag(session_soft_float_deprecated)] -#[note] -#[note(session_soft_float_deprecated_issue)] +#[diag("`-Csoft-float` is unsound and deprecated; use a corresponding *eabi target instead")] +#[note("it will be removed or ignored in a future version of Rust")] +#[note("see issue #129893 for more information")] pub(crate) struct SoftFloatDeprecated; #[derive(LintDiagnostic)] -#[diag(session_unexpected_builtin_cfg)] -#[note(session_controlled_by)] -#[note(session_incoherent)] +#[diag("unexpected `--cfg {$cfg}` flag")] +#[note("config `{$cfg_name}` is only supposed to be controlled by `{$controlled_by}`")] +#[note("manually setting a built-in cfg can and does create incoherent behaviors")] pub(crate) struct UnexpectedBuiltinCfg { pub(crate) cfg: String, pub(crate) cfg_name: Symbol, diff --git a/compiler/rustc_session/src/lib.rs b/compiler/rustc_session/src/lib.rs index 90108e911044..98731a235d41 100644 --- a/compiler/rustc_session/src/lib.rs +++ b/compiler/rustc_session/src/lib.rs @@ -32,8 +32,6 @@ pub mod output; pub use getopts; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - /// Requirements for a `StableHashingContext` to be used in this crate. /// This is a hack to allow using the `HashStable_Generic` derive macro /// instead of implementing everything in `rustc_middle`. From d2a0557afb3f7826266e24ab4b539e8efcb5d92f Mon Sep 17 00:00:00 2001 From: bjorn3 <17426603+bjorn3@users.noreply.github.com> Date: Wed, 4 Feb 2026 12:35:28 +0000 Subject: [PATCH 319/319] Convert to inline diagnostics in all codegen backends --- Cargo.lock | 1 - compiler/rustc_codegen_cranelift/src/lib.rs | 5 - compiler/rustc_codegen_gcc/messages.ftl | 8 -- compiler/rustc_codegen_gcc/src/errors.rs | 8 +- compiler/rustc_codegen_gcc/src/lib.rs | 7 - compiler/rustc_codegen_llvm/Cargo.toml | 1 - compiler/rustc_codegen_llvm/messages.ftl | 75 ----------- compiler/rustc_codegen_llvm/src/errors.rs | 124 +++++++++++------- compiler/rustc_codegen_llvm/src/lib.rs | 6 - .../rustc_codegen_ssa/src/traits/backend.rs | 4 - compiler/rustc_interface/src/interface.rs | 5 +- compiler/rustc_interface/src/util.rs | 4 - .../codegen-backend/auxiliary/the_backend.rs | 4 - 13 files changed, 79 insertions(+), 173 deletions(-) delete mode 100644 compiler/rustc_codegen_gcc/messages.ftl delete mode 100644 compiler/rustc_codegen_llvm/messages.ftl diff --git a/Cargo.lock b/Cargo.lock index 4cbc98beae11..d61826dd96f3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3635,7 +3635,6 @@ dependencies = [ "rustc_codegen_ssa", "rustc_data_structures", "rustc_errors", - "rustc_fluent_macro", "rustc_fs_util", "rustc_hashes", "rustc_hir", diff --git a/compiler/rustc_codegen_cranelift/src/lib.rs b/compiler/rustc_codegen_cranelift/src/lib.rs index 7361a6af4178..a49dc9be3458 100644 --- a/compiler/rustc_codegen_cranelift/src/lib.rs +++ b/compiler/rustc_codegen_cranelift/src/lib.rs @@ -125,11 +125,6 @@ pub struct CraneliftCodegenBackend { } impl CodegenBackend for CraneliftCodegenBackend { - fn locale_resource(&self) -> &'static str { - // FIXME(rust-lang/rust#100717) - cranelift codegen backend is not yet translated - "" - } - fn name(&self) -> &'static str { "cranelift" } diff --git a/compiler/rustc_codegen_gcc/messages.ftl b/compiler/rustc_codegen_gcc/messages.ftl deleted file mode 100644 index b9b77b7d18c6..000000000000 --- a/compiler/rustc_codegen_gcc/messages.ftl +++ /dev/null @@ -1,8 +0,0 @@ -codegen_gcc_unwinding_inline_asm = - GCC backend does not support unwinding from inline asm - -codegen_gcc_copy_bitcode = failed to copy bitcode to object file: {$err} - -codegen_gcc_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$gcc_err}) - -codegen_gcc_explicit_tail_calls_unsupported = explicit tail calls with the 'become' keyword are not implemented in the GCC backend diff --git a/compiler/rustc_codegen_gcc/src/errors.rs b/compiler/rustc_codegen_gcc/src/errors.rs index b252c39c0c05..f5815e723392 100644 --- a/compiler/rustc_codegen_gcc/src/errors.rs +++ b/compiler/rustc_codegen_gcc/src/errors.rs @@ -2,24 +2,24 @@ use rustc_macros::Diagnostic; use rustc_span::Span; #[derive(Diagnostic)] -#[diag(codegen_gcc_unwinding_inline_asm)] +#[diag("GCC backend does not support unwinding from inline asm")] pub(crate) struct UnwindingInlineAsm { #[primary_span] pub span: Span, } #[derive(Diagnostic)] -#[diag(codegen_gcc_copy_bitcode)] +#[diag("failed to copy bitcode to object file: {$err}")] pub(crate) struct CopyBitcode { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_gcc_lto_bitcode_from_rlib)] +#[diag("failed to get bitcode from object file for LTO ({$gcc_err})")] pub(crate) struct LtoBitcodeFromRlib { pub gcc_err: String, } #[derive(Diagnostic)] -#[diag(codegen_gcc_explicit_tail_calls_unsupported)] +#[diag("explicit tail calls with the 'become' keyword are not implemented in the GCC backend")] pub(crate) struct ExplicitTailCallsUnsupported; diff --git a/compiler/rustc_codegen_gcc/src/lib.rs b/compiler/rustc_codegen_gcc/src/lib.rs index 00bea0222622..cc88fd02435e 100644 --- a/compiler/rustc_codegen_gcc/src/lib.rs +++ b/compiler/rustc_codegen_gcc/src/lib.rs @@ -27,7 +27,6 @@ extern crate rustc_ast; extern crate rustc_codegen_ssa; extern crate rustc_data_structures; extern crate rustc_errors; -extern crate rustc_fluent_macro; extern crate rustc_fs_util; extern crate rustc_hir; extern crate rustc_index; @@ -105,8 +104,6 @@ use tempfile::TempDir; use crate::back::lto::ModuleBuffer; use crate::gcc_util::{target_cpu, to_gcc_features}; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub struct PrintOnPanic String>(pub F); impl String> Drop for PrintOnPanic { @@ -197,10 +194,6 @@ fn load_libgccjit_if_needed(libgccjit_target_lib_file: &Path) { } impl CodegenBackend for GccCodegenBackend { - fn locale_resource(&self) -> &'static str { - crate::DEFAULT_LOCALE_RESOURCE - } - fn name(&self) -> &'static str { "gcc" } diff --git a/compiler/rustc_codegen_llvm/Cargo.toml b/compiler/rustc_codegen_llvm/Cargo.toml index 9741436aacb7..90c87494c3c5 100644 --- a/compiler/rustc_codegen_llvm/Cargo.toml +++ b/compiler/rustc_codegen_llvm/Cargo.toml @@ -23,7 +23,6 @@ rustc_ast = { path = "../rustc_ast" } rustc_codegen_ssa = { path = "../rustc_codegen_ssa" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_errors = { path = "../rustc_errors" } -rustc_fluent_macro = { path = "../rustc_fluent_macro" } rustc_fs_util = { path = "../rustc_fs_util" } rustc_hashes = { path = "../rustc_hashes" } rustc_hir = { path = "../rustc_hir" } diff --git a/compiler/rustc_codegen_llvm/messages.ftl b/compiler/rustc_codegen_llvm/messages.ftl deleted file mode 100644 index 85cb7499cca4..000000000000 --- a/compiler/rustc_codegen_llvm/messages.ftl +++ /dev/null @@ -1,75 +0,0 @@ -codegen_llvm_autodiff_component_missing = autodiff backend not found in the sysroot: {$err} - .note = it will be distributed via rustup in the future - -codegen_llvm_autodiff_component_unavailable = failed to load our autodiff backend: {$err} - -codegen_llvm_autodiff_without_enable = using the autodiff feature requires -Z autodiff=Enable -codegen_llvm_autodiff_without_lto = using the autodiff feature requires setting `lto="fat"` in your Cargo.toml - -codegen_llvm_copy_bitcode = failed to copy bitcode to object file: {$err} - -codegen_llvm_fixed_x18_invalid_arch = the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture - -codegen_llvm_from_llvm_diag = {$message} - -codegen_llvm_from_llvm_optimization_diag = {$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message} - -codegen_llvm_load_bitcode = failed to load bitcode of module "{$name}" -codegen_llvm_load_bitcode_with_llvm_err = failed to load bitcode of module "{$name}": {$llvm_err} - -codegen_llvm_lto_bitcode_from_rlib = failed to get bitcode from object file for LTO ({$err}) - -codegen_llvm_mismatch_data_layout = - data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}` - -codegen_llvm_offload_bundleimages_failed = call to BundleImages failed, `host.out` was not created -codegen_llvm_offload_embed_failed = call to EmbedBufferInModule failed, `host.o` was not created -codegen_llvm_offload_no_abs_path = using the `-Z offload=Host=/absolute/path/to/host.out` flag requires an absolute path -codegen_llvm_offload_no_host_out = using the `-Z offload=Host=/absolute/path/to/host.out` flag must point to a `host.out` file -codegen_llvm_offload_nonexisting = the given path/file to `host.out` does not exist. Did you forget to run the device compilation first? -codegen_llvm_offload_without_enable = using the offload feature requires -Z offload= -codegen_llvm_offload_without_fat_lto = using the offload feature requires -C lto=fat - -codegen_llvm_parse_bitcode = failed to parse bitcode for LTO module -codegen_llvm_parse_bitcode_with_llvm_err = failed to parse bitcode for LTO module: {$llvm_err} - -codegen_llvm_parse_target_machine_config = - failed to parse target machine config to target machine: {$error} - -codegen_llvm_prepare_autodiff = failed to prepare autodiff: src: {$src}, target: {$target}, {$error} -codegen_llvm_prepare_autodiff_with_llvm_err = failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error} -codegen_llvm_prepare_thin_lto_context = failed to prepare thin LTO context -codegen_llvm_prepare_thin_lto_context_with_llvm_err = failed to prepare thin LTO context: {$llvm_err} - -codegen_llvm_prepare_thin_lto_module = failed to prepare thin LTO module -codegen_llvm_prepare_thin_lto_module_with_llvm_err = failed to prepare thin LTO module: {$llvm_err} - -codegen_llvm_run_passes = failed to run LLVM passes -codegen_llvm_run_passes_with_llvm_err = failed to run LLVM passes: {$llvm_err} - -codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0 = `-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later. - -codegen_llvm_sanitizer_memtag_requires_mte = - `-Zsanitizer=memtag` requires `-Ctarget-feature=+mte` - -codegen_llvm_serialize_module = failed to serialize module {$name} -codegen_llvm_serialize_module_with_llvm_err = failed to serialize module {$name}: {$llvm_err} - -codegen_llvm_symbol_already_defined = - symbol `{$symbol_name}` is already defined - -codegen_llvm_target_machine = could not create LLVM TargetMachine for triple: {$triple} -codegen_llvm_target_machine_with_llvm_err = could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err} - -codegen_llvm_unknown_debuginfo_compression = unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo - -codegen_llvm_write_bytecode = failed to write bytecode to {$path}: {$err} - -codegen_llvm_write_ir = failed to write LLVM IR to {$path} -codegen_llvm_write_ir_with_llvm_err = failed to write LLVM IR to {$path}: {$llvm_err} - -codegen_llvm_write_output = could not write output to {$path} -codegen_llvm_write_output_with_llvm_err = could not write output to {$path}: {$llvm_err} - -codegen_llvm_write_thinlto_key = error while writing ThinLTO key data: {$err} -codegen_llvm_write_thinlto_key_with_llvm_err = error while writing ThinLTO key data: {$err}: {$llvm_err} diff --git a/compiler/rustc_codegen_llvm/src/errors.rs b/compiler/rustc_codegen_llvm/src/errors.rs index bd42cf556966..23fa05f3d02f 100644 --- a/compiler/rustc_codegen_llvm/src/errors.rs +++ b/compiler/rustc_codegen_llvm/src/errors.rs @@ -2,14 +2,12 @@ use std::ffi::CString; use std::path::Path; use rustc_data_structures::small_c_str::SmallCStr; -use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level}; +use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, inline_fluent}; use rustc_macros::Diagnostic; use rustc_span::Span; -use crate::fluent_generated as fluent; - #[derive(Diagnostic)] -#[diag(codegen_llvm_symbol_already_defined)] +#[diag("symbol `{$symbol_name}` is already defined")] pub(crate) struct SymbolAlreadyDefined<'a> { #[primary_span] pub span: Span, @@ -17,7 +15,7 @@ pub(crate) struct SymbolAlreadyDefined<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_sanitizer_memtag_requires_mte)] +#[diag("`-Zsanitizer=memtag` requires `-Ctarget-feature=+mte`")] pub(crate) struct SanitizerMemtagRequiresMte; pub(crate) struct ParseTargetMachineConfig<'a>(pub LlvmError<'a>); @@ -27,89 +25,97 @@ impl Diagnostic<'_, G> for ParseTargetMachineConfig<'_> { let diag: Diag<'_, G> = self.0.into_diag(dcx, level); let (message, _) = diag.messages.first().expect("`LlvmError` with no message"); let message = dcx.eagerly_translate_to_string(message.clone(), diag.args.iter()); - Diag::new(dcx, level, fluent::codegen_llvm_parse_target_machine_config) - .with_arg("error", message) + Diag::new( + dcx, + level, + inline_fluent!("failed to parse target machine config to target machine: {$error}"), + ) + .with_arg("error", message) } } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_component_unavailable)] +#[diag("failed to load our autodiff backend: {$err}")] pub(crate) struct AutoDiffComponentUnavailable { pub err: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_component_missing)] -#[note] +#[diag("autodiff backend not found in the sysroot: {$err}")] +#[note("it will be distributed via rustup in the future")] pub(crate) struct AutoDiffComponentMissing { pub err: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_lto)] +#[diag("using the autodiff feature requires setting `lto=\"fat\"` in your Cargo.toml")] pub(crate) struct AutoDiffWithoutLto; #[derive(Diagnostic)] -#[diag(codegen_llvm_autodiff_without_enable)] +#[diag("using the autodiff feature requires -Z autodiff=Enable")] pub(crate) struct AutoDiffWithoutEnable; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_without_enable)] +#[diag("using the offload feature requires -Z offload=")] pub(crate) struct OffloadWithoutEnable; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_without_fat_lto)] +#[diag("using the offload feature requires -C lto=fat")] pub(crate) struct OffloadWithoutFatLTO; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_no_abs_path)] +#[diag("using the `-Z offload=Host=/absolute/path/to/host.out` flag requires an absolute path")] pub(crate) struct OffloadWithoutAbsPath; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_no_host_out)] +#[diag( + "using the `-Z offload=Host=/absolute/path/to/host.out` flag must point to a `host.out` file" +)] pub(crate) struct OffloadWrongFileName; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_nonexisting)] +#[diag( + "the given path/file to `host.out` does not exist. Did you forget to run the device compilation first?" +)] pub(crate) struct OffloadNonexistingPath; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_bundleimages_failed)] +#[diag("call to BundleImages failed, `host.out` was not created")] pub(crate) struct OffloadBundleImagesFailed; #[derive(Diagnostic)] -#[diag(codegen_llvm_offload_embed_failed)] +#[diag("call to EmbedBufferInModule failed, `host.o` was not created")] pub(crate) struct OffloadEmbedFailed; #[derive(Diagnostic)] -#[diag(codegen_llvm_lto_bitcode_from_rlib)] +#[diag("failed to get bitcode from object file for LTO ({$err})")] pub(crate) struct LtoBitcodeFromRlib { pub err: String, } #[derive(Diagnostic)] pub enum LlvmError<'a> { - #[diag(codegen_llvm_write_output)] + #[diag("could not write output to {$path}")] WriteOutput { path: &'a Path }, - #[diag(codegen_llvm_target_machine)] + #[diag("could not create LLVM TargetMachine for triple: {$triple}")] CreateTargetMachine { triple: SmallCStr }, - #[diag(codegen_llvm_run_passes)] + #[diag("failed to run LLVM passes")] RunLlvmPasses, - #[diag(codegen_llvm_serialize_module)] + #[diag("failed to serialize module {$name}")] SerializeModule { name: &'a str }, - #[diag(codegen_llvm_write_ir)] + #[diag("failed to write LLVM IR to {$path}")] WriteIr { path: &'a Path }, - #[diag(codegen_llvm_prepare_thin_lto_context)] + #[diag("failed to prepare thin LTO context")] PrepareThinLtoContext, - #[diag(codegen_llvm_load_bitcode)] + #[diag("failed to load bitcode of module \"{$name}\"")] LoadBitcode { name: CString }, - #[diag(codegen_llvm_write_thinlto_key)] + #[diag("error while writing ThinLTO key data: {$err}")] WriteThinLtoKey { err: std::io::Error }, - #[diag(codegen_llvm_prepare_thin_lto_module)] + #[diag("failed to prepare thin LTO module")] PrepareThinLtoModule, - #[diag(codegen_llvm_parse_bitcode)] + #[diag("failed to parse bitcode for LTO module")] ParseBitcode, - #[diag(codegen_llvm_prepare_autodiff)] + #[diag("failed to prepare autodiff: src: {$src}, target: {$target}, {$error}")] PrepareAutoDiff { src: String, target: String, error: String }, } @@ -119,17 +125,31 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { fn into_diag(self, dcx: DiagCtxtHandle<'_>, level: Level) -> Diag<'_, G> { use LlvmError::*; let msg_with_llvm_err = match &self.0 { - WriteOutput { .. } => fluent::codegen_llvm_write_output_with_llvm_err, - CreateTargetMachine { .. } => fluent::codegen_llvm_target_machine_with_llvm_err, - RunLlvmPasses => fluent::codegen_llvm_run_passes_with_llvm_err, - SerializeModule { .. } => fluent::codegen_llvm_serialize_module_with_llvm_err, - WriteIr { .. } => fluent::codegen_llvm_write_ir_with_llvm_err, - PrepareThinLtoContext => fluent::codegen_llvm_prepare_thin_lto_context_with_llvm_err, - LoadBitcode { .. } => fluent::codegen_llvm_load_bitcode_with_llvm_err, - WriteThinLtoKey { .. } => fluent::codegen_llvm_write_thinlto_key_with_llvm_err, - PrepareThinLtoModule => fluent::codegen_llvm_prepare_thin_lto_module_with_llvm_err, - ParseBitcode => fluent::codegen_llvm_parse_bitcode_with_llvm_err, - PrepareAutoDiff { .. } => fluent::codegen_llvm_prepare_autodiff_with_llvm_err, + WriteOutput { .. } => inline_fluent!("could not write output to {$path}: {$llvm_err}"), + CreateTargetMachine { .. } => inline_fluent!( + "could not create LLVM TargetMachine for triple: {$triple}: {$llvm_err}" + ), + RunLlvmPasses => inline_fluent!("failed to run LLVM passes: {$llvm_err}"), + SerializeModule { .. } => { + inline_fluent!("failed to serialize module {$name}: {$llvm_err}") + } + WriteIr { .. } => inline_fluent!("failed to write LLVM IR to {$path}: {$llvm_err}"), + PrepareThinLtoContext => { + inline_fluent!("failed to prepare thin LTO context: {$llvm_err}") + } + LoadBitcode { .. } => { + inline_fluent!("failed to load bitcode of module \"{$name}\": {$llvm_err}") + } + WriteThinLtoKey { .. } => { + inline_fluent!("error while writing ThinLTO key data: {$err}: {$llvm_err}") + } + PrepareThinLtoModule => { + inline_fluent!("failed to prepare thin LTO module: {$llvm_err}") + } + ParseBitcode => inline_fluent!("failed to parse bitcode for LTO module: {$llvm_err}"), + PrepareAutoDiff { .. } => inline_fluent!( + "failed to prepare autodiff: {$llvm_err}, src: {$src}, target: {$target}, {$error}" + ), }; self.0 .into_diag(dcx, level) @@ -139,7 +159,7 @@ impl Diagnostic<'_, G> for WithLlvmError<'_> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_from_llvm_optimization_diag)] +#[diag("{$filename}:{$line}:{$column} {$pass_name} ({$kind}): {$message}")] pub(crate) struct FromLlvmOptimizationDiag<'a> { pub filename: &'a str, pub line: std::ffi::c_uint, @@ -150,32 +170,36 @@ pub(crate) struct FromLlvmOptimizationDiag<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_from_llvm_diag)] +#[diag("{$message}")] pub(crate) struct FromLlvmDiag { pub message: String, } #[derive(Diagnostic)] -#[diag(codegen_llvm_write_bytecode)] +#[diag("failed to write bytecode to {$path}: {$err}")] pub(crate) struct WriteBytecode<'a> { pub path: &'a Path, pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_llvm_copy_bitcode)] +#[diag("failed to copy bitcode to object file: {$err}")] pub(crate) struct CopyBitcode { pub err: std::io::Error, } #[derive(Diagnostic)] -#[diag(codegen_llvm_unknown_debuginfo_compression)] +#[diag( + "unknown debuginfo compression algorithm {$algorithm} - will fall back to uncompressed debuginfo" +)] pub(crate) struct UnknownCompression { pub algorithm: &'static str, } #[derive(Diagnostic)] -#[diag(codegen_llvm_mismatch_data_layout)] +#[diag( + "data-layout for target `{$rustc_target}`, `{$rustc_layout}`, differs from LLVM target's `{$llvm_target}` default layout, `{$llvm_layout}`" +)] pub(crate) struct MismatchedDataLayout<'a> { pub rustc_target: &'a str, pub rustc_layout: &'a str, @@ -184,11 +208,11 @@ pub(crate) struct MismatchedDataLayout<'a> { } #[derive(Diagnostic)] -#[diag(codegen_llvm_fixed_x18_invalid_arch)] +#[diag("the `-Zfixed-x18` flag is not supported on the `{$arch}` architecture")] pub(crate) struct FixedX18InvalidArch<'a> { pub arch: &'a str, } #[derive(Diagnostic)] -#[diag(codegen_llvm_sanitizer_kcfi_arity_requires_llvm_21_0_0)] +#[diag("`-Zsanitizer-kcfi-arity` requires LLVM 21.0.0 or later.")] pub(crate) struct SanitizerKcfiArityRequiresLLVM2100; diff --git a/compiler/rustc_codegen_llvm/src/lib.rs b/compiler/rustc_codegen_llvm/src/lib.rs index 5879132eb9fb..8bcf99cbb316 100644 --- a/compiler/rustc_codegen_llvm/src/lib.rs +++ b/compiler/rustc_codegen_llvm/src/lib.rs @@ -74,8 +74,6 @@ mod typetree; mod va_arg; mod value; -rustc_fluent_macro::fluent_messages! { "../messages.ftl" } - pub(crate) use macros::TryFromU32; #[derive(Clone)] @@ -241,10 +239,6 @@ impl LlvmCodegenBackend { } impl CodegenBackend for LlvmCodegenBackend { - fn locale_resource(&self) -> &'static str { - crate::DEFAULT_LOCALE_RESOURCE - } - fn name(&self) -> &'static str { "llvm" } diff --git a/compiler/rustc_codegen_ssa/src/traits/backend.rs b/compiler/rustc_codegen_ssa/src/traits/backend.rs index 625551d17d9d..ebedcc4b756d 100644 --- a/compiler/rustc_codegen_ssa/src/traits/backend.rs +++ b/compiler/rustc_codegen_ssa/src/traits/backend.rs @@ -37,10 +37,6 @@ pub trait BackendTypes { } pub trait CodegenBackend { - /// Locale resources for diagnostic messages - a string the content of the Fluent resource. - /// Called before `init` so that all other functions are able to emit translatable diagnostics. - fn locale_resource(&self) -> &'static str; - fn name(&self) -> &'static str; fn init(&self, _sess: &Session) {} diff --git a/compiler/rustc_interface/src/interface.rs b/compiler/rustc_interface/src/interface.rs index f76e8d4632fc..9165842dbdd0 100644 --- a/compiler/rustc_interface/src/interface.rs +++ b/compiler/rustc_interface/src/interface.rs @@ -463,9 +463,6 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se Err(e) => early_dcx.early_fatal(format!("failed to load fluent bundle: {e}")), }; - let mut locale_resources = config.locale_resources; - locale_resources.push(codegen_backend.locale_resource()); - let mut sess = rustc_session::build_session( config.opts, CompilerIO { @@ -476,7 +473,7 @@ pub fn run_compiler(config: Config, f: impl FnOnce(&Compiler) -> R + Se }, bundle, config.registry, - locale_resources, + config.locale_resources, config.lint_caps, target, util::rustc_version_str().unwrap_or("unknown"), diff --git a/compiler/rustc_interface/src/util.rs b/compiler/rustc_interface/src/util.rs index 25f59f0e89df..458e7ad32143 100644 --- a/compiler/rustc_interface/src/util.rs +++ b/compiler/rustc_interface/src/util.rs @@ -361,10 +361,6 @@ pub struct DummyCodegenBackend { } impl CodegenBackend for DummyCodegenBackend { - fn locale_resource(&self) -> &'static str { - "" - } - fn name(&self) -> &'static str { "dummy" } diff --git a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs index 48f328f4fad3..c15c2dea4c19 100644 --- a/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs +++ b/tests/ui-fulldeps/codegen-backend/auxiliary/the_backend.rs @@ -29,10 +29,6 @@ use rustc_session::config::OutputFilenames; struct TheBackend; impl CodegenBackend for TheBackend { - fn locale_resource(&self) -> &'static str { - "" - } - fn name(&self) -> &'static str { "the-backend" }