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

`}

`