|
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,81 @@ 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<Span>; |
| 371 | +impl<'tcx> FindSignificantDropper<'_, 'tcx> { |
| 372 | + fn check_if_let_scrutinee(&mut self, init: &'tcx hir::Expr<'tcx>) -> ControlFlow<Span> { |
| 373 | + self.check_promoted_temp_with_drop(init)?; |
| 374 | + self.visit_expr(init) |
| 375 | + } |
372 | 376 |
|
373 | | - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { |
374 | | - if self |
| 377 | + // Check that an expression is not a promoted temporary with a significant |
| 378 | + // drop impl. |
| 379 | + // |
| 380 | + // An expression is a promoted temporary if it has an addr taken (i.e. `&expr`) |
| 381 | + // or is the scrutinee of the `if let`, *and* the expression is not a place |
| 382 | + // expr, and it has a significant drop. |
| 383 | + fn check_promoted_temp_with_drop(&self, expr: &'tcx hir::Expr<'tcx>) -> ControlFlow<Span> { |
| 384 | + if !expr.is_place_expr(|base| { |
| 385 | + self.cx |
| 386 | + .typeck_results() |
| 387 | + .adjustments() |
| 388 | + .get(base.hir_id) |
| 389 | + .is_some_and(|x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) |
| 390 | + }) && self |
375 | 391 | .cx |
376 | 392 | .typeck_results() |
377 | 393 | .expr_ty(expr) |
378 | 394 | .has_significant_drop(self.cx.tcx, self.cx.typing_env()) |
379 | 395 | { |
380 | | - return ControlFlow::Break(expr.span); |
| 396 | + ControlFlow::Break(expr.span) |
| 397 | + } else { |
| 398 | + ControlFlow::Continue(()) |
381 | 399 | } |
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(()), |
| 400 | + } |
| 401 | +} |
401 | 402 |
|
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)?; |
| 403 | +impl<'tcx> Visitor<'tcx> for FindSignificantDropper<'_, 'tcx> { |
| 404 | + type Result = ControlFlow<Span>; |
| 405 | + |
| 406 | + fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) -> Self::Result { |
| 407 | + // Blocks introduce temporary terminating scope for all of its |
| 408 | + // statements, so just visit the tail expr. |
| 409 | + if let Some(expr) = b.expr { self.visit_expr(expr) } else { ControlFlow::Continue(()) } |
| 410 | + } |
| 411 | + |
| 412 | + fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) -> Self::Result { |
| 413 | + // Check for promoted temporaries from autoref, e.g. |
| 414 | + // `if let None = TypeWithDrop.as_ref() {} else {}` |
| 415 | + // where `fn as_ref(&self) -> Option<...>`. |
| 416 | + for adj in self.cx.typeck_results().expr_adjustments(expr) { |
| 417 | + match adj.kind { |
| 418 | + Adjust::Deref(_) => break, |
| 419 | + Adjust::Borrow(_) => { |
| 420 | + self.check_promoted_temp_with_drop(expr)?; |
419 | 421 | } |
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) |
| 422 | + _ => {} |
425 | 423 | } |
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(()), |
| 424 | + } |
442 | 425 |
|
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(()) |
| 426 | + match expr.kind { |
| 427 | + // Check for cases like `if let None = Some(&Drop) {} else {}`. |
| 428 | + hir::ExprKind::AddrOf(_, _, expr) => { |
| 429 | + self.check_promoted_temp_with_drop(expr)?; |
| 430 | + intravisit::walk_expr(self, expr) |
455 | 431 | } |
| 432 | + // If always introduces a temporary terminating scope for its cond and arms, |
| 433 | + // so don't visit them. |
| 434 | + hir::ExprKind::If(..) => ControlFlow::Continue(()), |
| 435 | + // Match introduces temporary terminating scopes for arms, so don't visit |
| 436 | + // them, and only visit the scrutinee to account for cases like: |
| 437 | + // `if let None = match &Drop { _ => Some(1) } {} else {}`. |
| 438 | + hir::ExprKind::Match(scrut, _, _) => self.visit_expr(scrut), |
| 439 | + // Self explanatory. |
| 440 | + hir::ExprKind::DropTemps(_) => ControlFlow::Continue(()), |
| 441 | + _ => intravisit::walk_expr(self, expr), |
456 | 442 | } |
457 | 443 | } |
458 | 444 | } |
0 commit comments