diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs index 8b0761889b8..a0c9b43d5af 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/conflict_errors.rs @@ -453,6 +453,7 @@ pub(in crate::borrow_check) fn report_move_out_while_borrowed( &mut err, "", Some(borrow_span), + None, ); err.buffer(&mut self.errors_buffer); } @@ -498,6 +499,7 @@ pub(in crate::borrow_check) fn report_use_while_mutably_borrowed( &mut err, "", None, + None, ); err } @@ -718,6 +720,7 @@ pub(in crate::borrow_check) fn report_conflicting_borrow( &mut err, first_borrow_desc, None, + Some((issued_span, span)), ); err @@ -1076,6 +1079,7 @@ fn report_local_value_does_not_live_long_enough( &mut err, "", None, + None, ); } } else { @@ -1093,6 +1097,7 @@ fn report_local_value_does_not_live_long_enough( &mut err, "", None, + None, ); } @@ -1158,6 +1163,7 @@ fn report_borrow_conflicts_with_destructor( &mut err, "", None, + None, ); err.buffer(&mut self.errors_buffer); @@ -1236,6 +1242,7 @@ fn report_temporary_value_does_not_live_long_enough( &mut err, "", None, + None, ); let within = if borrow_spans.for_generator() { " by generator" } else { "" }; @@ -1614,6 +1621,7 @@ pub(in crate::borrow_check) fn report_illegal_mutation_of_borrowed( &mut err, "", None, + None, ); self.explain_deref_coercion(loan, &mut err); diff --git a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs index e9f1ecb9bbc..76de010d139 100644 --- a/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs +++ b/compiler/rustc_mir/src/borrow_check/diagnostics/explain_borrow.rs @@ -66,6 +66,7 @@ pub(in crate::borrow_check) fn add_explanation_to_diagnostic<'tcx>( err: &mut DiagnosticBuilder<'_>, borrow_desc: &str, borrow_span: Option, + multiple_borrow_span: Option<(Span, Span)>, ) { match *self { BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => { @@ -192,14 +193,23 @@ pub(in crate::borrow_check) fn add_explanation_to_diagnostic<'tcx>( if let Some(info) = &local_decl.is_block_tail { if info.tail_result_is_ignored { - err.span_suggestion_verbose( - info.span.shrink_to_hi(), - "consider adding semicolon after the expression so its \ - temporaries are dropped sooner, before the local variables \ - declared by the block are dropped", - ";".to_string(), - Applicability::MaybeIncorrect, - ); + // #85581: If the first mutable borrow's scope contains + // the second borrow, this suggestion isn't helpful. + if !multiple_borrow_span + .map(|(old, new)| { + old.to(info.span.shrink_to_hi()).contains(new) + }) + .unwrap_or(false) + { + err.span_suggestion_verbose( + info.span.shrink_to_hi(), + "consider adding semicolon after the expression so its \ + temporaries are dropped sooner, before the local variables \ + declared by the block are dropped", + ";".to_string(), + Applicability::MaybeIncorrect, + ); + } } else { err.note( "the temporary is part of an expression at the end of a \ diff --git a/src/test/ui/borrowck/issue-85581.rs b/src/test/ui/borrowck/issue-85581.rs new file mode 100644 index 00000000000..ccc120c5421 --- /dev/null +++ b/src/test/ui/borrowck/issue-85581.rs @@ -0,0 +1,15 @@ +// Regression test of #85581. +// Checks not to suggest to add `;` when the second mutable borrow +// is in the first's scope. + +use std::collections::BinaryHeap; + +fn foo(heap: &mut BinaryHeap) { + match heap.peek_mut() { + Some(_) => { heap.pop(); }, + //~^ ERROR: cannot borrow `*heap` as mutable more than once at a time + None => (), + } +} + +fn main() {} diff --git a/src/test/ui/borrowck/issue-85581.stderr b/src/test/ui/borrowck/issue-85581.stderr new file mode 100644 index 00000000000..29c0429f2a0 --- /dev/null +++ b/src/test/ui/borrowck/issue-85581.stderr @@ -0,0 +1,17 @@ +error[E0499]: cannot borrow `*heap` as mutable more than once at a time + --> $DIR/issue-85581.rs:9:22 + | +LL | match heap.peek_mut() { + | --------------- + | | + | first mutable borrow occurs here + | a temporary with access to the first borrow is created here ... +LL | Some(_) => { heap.pop(); }, + | ^^^^ second mutable borrow occurs here +... +LL | } + | - ... and the first borrow might be used here, when that temporary is dropped and runs the destructor for type `Option>` + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0499`.