From ebd4bc9224a5de37ce151745b00cb8039fef4385 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 27 Dec 2025 20:56:37 +0000 Subject: [PATCH 1/5] Support long diff conflict markers git can be configured to use more than 7 characters for conflict markers, and jj automatically uses longer conflict markers when the text contains any char sequence that could be confused with conflict markers. Ensure that we only point at markers that are consistent with the start marker's length. Ensure that we only consider char sequences at the beginning of a line as a diff marker. --- .../rustc_parse/src/parser/diagnostics.rs | 52 +++++++++++++++---- .../diff-markers/long-conflict-markers.rs | 11 ++++ .../diff-markers/long-conflict-markers.stderr | 20 +++++++ 3 files changed, 73 insertions(+), 10 deletions(-) create mode 100644 tests/ui/parser/diff-markers/long-conflict-markers.rs create mode 100644 tests/ui/parser/diff-markers/long-conflict-markers.stderr diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 70ec80a508126..9c4895e391d23 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -3037,27 +3037,53 @@ impl<'a> Parser<'a> { long_kind: &TokenKind, short_kind: &TokenKind, ) -> bool { - (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind)) - && self.look_ahead(3, |tok| tok == short_kind) + if long_kind == short_kind { + // For conflict marker chars like `%` and `\`. + (0..7).all(|i| self.look_ahead(i, |tok| tok == long_kind)) + } else { + // For conflict marker chars like `<` and `|`. + (0..3).all(|i| self.look_ahead(i, |tok| tok == long_kind)) + && self.look_ahead(3, |tok| tok == short_kind || tok == long_kind) + } } - fn conflict_marker(&mut self, long_kind: &TokenKind, short_kind: &TokenKind) -> Option { + fn conflict_marker( + &mut self, + long_kind: &TokenKind, + short_kind: &TokenKind, + expected: Option, + ) -> Option<(Span, usize)> { if self.is_vcs_conflict_marker(long_kind, short_kind) { let lo = self.token.span; - for _ in 0..4 { + if self.psess.source_map().span_to_margin(lo) != Some(0) { + return None; + } + let mut len = 0; + while self.token.kind == *long_kind || self.token.kind == *short_kind { + if self.token.kind.break_two_token_op(1).is_some() { + len += 2; + } else { + len += 1; + } self.bump(); + if expected == Some(len) { + break; + } + } + if expected.is_some() && expected != Some(len) { + return None; } - return Some(lo.to(self.prev_token.span)); + return Some((lo.to(self.prev_token.span), len)); } None } pub(super) fn recover_vcs_conflict_marker(&mut self) { // <<<<<<< - let Some(start) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt) else { + let Some((start, len)) = self.conflict_marker(&TokenKind::Shl, &TokenKind::Lt, None) else { return; }; - let mut spans = Vec::with_capacity(3); + let mut spans = Vec::with_capacity(2); spans.push(start); // ||||||| let mut middlediff3 = None; @@ -3069,13 +3095,19 @@ impl<'a> Parser<'a> { if self.token == TokenKind::Eof { break; } - if let Some(span) = self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::OrOr, &TokenKind::Or, Some(len)) + { middlediff3 = Some(span); } - if let Some(span) = self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::EqEq, &TokenKind::Eq, Some(len)) + { middle = Some(span); } - if let Some(span) = self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt) { + if let Some((span, _)) = + self.conflict_marker(&TokenKind::Shr, &TokenKind::Gt, Some(len)) + { spans.push(span); end = Some(span); break; diff --git a/tests/ui/parser/diff-markers/long-conflict-markers.rs b/tests/ui/parser/diff-markers/long-conflict-markers.rs new file mode 100644 index 0000000000000..f92615fb75b47 --- /dev/null +++ b/tests/ui/parser/diff-markers/long-conflict-markers.rs @@ -0,0 +1,11 @@ +enum E { + Foo { +<<<<<<<<< HEAD //~ ERROR encountered diff marker + x: u8, +||||||| + z: (), +========= + y: i8, +>>>>>>>>> branch + } +} diff --git a/tests/ui/parser/diff-markers/long-conflict-markers.stderr b/tests/ui/parser/diff-markers/long-conflict-markers.stderr new file mode 100644 index 0000000000000..c83aaf16b7418 --- /dev/null +++ b/tests/ui/parser/diff-markers/long-conflict-markers.stderr @@ -0,0 +1,20 @@ +error: encountered diff marker + --> $DIR/long-conflict-markers.rs:3:1 + | +LL | <<<<<<<<< HEAD + | ^^^^^^^^^ between this marker and `=======` is the code that you are merging into +... +LL | ========= + | --------- between this marker and `>>>>>>>` is the incoming code +LL | y: i8, +LL | >>>>>>>>> branch + | ^^^^^^^^^ this marker concludes the conflict region + | + = note: conflict markers indicate that a merge was started but could not be completed due to merge conflicts + to resolve a conflict, keep only the code you want and then delete the lines containing conflict markers + = help: if you are in a merge, the top section is the code you already had checked out and the bottom section is the new code + if you are in a rebase, the top section is the code being rebased onto and the bottom section is the code you had checked out which is being rebased + = note: for an explanation on these markers from the `git` documentation, visit + +error: aborting due to 1 previous error + From 345e9a089bde20917fb7cf1f9db5092d69d70b8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 16 Jan 2026 22:04:53 +0100 Subject: [PATCH 2/5] Document `-Zcache-proc-macros` --- .../src/compiler-flags/cache-proc-macros.md | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/doc/unstable-book/src/compiler-flags/cache-proc-macros.md diff --git a/src/doc/unstable-book/src/compiler-flags/cache-proc-macros.md b/src/doc/unstable-book/src/compiler-flags/cache-proc-macros.md new file mode 100644 index 0000000000000..eefaf9ca2f1bb --- /dev/null +++ b/src/doc/unstable-book/src/compiler-flags/cache-proc-macros.md @@ -0,0 +1,9 @@ +## `cache-proc-macros` + +The tracking issue for this feature is: [#151364] + +[#151364]: https://github.com/rust-lang/rust/issues/151364 + +------------------------ + +This option instructs `rustc` to cache (derive) proc-macro invocations using the incremental system. Note that the compiler does not currently check whether the proc-macro is actually "cacheable" or not. If you use this flag when compiling a crate that uses non-pure proc-macros, it can result in stale expansions being compiled. From f7931c805097f97c9586f0ce49d01ff4adfc972c Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 30 Jan 2026 18:21:19 -0800 Subject: [PATCH 3/5] Remove some unnecessary `try`-related type annotations I left a few, like ```rust let result: Result<_, ModError<'_>> = try { ``` where it felt like seeing it might still be useful for the reader. Feel free to push back on any of these changes if you think seeing the type would be better. --- compiler/rustc_borrowck/src/nll.rs | 4 +-- compiler/rustc_borrowck/src/polonius/dump.rs | 2 +- compiler/rustc_codegen_ssa/src/back/linker.rs | 16 ++++----- compiler/rustc_driver_impl/src/lib.rs | 2 +- .../rustc_expand/src/proc_macro_server.rs | 4 +-- .../src/check/compare_impl_item.rs | 4 +-- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_lint/src/types.rs | 33 +++++++++---------- compiler/rustc_middle/src/mir/pretty.rs | 4 +-- .../rustc_mir_build/src/builder/custom/mod.rs | 2 +- compiler/rustc_privacy/src/lib.rs | 6 ++-- .../error_reporting/infer/need_type_info.rs | 15 ++++----- 12 files changed, 46 insertions(+), 48 deletions(-) diff --git a/compiler/rustc_borrowck/src/nll.rs b/compiler/rustc_borrowck/src/nll.rs index 64e3b59acfff9..7cb27638e6450 100644 --- a/compiler/rustc_borrowck/src/nll.rs +++ b/compiler/rustc_borrowck/src/nll.rs @@ -230,13 +230,13 @@ pub(super) fn dump_nll_mir<'tcx>( dumper.dump_mir(body); // Also dump the region constraint graph as a graphviz file. - let _: io::Result<()> = try { + let _ = try { let mut file = dumper.create_dump_file("regioncx.all.dot", body)?; regioncx.dump_graphviz_raw_constraints(tcx, &mut file)?; }; // Also dump the region constraint SCC graph as a graphviz file. - let _: io::Result<()> = try { + let _ = try { let mut file = dumper.create_dump_file("regioncx.scc.dot", body)?; regioncx.dump_graphviz_scc_constraints(tcx, &mut file)?; }; diff --git a/compiler/rustc_borrowck/src/polonius/dump.rs b/compiler/rustc_borrowck/src/polonius/dump.rs index 62f9ae173474d..a8e7ab234f006 100644 --- a/compiler/rustc_borrowck/src/polonius/dump.rs +++ b/compiler/rustc_borrowck/src/polonius/dump.rs @@ -58,7 +58,7 @@ pub(crate) fn dump_polonius_mir<'tcx>( let dumper = dumper.set_extra_data(extra_data).set_options(options); - let _: io::Result<()> = try { + let _ = try { let mut file = dumper.create_dump_file("html", body)?; emit_polonius_dump( &dumper, diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs index db49f92e39acc..10c4eedb58e8e 100644 --- a/compiler/rustc_codegen_ssa/src/back/linker.rs +++ b/compiler/rustc_codegen_ssa/src/back/linker.rs @@ -2,7 +2,7 @@ use std::ffi::{OsStr, OsString}; use std::fs::{self, File}; use std::io::prelude::*; use std::path::{Path, PathBuf}; -use std::{env, io, iter, mem, str}; +use std::{env, iter, mem, str}; use find_msvc_tools; use rustc_hir::attrs::WindowsSubsystemKind; @@ -809,7 +809,7 @@ impl<'a> Linker for GccLinker<'a> { if self.sess.target.is_like_darwin { // Write a plain, newline-separated list of symbols - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; for (sym, _) in symbols { debug!(" _{sym}"); @@ -821,7 +821,7 @@ impl<'a> Linker for GccLinker<'a> { } self.link_arg("-exported_symbols_list").link_arg(path); } else if self.sess.target.is_like_windows { - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; // .def file similar to MSVC one but without LIBRARY section @@ -845,7 +845,7 @@ impl<'a> Linker for GccLinker<'a> { self.link_arg("--export").link_arg(sym); } } else if crate_type == CrateType::Executable && !self.sess.target.is_like_solaris { - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; for (sym, _) in symbols { @@ -860,7 +860,7 @@ impl<'a> Linker for GccLinker<'a> { self.link_arg("--dynamic-list").link_arg(path); } else { // Write an LD version script - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; writeln!(f, "{{")?; if !symbols.is_empty() { @@ -1139,7 +1139,7 @@ impl<'a> Linker for MsvcLinker<'a> { } let path = tmpdir.join("lib.def"); - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; // Start off with the standard module name header and then go @@ -1735,7 +1735,7 @@ impl<'a> Linker for AixLinker<'a> { symbols: &[(String, SymbolExportKind)], ) { let path = tmpdir.join("list.exp"); - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; // FIXME: use llvm-nm to generate export list. for (symbol, _) in symbols { @@ -2135,7 +2135,7 @@ impl<'a> Linker for BpfLinker<'a> { symbols: &[(String, SymbolExportKind)], ) { let path = tmpdir.join("symbols"); - let res: io::Result<()> = try { + let res = try { let mut f = File::create_buffered(&path)?; for (sym, _) in symbols { writeln!(f, "{sym}")?; diff --git a/compiler/rustc_driver_impl/src/lib.rs b/compiler/rustc_driver_impl/src/lib.rs index 38ee87601614b..5579c25329eb7 100644 --- a/compiler/rustc_driver_impl/src/lib.rs +++ b/compiler/rustc_driver_impl/src/lib.rs @@ -537,7 +537,7 @@ fn show_md_content_with_pager(content: &str, color: ColorConfig) { }; // Try to print via the pager, pretty output if possible. - let pager_res: Option<()> = try { + let pager_res = try { let mut pager = cmd.stdin(Stdio::piped()).spawn().ok()?; let pager_stdin = pager.stdin.as_mut()?; diff --git a/compiler/rustc_expand/src/proc_macro_server.rs b/compiler/rustc_expand/src/proc_macro_server.rs index 12490880ab0eb..947b8a6e3e5ee 100644 --- a/compiler/rustc_expand/src/proc_macro_server.rs +++ b/compiler/rustc_expand/src/proc_macro_server.rs @@ -7,7 +7,7 @@ use rustc_ast::tokenstream::{self, DelimSpacing, Spacing, TokenStream}; use rustc_ast::util::literal::escape_byte_str_symbol; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan, PResult}; +use rustc_errors::{Diag, ErrorGuaranteed, MultiSpan}; use rustc_parse::lexer::{StripTokens, nfc_normalize}; use rustc_parse::parser::Parser; use rustc_parse::{exp, new_parser_from_source_str, source_str_to_stream, unwrap_or_emit_fatal}; @@ -591,7 +591,7 @@ impl server::Server for Rustc<'_, '_> { fn ts_expand_expr(&mut self, stream: &Self::TokenStream) -> Result { // Parse the expression from our tokenstream. - let expr: PResult<'_, _> = try { + let expr = try { let mut p = Parser::new(self.psess(), stream.clone(), Some("proc_macro expand expr")); let expr = p.parse_expr()?; if p.token != token::Eof { diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index dd7b03c9dac3f..777929eaadb84 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -1845,7 +1845,7 @@ fn compare_synthetic_generics<'tcx>( // The case where the impl method uses `impl Trait` but the trait method uses // explicit generics err.span_label(impl_span, "expected generic parameter, found `impl Trait`"); - let _: Option<_> = try { + try { // try taking the name from the trait impl // FIXME: this is obviously suboptimal since the name can already be used // as another generic argument @@ -1882,7 +1882,7 @@ fn compare_synthetic_generics<'tcx>( // The case where the trait method uses `impl Trait`, but the impl method uses // explicit generics. err.span_label(impl_span, "expected `impl Trait`, found generic parameter"); - let _: Option<_> = try { + try { let impl_m = impl_m.def_id.as_local()?; let impl_m = tcx.hir_expect_impl_item(impl_m); let (sig, _) = impl_m.expect_fn(); diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index f5fb8031ab0f0..35f8c44b685d7 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -582,7 +582,7 @@ fn write_out_deps(tcx: TyCtxt<'_>, outputs: &OutputFilenames, out_filenames: &[P let deps_output = outputs.path(OutputType::DepInfo); let deps_filename = deps_output.as_path(); - let result: io::Result<()> = try { + let result = try { // Build a list of files used to compile the output and // write Makefile-compatible dependency rules let mut files: IndexMap)> = sess diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index f3e6db6f2d8e4..47e1fef8b82ea 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -746,24 +746,23 @@ fn pat_ty_is_known_nonnull<'tcx>( typing_env: ty::TypingEnv<'tcx>, pat: ty::Pattern<'tcx>, ) -> bool { - Option::unwrap_or_default( - try { - match *pat { - ty::PatternKind::Range { start, end } => { - let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?; - let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; - - // This also works for negative numbers, as we just need - // to ensure we aren't wrapping over zero. - start > 0 && end >= start - } - ty::PatternKind::NotNull => true, - ty::PatternKind::Or(patterns) => { - patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat)) - } + try { + match *pat { + ty::PatternKind::Range { start, end } => { + let start = start.try_to_value()?.try_to_bits(tcx, typing_env)?; + let end = end.try_to_value()?.try_to_bits(tcx, typing_env)?; + + // This also works for negative numbers, as we just need + // to ensure we aren't wrapping over zero. + start > 0 && end >= start } - }, - ) + ty::PatternKind::NotNull => true, + ty::PatternKind::Or(patterns) => { + patterns.iter().all(|pat| pat_ty_is_known_nonnull(tcx, typing_env, pat)) + } + } + } + .unwrap_or_default() } /// Given a non-null scalar (or transparent) type `ty`, return the nullable version of that type. diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index ded02595563c9..2e6e96b4d8a80 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -157,13 +157,13 @@ impl<'dis, 'de, 'tcx> MirDumper<'dis, 'de, 'tcx> { /// - `foo & nll | bar & typeck` == match if `foo` and `nll` both appear in the name /// or `typeck` and `bar` both appear in the name. pub fn dump_mir(&self, body: &Body<'tcx>) { - let _: io::Result<()> = try { + let _ = try { let mut file = self.create_dump_file("mir", body)?; self.dump_mir_to_writer(body, &mut file)?; }; if self.tcx().sess.opts.unstable_opts.dump_mir_graphviz { - let _: io::Result<()> = try { + let _ = try { let mut file = self.create_dump_file("dot", body)?; write_mir_fn_graphviz(self.tcx(), body, false, &mut file)?; }; diff --git a/compiler/rustc_mir_build/src/builder/custom/mod.rs b/compiler/rustc_mir_build/src/builder/custom/mod.rs index 792ad6d782cf3..1005dd30d73f4 100644 --- a/compiler/rustc_mir_build/src/builder/custom/mod.rs +++ b/compiler/rustc_mir_build/src/builder/custom/mod.rs @@ -85,7 +85,7 @@ pub(super) fn build_custom_mir<'tcx>( block_map: FxHashMap::default(), }; - let res: PResult<_> = try { + let res = try { pctxt.parse_args(params)?; pctxt.parse_body(expr)?; }; diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 3336775210233..19c030a94e5c5 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -1159,14 +1159,14 @@ impl<'tcx> TypePrivacyVisitor<'tcx> { let typeck_results = self .maybe_typeck_results .unwrap_or_else(|| span_bug!(span, "`hir::Expr` or `hir::Pat` outside of a body")); - let result: ControlFlow<()> = try { + try { self.visit(typeck_results.node_type(id))?; self.visit(typeck_results.node_args(id))?; if let Some(adjustments) = typeck_results.adjustments().get(id) { adjustments.iter().try_for_each(|adjustment| self.visit(adjustment.target))?; } - }; - result.is_break() + } + .is_break() } fn check_def_id(&self, def_id: DefId, kind: &str, descr: &dyn fmt::Display) -> bool { 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 e3c8bfe4a452a..b8f11189a11f2 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 @@ -1023,7 +1023,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { hir::ExprKind::MethodCall(segment, ..) => { if let Some(def_id) = self.typeck_results.type_dependent_def_id(expr.hir_id) { let generics = tcx.generics_of(def_id); - let insertable: Option<_> = try { + let insertable = try { if generics.has_impl_trait() { None? } @@ -1061,7 +1061,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // // FIXME: We deal with that one separately for now, // would be good to remove this special case. - let last_segment_using_path_data: Option<_> = try { + let last_segment_using_path_data = try { let generics_def_id = tcx.res_generics_def_id(path.res)?; let generics = tcx.generics_of(generics_def_id); if generics.has_impl_trait() { @@ -1117,19 +1117,18 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { }; let generics = tcx.generics_of(def_id); - let segment: Option<_> = try { - if !segment.infer_args || generics.has_impl_trait() { - do yeet (); - } + let segment = if !segment.infer_args || generics.has_impl_trait() { + None + } else { let span = tcx.hir_span(segment.hir_id); let insert_span = segment.ident.span.shrink_to_hi().with_hi(span.hi()); - InsertableGenericArgs { + Some(InsertableGenericArgs { insert_span, args, generics_def_id: def_id, def_id, have_turbofish: false, - } + }) }; let parent_def_id = generics.parent.unwrap(); From 5405e5d06e7b2fede8cfba16a5bd140a8ebeb4f0 Mon Sep 17 00:00:00 2001 From: Alan Egerton Date: Tue, 3 Feb 2026 11:52:00 +0000 Subject: [PATCH 4/5] Suppress unused_mut lint if mutation fails due to borrowck error Remedying the borrowck error will likely result in the mut becoming used. --- compiler/rustc_borrowck/src/lib.rs | 11 +++++++++++ .../unused/mut-used-despite-borrowck-error.rs | 19 +++++++++++++++++++ .../mut-used-despite-borrowck-error.stderr | 13 +++++++++++++ 3 files changed, 43 insertions(+) create mode 100644 tests/ui/lint/unused/mut-used-despite-borrowck-error.rs create mode 100644 tests/ui/lint/unused/mut-used-despite-borrowck-error.stderr diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 7c5cbc58e25c5..946c1c093fd71 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -1207,6 +1207,17 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> { "access_place: suppressing error place_span=`{:?}` kind=`{:?}`", place_span, kind ); + + // If the place is being mutated, then mark it as such anyway in order to suppress the + // `unused_mut` lint, which is likely incorrect once the access place error has been + // resolved. + if rw == ReadOrWrite::Write(WriteKind::Mutate) + && let Ok(root_place) = + self.is_mutable(place_span.0.as_ref(), is_local_mutation_allowed) + { + self.add_used_mut(root_place, state); + } + return; } diff --git a/tests/ui/lint/unused/mut-used-despite-borrowck-error.rs b/tests/ui/lint/unused/mut-used-despite-borrowck-error.rs new file mode 100644 index 0000000000000..7f1a51bc85105 --- /dev/null +++ b/tests/ui/lint/unused/mut-used-despite-borrowck-error.rs @@ -0,0 +1,19 @@ +//! Do not fire unused_mut lint when mutation of the bound variable fails due to a borrow-checking +//! error. +//! +//! Regression test for https://github.com/rust-lang/rust/issues/152024 +//@ compile-flags: -W unused_mut + +struct Thing; +impl Drop for Thing { + fn drop(&mut self) {} +} + +fn main() { + let mut t; + let mut b = None; + loop { + t = Thing; //~ ERROR cannot assign to `t` because it is borrowed + b.insert(&t); + } +} diff --git a/tests/ui/lint/unused/mut-used-despite-borrowck-error.stderr b/tests/ui/lint/unused/mut-used-despite-borrowck-error.stderr new file mode 100644 index 0000000000000..c03cf3f7c6093 --- /dev/null +++ b/tests/ui/lint/unused/mut-used-despite-borrowck-error.stderr @@ -0,0 +1,13 @@ +error[E0506]: cannot assign to `t` because it is borrowed + --> $DIR/mut-used-despite-borrowck-error.rs:16:9 + | +LL | t = Thing; + | ^ `t` is assigned to here but it was already borrowed +LL | b.insert(&t); + | - -- `t` is borrowed here + | | + | borrow later used here + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0506`. From 80a4257fdfd47a05839d94c50fcd9427c330f355 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= Date: Tue, 3 Feb 2026 21:46:06 +0100 Subject: [PATCH 5/5] Weaken `assert_dep_node_not_yet_allocated_in_current_session` for multiple threads --- compiler/rustc_codegen_ssa/src/base.rs | 2 +- .../rustc_query_system/src/dep_graph/graph.rs | 29 ++++++++++++++----- 2 files changed, 22 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/base.rs b/compiler/rustc_codegen_ssa/src/base.rs index 43767dff92bfc..85c8890d661c5 100644 --- a/compiler/rustc_codegen_ssa/src/base.rs +++ b/compiler/rustc_codegen_ssa/src/base.rs @@ -1111,7 +1111,7 @@ pub fn determine_cgu_reuse<'tcx>(tcx: TyCtxt<'tcx>, cgu: &CodegenUnit<'tcx>) -> // know that later). If we are not doing LTO, there is only one optimized // version of each module, so we re-use that. let dep_node = cgu.codegen_dep_node(tcx); - tcx.dep_graph.assert_dep_node_not_yet_allocated_in_current_session(&dep_node, || { + tcx.dep_graph.assert_dep_node_not_yet_allocated_in_current_session(tcx.sess, &dep_node, || { format!( "CompileCodegenUnit dep-node for CGU `{}` already exists before marking.", cgu.name() diff --git a/compiler/rustc_query_system/src/dep_graph/graph.rs b/compiler/rustc_query_system/src/dep_graph/graph.rs index 06e576baf22ad..450f4e546456e 100644 --- a/compiler/rustc_query_system/src/dep_graph/graph.rs +++ b/compiler/rustc_query_system/src/dep_graph/graph.rs @@ -336,13 +336,17 @@ impl DepGraphData { // in `DepGraph::try_mark_green()`. // 2. Two distinct query keys get mapped to the same `DepNode` // (see for example #48923). - self.assert_dep_node_not_yet_allocated_in_current_session(&key, || { - format!( - "forcing query with already existing `DepNode`\n\ + self.assert_dep_node_not_yet_allocated_in_current_session( + cx.dep_context().sess(), + &key, + || { + format!( + "forcing query with already existing `DepNode`\n\ - query-key: {arg:?}\n\ - dep-node: {key:?}" - ) - }); + ) + }, + ); let with_deps = |task_deps| D::with_deps(task_deps, || task(cx, arg)); let (result, edges) = if cx.dep_context().is_eval_always(key.kind) { @@ -627,12 +631,20 @@ impl DepGraph { impl DepGraphData { fn assert_dep_node_not_yet_allocated_in_current_session( &self, + sess: &Session, dep_node: &DepNode, msg: impl FnOnce() -> S, ) { if let Some(prev_index) = self.previous.node_to_index_opt(dep_node) { - let current = self.colors.get(prev_index); - assert_matches!(current, DepNodeColor::Unknown, "{}", msg()) + let color = self.colors.get(prev_index); + let ok = match color { + DepNodeColor::Unknown => true, + DepNodeColor::Red => false, + DepNodeColor::Green(..) => sess.threads() > 1, // Other threads may mark this green + }; + if !ok { + panic!("{}", msg()) + } } else if let Some(nodes_in_current_session) = &self.current.nodes_in_current_session { outline(|| { let seen = nodes_in_current_session.lock().contains_key(dep_node); @@ -1035,11 +1047,12 @@ impl DepGraph { pub fn assert_dep_node_not_yet_allocated_in_current_session( &self, + sess: &Session, dep_node: &DepNode, msg: impl FnOnce() -> S, ) { if let Some(data) = &self.data { - data.assert_dep_node_not_yet_allocated_in_current_session(dep_node, msg) + data.assert_dep_node_not_yet_allocated_in_current_session(sess, dep_node, msg) } }