rescope temp lifetime in let-chain into IfElse
apply rules by span edition
This commit is contained in:
parent
33855f80d4
commit
f93df1f7dc
30 changed files with 1102 additions and 35 deletions
|
@ -1999,19 +1999,32 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
) {
|
||||
let used_in_call = matches!(
|
||||
explanation,
|
||||
BorrowExplanation::UsedLater(LaterUseKind::Call | LaterUseKind::Other, _call_span, _)
|
||||
BorrowExplanation::UsedLater(
|
||||
_,
|
||||
LaterUseKind::Call | LaterUseKind::Other,
|
||||
_call_span,
|
||||
_
|
||||
)
|
||||
);
|
||||
if !used_in_call {
|
||||
debug!("not later used in call");
|
||||
return;
|
||||
}
|
||||
if matches!(
|
||||
self.body.local_decls[issued_borrow.borrowed_place.local].local_info(),
|
||||
LocalInfo::IfThenRescopeTemp { .. }
|
||||
) {
|
||||
// A better suggestion will be issued by the `if_let_rescope` lint
|
||||
return;
|
||||
}
|
||||
|
||||
let use_span =
|
||||
if let BorrowExplanation::UsedLater(LaterUseKind::Other, use_span, _) = explanation {
|
||||
Some(use_span)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let use_span = if let BorrowExplanation::UsedLater(_, LaterUseKind::Other, use_span, _) =
|
||||
explanation
|
||||
{
|
||||
Some(use_span)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let outer_call_loc =
|
||||
if let TwoPhaseActivation::ActivatedAt(loc) = issued_borrow.activation_location {
|
||||
|
@ -2862,7 +2875,7 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> {
|
|||
// and `move` will not help here.
|
||||
(
|
||||
Some(name),
|
||||
BorrowExplanation::UsedLater(LaterUseKind::ClosureCapture, var_or_use_span, _),
|
||||
BorrowExplanation::UsedLater(_, LaterUseKind::ClosureCapture, var_or_use_span, _),
|
||||
) if borrow_spans.for_coroutine() || borrow_spans.for_closure() => self
|
||||
.report_escaping_closure_capture(
|
||||
borrow_spans,
|
||||
|
|
|
@ -30,7 +30,7 @@ use crate::{MirBorrowckCtxt, WriteKind};
|
|||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum BorrowExplanation<'tcx> {
|
||||
UsedLater(LaterUseKind, Span, Option<Span>),
|
||||
UsedLater(Local, LaterUseKind, Span, Option<Span>),
|
||||
UsedLaterInLoop(LaterUseKind, Span, Option<Span>),
|
||||
UsedLaterWhenDropped {
|
||||
drop_loc: Location,
|
||||
|
@ -99,7 +99,12 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
}
|
||||
}
|
||||
match *self {
|
||||
BorrowExplanation::UsedLater(later_use_kind, var_or_use_span, path_span) => {
|
||||
BorrowExplanation::UsedLater(
|
||||
dropped_local,
|
||||
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",
|
||||
|
@ -107,9 +112,26 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
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) {
|
||||
let local_decl = &body.local_decls[dropped_local];
|
||||
|
||||
if let &LocalInfo::IfThenRescopeTemp { if_then } = local_decl.local_info()
|
||||
&& let Some((_, hir::Node::Expr(expr))) = tcx.hir().parent_iter(if_then).next()
|
||||
&& let hir::ExprKind::If(cond, conseq, alt) = expr.kind
|
||||
&& let hir::ExprKind::Let(&hir::LetExpr {
|
||||
span: _,
|
||||
pat,
|
||||
init,
|
||||
// FIXME(#101728): enable rewrite when type ascription is stabilized again
|
||||
ty: None,
|
||||
recovered: _,
|
||||
}) = cond.kind
|
||||
&& pat.span.can_be_used_for_suggestions()
|
||||
&& let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
|
||||
{
|
||||
suggest_rewrite_if_let(expr, &pat, init, conseq, alt, err);
|
||||
} else if path_span.map_or(true, |path_span| path_span == var_or_use_span) {
|
||||
// We can use `var_or_use_span` if either `path_span` is not present, or both spans are the same
|
||||
if borrow_span.map_or(true, |sp| !sp.overlaps(var_or_use_span)) {
|
||||
err.span_label(
|
||||
var_or_use_span,
|
||||
format!("{borrow_desc}borrow later {message}"),
|
||||
|
@ -255,6 +277,22 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
};
|
||||
} else if let &LocalInfo::IfThenRescopeTemp { if_then } =
|
||||
local_decl.local_info()
|
||||
&& let hir::Node::Expr(expr) = tcx.hir_node(if_then)
|
||||
&& let hir::ExprKind::If(cond, conseq, alt) = expr.kind
|
||||
&& let hir::ExprKind::Let(&hir::LetExpr {
|
||||
span: _,
|
||||
pat,
|
||||
init,
|
||||
// FIXME(#101728): enable rewrite when type ascription is stabilized again
|
||||
ty: None,
|
||||
recovered: _,
|
||||
}) = cond.kind
|
||||
&& pat.span.can_be_used_for_suggestions()
|
||||
&& let Ok(pat) = tcx.sess.source_map().span_to_snippet(pat.span)
|
||||
{
|
||||
suggest_rewrite_if_let(expr, &pat, init, conseq, alt, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -390,6 +428,38 @@ impl<'tcx> BorrowExplanation<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn suggest_rewrite_if_let<'tcx>(
|
||||
expr: &hir::Expr<'tcx>,
|
||||
pat: &str,
|
||||
init: &hir::Expr<'tcx>,
|
||||
conseq: &hir::Expr<'tcx>,
|
||||
alt: Option<&hir::Expr<'tcx>>,
|
||||
err: &mut Diag<'_>,
|
||||
) {
|
||||
err.span_note(
|
||||
conseq.span.shrink_to_hi(),
|
||||
"lifetime for temporaries generated in `if let`s have been shorted in Edition 2024",
|
||||
);
|
||||
if expr.span.can_be_used_for_suggestions() && conseq.span.can_be_used_for_suggestions() {
|
||||
let mut sugg = vec![
|
||||
(expr.span.shrink_to_lo().between(init.span), "match ".into()),
|
||||
(conseq.span.shrink_to_lo(), format!(" {{ {pat} => ")),
|
||||
];
|
||||
let expr_end = expr.span.shrink_to_hi();
|
||||
if let Some(alt) = alt {
|
||||
sugg.push((conseq.span.between(alt.span), format!(" _ => ")));
|
||||
sugg.push((expr_end, "}".into()));
|
||||
} else {
|
||||
sugg.push((expr_end, " _ => {} }".into()));
|
||||
}
|
||||
err.multipart_suggestion(
|
||||
"consider rewriting the `if` into `match` which preserves the extended lifetime",
|
||||
sugg,
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
||||
fn free_region_constraint_info(
|
||||
&self,
|
||||
|
@ -465,14 +535,21 @@ impl<'tcx> MirBorrowckCtxt<'_, '_, 'tcx> {
|
|||
.or_else(|| self.borrow_spans(span, location));
|
||||
|
||||
if use_in_later_iteration_of_loop {
|
||||
let later_use = self.later_use_kind(borrow, spans, use_location);
|
||||
BorrowExplanation::UsedLaterInLoop(later_use.0, later_use.1, later_use.2)
|
||||
let (later_use_kind, var_or_use_span, path_span) =
|
||||
self.later_use_kind(borrow, spans, use_location);
|
||||
BorrowExplanation::UsedLaterInLoop(later_use_kind, var_or_use_span, path_span)
|
||||
} else {
|
||||
// Check if the location represents a `FakeRead`, and adapt the error
|
||||
// message to the `FakeReadCause` it is from: in particular,
|
||||
// the ones inserted in optimized `let var = <expr>` patterns.
|
||||
let later_use = self.later_use_kind(borrow, spans, location);
|
||||
BorrowExplanation::UsedLater(later_use.0, later_use.1, later_use.2)
|
||||
let (later_use_kind, var_or_use_span, path_span) =
|
||||
self.later_use_kind(borrow, spans, location);
|
||||
BorrowExplanation::UsedLater(
|
||||
borrow.borrowed_place.local,
|
||||
later_use_kind,
|
||||
var_or_use_span,
|
||||
path_span,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue