Skip to content

Borrowck diag mig in one #101042

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

use rustc_errors::{DiagnosticBuilder, ErrorGuaranteed};
use rustc_infer::infer::canonical::Canonical;
use rustc_infer::infer::error_reporting::nice_region_error::NiceRegionError;
Expand Down
239 changes: 143 additions & 96 deletions compiler/rustc_borrowck/src/diagnostics/explain_borrow.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// #![deny(rustc::untranslatable_diagnostic)]
// #![deny(rustc::diagnostic_outside_of_impl)]

//! Print diagnostics to explain why values are borrowed.

use std::collections::VecDeque;
Expand All @@ -16,6 +19,10 @@ use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, DesugaringKind, Span};

use crate::region_infer::BlameConstraint;
use crate::session_diagnostics::{
BorrowLaterBorrowUsedLaterInLoop, BorrowUsedHere, BorrowUsedLater, BorrowUsedLaterInLoop,
MustValidFor, UsedLaterDropped,
};
use crate::{
borrow_set::BorrowData, nll::ConstraintDescription, region_infer::Cause, MirBorrowckCtxt,
WriteKind,
Expand Down Expand Up @@ -67,65 +74,121 @@ impl<'tcx> BorrowExplanation<'tcx> {
) {
match *self {
BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => {
let message = match later_use_kind {
LaterUseKind::TraitCapture => "captured here by trait object",
LaterUseKind::ClosureCapture => "captured here by closure",
LaterUseKind::Call => "used by call",
LaterUseKind::FakeLetRead => "stored here",
LaterUseKind::Other => "used here",
};
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) {
if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) {
err.span_label(
var_or_use_span,
format!("{}borrow later {}", borrow_desc, message),
);
let sub_err = match later_use_kind {
LaterUseKind::TraitCapture => {
BorrowUsedLater::TraitCapture { borrow_desc, span: var_or_use_span }
}
LaterUseKind::ClosureCapture => BorrowUsedLater::ClosureCapture {
borrow_desc,
span: var_or_use_span,
},
LaterUseKind::Call => {
BorrowUsedLater::Call { borrow_desc, span: var_or_use_span }
}
LaterUseKind::FakeLetRead => {
BorrowUsedLater::FakeLetRead { borrow_desc, span: var_or_use_span }
}
LaterUseKind::Other => {
BorrowUsedLater::Other { borrow_desc, span: var_or_use_span }
}
};
err.subdiagnostic(sub_err);
}
} else {
// path_span must be `Some` as otherwise the if condition is true
let path_span = path_span.unwrap();
// path_span is only present in the case of closure capture
assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture));
if !borrow_span.map_or(false, |sp| sp.overlaps(var_or_use_span)) {
let path_label = "used here by closure";
let capture_kind_label = message;
err.span_label(
var_or_use_span,
format!("{}borrow later {}", borrow_desc, capture_kind_label),
);
err.span_label(path_span, path_label);
let sub_err = match later_use_kind {
LaterUseKind::TraitCapture => {
BorrowUsedLater::TraitCapture { borrow_desc, span: var_or_use_span }
}
LaterUseKind::ClosureCapture => BorrowUsedLater::ClosureCapture {
borrow_desc,
span: var_or_use_span,
},
LaterUseKind::Call => {
BorrowUsedLater::Call { borrow_desc, span: var_or_use_span }
}
LaterUseKind::FakeLetRead => {
BorrowUsedLater::FakeLetRead { borrow_desc, span: var_or_use_span }
}
LaterUseKind::Other => {
BorrowUsedLater::Other { borrow_desc, span: var_or_use_span }
}
};
err.subdiagnostic(sub_err);
let sub_label = BorrowUsedHere::ByClosure { path_span };
err.subdiagnostic(sub_label);
}
}
}
BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span) => {
let message = match later_use_kind {
LaterUseKind::TraitCapture => {
"borrow captured here by trait object, in later iteration of loop"
}
LaterUseKind::ClosureCapture => {
"borrow captured here by closure, in later iteration of loop"
}
LaterUseKind::Call => "borrow used by call, in later iteration of loop",
LaterUseKind::FakeLetRead => "borrow later stored here",
LaterUseKind::Other => "borrow used here, in later iteration of loop",
};
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
if path_span.map(|path_span| path_span == var_or_use_span).unwrap_or(true) {
err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
let sub_err = match later_use_kind {
LaterUseKind::TraitCapture => BorrowUsedLaterInLoop::TraitCapture {
borrow_desc,
span: var_or_use_span,
},
LaterUseKind::ClosureCapture => BorrowUsedLaterInLoop::ClosureCapture {
borrow_desc,
span: var_or_use_span,
},
LaterUseKind::Call => {
BorrowUsedLaterInLoop::Call { borrow_desc, span: var_or_use_span }
}
LaterUseKind::FakeLetRead => BorrowUsedLaterInLoop::FakeLetRead {
borrow_desc,
span: var_or_use_span,
},
LaterUseKind::Other => {
BorrowUsedLaterInLoop::Other { borrow_desc, span: var_or_use_span }
}
};
err.subdiagnostic(sub_err);
// err.span_label(var_or_use_span, format!("{}{}", borrow_desc, message));
} else {
// path_span must be `Some` as otherwise the if condition is true
let path_span = path_span.unwrap();
// path_span is only present in the case of closure capture
assert!(matches!(later_use_kind, LaterUseKind::ClosureCapture));
if borrow_span.map(|sp| !sp.overlaps(var_or_use_span)).unwrap_or(true) {
let path_label = "used here by closure";
let capture_kind_label = message;
err.span_label(
var_or_use_span,
format!("{}borrow later {}", borrow_desc, capture_kind_label),
);
err.span_label(path_span, path_label);
let sub_err = match later_use_kind {
LaterUseKind::TraitCapture => {
BorrowLaterBorrowUsedLaterInLoop::TraitCapture {
borrow_desc,
span: var_or_use_span,
}
}
LaterUseKind::ClosureCapture => {
BorrowLaterBorrowUsedLaterInLoop::ClosureCapture {
borrow_desc,
span: var_or_use_span,
}
}
LaterUseKind::Call => BorrowLaterBorrowUsedLaterInLoop::Call {
borrow_desc,
span: var_or_use_span,
},
LaterUseKind::FakeLetRead => {
BorrowLaterBorrowUsedLaterInLoop::FakeLetRead {
borrow_desc,
span: var_or_use_span,
}
}
LaterUseKind::Other => BorrowLaterBorrowUsedLaterInLoop::Other {
borrow_desc,
span: var_or_use_span,
},
};
err.subdiagnostic(sub_err);
let sub_label = BorrowUsedHere::ByClosure { path_span };
err.subdiagnostic(sub_label);
}
}
}
Expand Down Expand Up @@ -161,41 +224,33 @@ impl<'tcx> BorrowExplanation<'tcx> {

match local_names[dropped_local] {
Some(local_name) if !local_decl.from_compiler_desugaring() => {
let message = format!(
"{B}borrow might be used here, when `{LOC}` is dropped \
and runs the {DTOR} for {TYPE}",
B = borrow_desc,
LOC = local_name,
TYPE = type_desc,
DTOR = dtor_desc
);
err.span_label(body.source_info(drop_loc).span, message);
let sub_label = UsedLaterDropped::UsedHere {
borrow_desc,
local_name: &local_name.to_ident_string(),
type_desc: &type_desc,
dtor_desc,
span: body.source_info(drop_loc).span,
};
err.subdiagnostic(sub_label);

if should_note_order {
err.note(
"values in a scope are dropped \
in the opposite order they are defined",
);
let sub_note = UsedLaterDropped::OppositeOrder {};
err.subdiagnostic(sub_note);
}
}
_ => {
err.span_label(
local_decl.source_info.span,
format!(
"a temporary with access to the {B}borrow \
is created here ...",
B = borrow_desc
),
);
let message = format!(
"... and the {B}borrow might be used here, \
when that temporary is dropped \
and runs the {DTOR} for {TYPE}",
B = borrow_desc,
TYPE = type_desc,
DTOR = dtor_desc
);
err.span_label(body.source_info(drop_loc).span, message);
let sub_label = UsedLaterDropped::TemporaryCreatedHere {
borrow_desc,
span: local_decl.source_info.span,
};
err.subdiagnostic(sub_label);
let sub_label_2 = UsedLaterDropped::MightUsedHere {
borrow_desc,
type_desc: &type_desc,
dtor_desc,
span: body.source_info(drop_loc).span,
};
err.subdiagnostic(sub_label_2);

if let Some(info) = &local_decl.is_block_tail {
if info.tail_result_is_ignored {
Expand All @@ -207,21 +262,16 @@ impl<'tcx> BorrowExplanation<'tcx> {
})
.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",
";",
Applicability::MaybeIncorrect,
);
let sub_suggest = UsedLaterDropped::AddSemicolon {
span: info.span.shrink_to_hi(),
};
err.subdiagnostic(sub_suggest);
}
} else {
err.note(
"the temporary is part of an expression at the end of a \
block;\nconsider forcing this temporary to be dropped sooner, \
before the block's local variables are dropped",
);
let sub_note = UsedLaterDropped::ManualDrop {};
err.subdiagnostic(sub_note);

//FIXME: waiting for multipart suggestion derive
err.multipart_suggestion(
"for example, you could save the expression's value in a new \
local variable `x` and then make `x` be the expression at the \
Expand All @@ -247,25 +297,22 @@ impl<'tcx> BorrowExplanation<'tcx> {
region_name.highlight_region_name(err);

if let Some(desc) = opt_place_desc {
err.span_label(
let sub_label = MustValidFor::Borrowed {
category: category.description(),
desc,
region_name,
span,
format!(
"{}requires that `{}` is borrowed for `{}`",
category.description(),
desc,
region_name,
),
);
};
err.subdiagnostic(sub_label);
} else {
err.span_label(
//FIXME: src/test/ui/consts/const-eval/const-eval-intrinsic-promotion.rs
let sub_label = MustValidFor::Lasts {
category: category.description(),
borrow_desc,
region_name,
span,
format!(
"{}requires that {}borrow lasts for `{}`",
category.description(),
borrow_desc,
region_name,
),
);
};
err.subdiagnostic(sub_label);
};

self.add_lifetime_bound_suggestion_to_diagnostic(err, &category, span, region_name);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

use std::collections::BTreeSet;

use rustc_middle::mir::visit::{PlaceContext, Visitor};
Expand Down
3 changes: 3 additions & 0 deletions compiler/rustc_borrowck/src/diagnostics/find_use.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
#![deny(rustc::untranslatable_diagnostic)]
#![deny(rustc::diagnostic_outside_of_impl)]

use std::collections::VecDeque;
use std::rc::Rc;

Expand Down
15 changes: 7 additions & 8 deletions compiler/rustc_borrowck/src/diagnostics/mutability_errors.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// #![deny(rustc::untranslatable_diagnostic)]
// #![deny(rustc::diagnostic_outside_of_impl)]

use rustc_errors::{
Applicability, Diagnostic, DiagnosticBuilder, EmissionGuarantee, ErrorGuaranteed,
};
Expand All @@ -19,6 +22,7 @@ use rustc_span::symbol::{kw, Symbol};
use rustc_span::{sym, BytePos, Span};

use crate::diagnostics::BorrowedContentSource;
use crate::session_diagnostics::ShowMutatingUpvar;
use crate::MirBorrowckCtxt;
use rustc_const_eval::util::collect_writes::FindAssignments;

Expand Down Expand Up @@ -864,14 +868,9 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
} else {
bug!("not an upvar")
};
err.span_label(
*span,
format!(
"calling `{}` requires mutable binding due to {}",
self.describe_place(the_place_err).unwrap(),
reason
),
);
let place = self.describe_place(the_place_err).unwrap();
let sub_label = ShowMutatingUpvar::RequireMutableBinding { place, reason, span: *span };
err.subdiagnostic(sub_label);
}
}

Expand Down
Loading