Auto merge of #137608 - fmease:rollup-h4siso6, r=fmease · rust-lang/rust@85abb27 (original) (raw)
1
1
`use std::iter::repeat;
`
2
2
`use std::ops::ControlFlow;
`
3
3
``
4
``
`-
use hir::intravisit::Visitor;
`
``
4
`+
use hir::intravisit::{self, Visitor};
`
5
5
`use rustc_ast::Recovered;
`
6
6
`use rustc_errors::{
`
7
7
`Applicability, Diag, EmissionGuarantee, SubdiagMessageOp, Subdiagnostic, SuggestionStyle,
`
8
8
`};
`
9
9
`use rustc_hir::{self as hir, HirIdSet};
`
10
10
`use rustc_macros::LintDiagnostic;
`
11
11
`use rustc_middle::ty::TyCtxt;
`
``
12
`+
use rustc_middle::ty::adjustment::Adjust;
`
12
13
`use rustc_session::lint::{FutureIncompatibilityReason, LintId};
`
13
14
`use rustc_session::{declare_lint, impl_lint_pass};
`
14
15
`use rustc_span::Span;
`
`@@ -160,7 +161,7 @@ impl IfLetRescope {
`
160
161
`let lifetime_end = source_map.end_point(conseq.span);
`
161
162
``
162
163
`if let ControlFlow::Break(significant_dropper) =
`
163
``
`-
(FindSignificantDropper { cx }).visit_expr(init)
`
``
164
`+
(FindSignificantDropper { cx }).check_if_let_scrutinee(init)
`
164
165
`{
`
165
166
` first_if_to_lint = first_if_to_lint.or_else(|| Some((span, expr.hir_id)));
`
166
167
` significant_droppers.push(significant_dropper);
`
`@@ -363,96 +364,97 @@ enum SingleArmMatchBegin {
`
363
364
`WithoutOpenBracket(Span),
`
364
365
`}
`
365
366
``
366
``
`-
struct FindSignificantDropper<'tcx, 'a> {
`
``
367
`+
struct FindSignificantDropper<'a, 'tcx> {
`
367
368
`cx: &'a LateContext<'tcx>,
`
368
369
`}
`
369
370
``
370
``
`-
impl<'tcx, 'a> Visitor<'tcx> for FindSignificantDropper<'tcx, 'a> {
`
371
``
`-
type Result = ControlFlow;
`
``
371
`+
impl<'tcx> FindSignificantDropper<'_, 'tcx> {
`
``
372
`` +
/// Check the scrutinee of an if let
to see if it promotes any temporary values
``
``
373
`+
/// that would change drop order in edition 2024. Specifically, it checks the value
`
``
374
`+
/// of the scrutinee itself, and also recurses into the expression to find any ref
`
``
375
`+
/// exprs (or autoref) which would promote temporaries that would be scoped to the
`
``
376
`` +
/// end of this if
.
``
``
377
`+
fn check_if_let_scrutinee(&mut self, init: &'tcx hir::Expr<'tcx>) -> ControlFlow {
`
``
378
`+
self.check_promoted_temp_with_drop(init)?;
`
``
379
`+
self.visit_expr(init)
`
``
380
`+
}
`
372
381
``
373
``
`-
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
`
374
``
`-
if self
`
``
382
`+
/// Check that an expression is not a promoted temporary with a significant
`
``
383
`+
/// drop impl.
`
``
384
`+
///
`
``
385
`` +
/// An expression is a promoted temporary if it has an addr taken (i.e. &expr
or autoref)
``
``
386
`` +
/// or is the scrutinee of the if let
, and the expression is not a place
``
``
387
`+
/// expr, and it has a significant drop.
`
``
388
`+
fn check_promoted_temp_with_drop(&self, expr: &'tcx hir::Expr<'tcx>) -> ControlFlow {
`
``
389
`+
if !expr.is_place_expr(|base| {
`
``
390
`+
self.cx
`
``
391
`+
.typeck_results()
`
``
392
`+
.adjustments()
`
``
393
`+
.get(base.hir_id)
`
``
394
`+
.is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_))))
`
``
395
`+
}) && self
`
375
396
`.cx
`
376
397
`.typeck_results()
`
377
398
`.expr_ty(expr)
`
378
399
`.has_significant_drop(self.cx.tcx, self.cx.typing_env())
`
379
400
`{
`
380
``
`-
return ControlFlow::Break(expr.span);
`
``
401
`+
ControlFlow::Break(expr.span)
`
``
402
`+
} else {
`
``
403
`+
ControlFlow::Continue(())
`
381
404
`}
`
382
``
`-
match expr.kind {
`
383
``
`-
hir::ExprKind::ConstBlock(_)
`
384
``
`-
| hir::ExprKind::Lit(_)
`
385
``
`-
| hir::ExprKind::Path(_)
`
386
``
`-
| hir::ExprKind::Assign(_, _, _)
`
387
``
`-
| hir::ExprKind::AssignOp(_, _, _)
`
388
``
`-
| hir::ExprKind::Break(_, _)
`
389
``
`-
| hir::ExprKind::Continue(_)
`
390
``
`-
| hir::ExprKind::Ret(_)
`
391
``
`-
| hir::ExprKind::Become(_)
`
392
``
`-
| hir::ExprKind::InlineAsm(_)
`
393
``
`-
| hir::ExprKind::OffsetOf(_, _)
`
394
``
`-
| hir::ExprKind::Repeat(_, _)
`
395
``
`-
| hir::ExprKind::Err(_)
`
396
``
`-
| hir::ExprKind::Struct(_, _, _)
`
397
``
`-
| hir::ExprKind::Closure(_)
`
398
``
`-
| hir::ExprKind::Block(_, _)
`
399
``
`-
| hir::ExprKind::DropTemps(_)
`
400
``
`-
| hir::ExprKind::Loop(_, _, _, _) => ControlFlow::Continue(()),
`
``
405
`+
}
`
``
406
`+
}
`
401
407
``
402
``
`-
hir::ExprKind::Tup(exprs) | hir::ExprKind::Array(exprs) => {
`
403
``
`-
for expr in exprs {
`
404
``
`-
self.visit_expr(expr)?;
`
405
``
`-
}
`
406
``
`-
ControlFlow::Continue(())
`
407
``
`-
}
`
408
``
`-
hir::ExprKind::Call(callee, args) => {
`
409
``
`-
self.visit_expr(callee)?;
`
410
``
`-
for expr in args {
`
411
``
`-
self.visit_expr(expr)?;
`
412
``
`-
}
`
413
``
`-
ControlFlow::Continue(())
`
414
``
`-
}
`
415
``
`-
hir::ExprKind::MethodCall(_, receiver, args, _) => {
`
416
``
`-
self.visit_expr(receiver)?;
`
417
``
`-
for expr in args {
`
418
``
`-
self.visit_expr(expr)?;
`
``
408
`+
impl<'tcx> Visitor<'tcx> for FindSignificantDropper<'_, 'tcx> {
`
``
409
`+
type Result = ControlFlow;
`
``
410
+
``
411
`+
fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) -> Self::Result {
`
``
412
`+
// Blocks introduce temporary terminating scope for all of its
`
``
413
`+
// statements, so just visit the tail expr, skipping over any
`
``
414
`` +
// statements. This prevents false positives like { let x = &Drop; }
.
``
``
415
`+
if let Some(expr) = b.expr { self.visit_expr(expr) } else { ControlFlow::Continue(()) }
`
``
416
`+
}
`
``
417
+
``
418
`+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result {
`
``
419
`+
// Check for promoted temporaries from autoref, e.g.
`
``
420
`` +
// if let None = TypeWithDrop.as_ref() {} else {}
``
``
421
`` +
// where fn as_ref(&self) -> Option<...>
.
``
``
422
`+
for adj in self.cx.typeck_results().expr_adjustments(expr) {
`
``
423
`+
match adj.kind {
`
``
424
`+
// Skip when we hit the first deref expr.
`
``
425
`+
Adjust::Deref(_) => break,
`
``
426
`+
Adjust::Borrow(_) => {
`
``
427
`+
self.check_promoted_temp_with_drop(expr)?;
`
419
428
`}
`
420
``
`-
ControlFlow::Continue(())
`
421
``
`-
}
`
422
``
`-
hir::ExprKind::Index(left, right, ) | hir::ExprKind::Binary(, left, right) => {
`
423
``
`-
self.visit_expr(left)?;
`
424
``
`-
self.visit_expr(right)
`
``
429
`+
_ => {}
`
425
430
`}
`
426
``
`-
hir::ExprKind::Unary(_, expr)
`
427
``
`-
| hir::ExprKind::Cast(expr, _)
`
428
``
`-
| hir::ExprKind::Type(expr, _)
`
429
``
`-
| hir::ExprKind::UnsafeBinderCast(_, expr, _)
`
430
``
`-
| hir::ExprKind::Yield(expr, _)
`
431
``
`-
| hir::ExprKind::AddrOf(_, _, expr)
`
432
``
`-
| hir::ExprKind::Match(expr, _, _)
`
433
``
`-
| hir::ExprKind::Field(expr, _)
`
434
``
`-
| hir::ExprKind::Let(&hir::LetExpr {
`
435
``
`-
init: expr,
`
436
``
`-
span: _,
`
437
``
`-
pat: _,
`
438
``
`-
ty: _,
`
439
``
`-
recovered: Recovered::No,
`
440
``
`-
}) => self.visit_expr(expr),
`
441
``
`-
hir::ExprKind::Let(_) => ControlFlow::Continue(()),
`
``
431
`+
}
`
442
432
``
443
``
`-
hir::ExprKind::If(cond, _, _) => {
`
444
``
`-
if let hir::ExprKind::Let(hir::LetExpr {
`
445
``
`-
init,
`
446
``
`-
span: _,
`
447
``
`-
pat: _,
`
448
``
`-
ty: _,
`
449
``
`-
recovered: Recovered::No,
`
450
``
`-
}) = cond.kind
`
451
``
`-
{
`
452
``
`-
self.visit_expr(init)?;
`
453
``
`-
}
`
454
``
`-
ControlFlow::Continue(())
`
``
433
`+
match expr.kind {
`
``
434
`` +
// Account for cases like if let None = Some(&Drop) {} else {}
.
``
``
435
`+
hir::ExprKind::AddrOf(_, _, expr) => {
`
``
436
`+
self.check_promoted_temp_with_drop(expr)?;
`
``
437
`+
intravisit::walk_expr(self, expr)
`
``
438
`+
}
`
``
439
`` +
// (Drop, ()).1
introduces a temporary and then moves out of
``
``
440
`+
// part of it, therefore we should check it for temporaries.
`
``
441
`+
// FIXME: This may have false positives if we move the part
`
``
442
`+
// that actually has drop, but oh well.
`
``
443
`+
hir::ExprKind::Index(expr, _, _) | hir::ExprKind::Field(expr, _) => {
`
``
444
`+
self.check_promoted_temp_with_drop(expr)?;
`
``
445
`+
intravisit::walk_expr(self, expr)
`
455
446
`}
`
``
447
`+
// If always introduces a temporary terminating scope for its cond and arms,
`
``
448
`+
// so don't visit them.
`
``
449
`+
hir::ExprKind::If(..) => ControlFlow::Continue(()),
`
``
450
`+
// Match introduces temporary terminating scopes for arms, so don't visit
`
``
451
`+
// them, and only visit the scrutinee to account for cases like:
`
``
452
`` +
// if let None = match &Drop { _ => Some(1) } {} else {}
.
``
``
453
`+
hir::ExprKind::Match(scrut, _, _) => self.visit_expr(scrut),
`
``
454
`+
// Self explanatory.
`
``
455
`+
hir::ExprKind::DropTemps(_) => ControlFlow::Continue(()),
`
``
456
`+
// Otherwise, walk into the expr's parts.
`
``
457
`+
_ => intravisit::walk_expr(self, expr),
`
456
458
`}
`
457
459
`}
`
458
460
`}
`