@@ -1320,84 +1320,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1320
1320
) -> Ty<'tcx> {
1321
1321
let expected_ty = expected.coercion_target_type(self, expr.span);
1322
1322
if expected_ty == self.tcx.types.bool {
1323
- // The expected type is `bool` but this will result in `()` so we can reasonably
1324
- // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`.
1325
- // The likely cause of this is `if foo = bar { .. }`.
1326
- let actual_ty = self.tcx.types.unit;
1327
- let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err();
1328
- let lhs_ty = self.check_expr(lhs);
1329
- let rhs_ty = self.check_expr(rhs);
1330
- let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| {
1331
- let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs());
1332
- let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs());
1333
- self.may_coerce(rhs, lhs)
1334
- };
1335
- let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) {
1336
- (Applicability::MachineApplicable, true)
1337
- } else if refs_can_coerce(rhs_ty, lhs_ty) {
1338
- // The lhs and rhs are likely missing some references in either side. Subsequent
1339
- // suggestions will show up.
1340
- (Applicability::MaybeIncorrect, true)
1341
- } else if let ExprKind::Binary(
1342
- Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
1343
- _,
1344
- rhs_expr,
1345
- ) = lhs.kind
1346
- {
1347
- // if x == 1 && y == 2 { .. }
1348
- // +
1349
- let actual_lhs_ty = self.check_expr(rhs_expr);
1350
- (
1351
- Applicability::MaybeIncorrect,
1352
- self.may_coerce(rhs_ty, actual_lhs_ty)
1353
- || refs_can_coerce(rhs_ty, actual_lhs_ty),
1354
- )
1355
- } else if let ExprKind::Binary(
1356
- Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
1357
- lhs_expr,
1358
- _,
1359
- ) = rhs.kind
1360
- {
1361
- // if x == 1 && y == 2 { .. }
1362
- // +
1363
- let actual_rhs_ty = self.check_expr(lhs_expr);
1364
- (
1365
- Applicability::MaybeIncorrect,
1366
- self.may_coerce(actual_rhs_ty, lhs_ty)
1367
- || refs_can_coerce(actual_rhs_ty, lhs_ty),
1368
- )
1369
- } else {
1370
- (Applicability::MaybeIncorrect, false)
1371
- };
1372
- if !lhs.is_syntactic_place_expr()
1373
- && lhs.is_approximately_pattern()
1374
- && !matches!(lhs.kind, hir::ExprKind::Lit(_))
1375
- {
1376
- // Do not suggest `if let x = y` as `==` is way more likely to be the intention.
1377
- if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
1378
- self.tcx.parent_hir_node(expr.hir_id)
1379
- {
1380
- err.span_suggestion_verbose(
1381
- expr.span.shrink_to_lo(),
1382
- "you might have meant to use pattern matching",
1383
- "let ",
1384
- applicability,
1385
- );
1386
- };
1387
- }
1388
- if eq {
1389
- err.span_suggestion_verbose(
1390
- span.shrink_to_hi(),
1391
- "you might have meant to compare for equality",
1392
- '=',
1393
- applicability,
1394
- );
1395
- }
1396
-
1397
- // If the assignment expression itself is ill-formed, don't
1398
- // bother emitting another error
1399
- let reported = err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error());
1400
- return Ty::new_error(self.tcx, reported);
1323
+ let guar = self.expr_assign_expected_bool_error(expr, lhs, rhs, span);
1324
+ return Ty::new_error(self.tcx, guar);
1401
1325
}
1402
1326
1403
1327
let lhs_ty = self.check_expr_with_needs(lhs, Needs::MutPlace);
@@ -1450,6 +1374,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
1450
1374
}
1451
1375
}
1452
1376
1377
+ /// The expected type is `bool` but this will result in `()` so we can reasonably
1378
+ /// say that the user intended to write `lhs == rhs` instead of `lhs = rhs`.
1379
+ /// The likely cause of this is `if foo = bar { .. }`.
1380
+ fn expr_assign_expected_bool_error(
1381
+ &self,
1382
+ expr: &'tcx hir::Expr<'tcx>,
1383
+ lhs: &'tcx hir::Expr<'tcx>,
1384
+ rhs: &'tcx hir::Expr<'tcx>,
1385
+ span: Span,
1386
+ ) -> ErrorGuaranteed {
1387
+ let actual_ty = self.tcx.types.unit;
1388
+ let expected_ty = self.tcx.types.bool;
1389
+ let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap_err();
1390
+ let lhs_ty = self.check_expr(lhs);
1391
+ let rhs_ty = self.check_expr(rhs);
1392
+ let refs_can_coerce = |lhs: Ty<'tcx>, rhs: Ty<'tcx>| {
1393
+ let lhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, lhs.peel_refs());
1394
+ let rhs = Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rhs.peel_refs());
1395
+ self.may_coerce(rhs, lhs)
1396
+ };
1397
+ let (applicability, eq) = if self.may_coerce(rhs_ty, lhs_ty) {
1398
+ (Applicability::MachineApplicable, true)
1399
+ } else if refs_can_coerce(rhs_ty, lhs_ty) {
1400
+ // The lhs and rhs are likely missing some references in either side. Subsequent
1401
+ // suggestions will show up.
1402
+ (Applicability::MaybeIncorrect, true)
1403
+ } else if let ExprKind::Binary(
1404
+ Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
1405
+ _,
1406
+ rhs_expr,
1407
+ ) = lhs.kind
1408
+ {
1409
+ // if x == 1 && y == 2 { .. }
1410
+ // +
1411
+ let actual_lhs = self.check_expr(rhs_expr);
1412
+ let may_eq = self.may_coerce(rhs_ty, actual_lhs) || refs_can_coerce(rhs_ty, actual_lhs);
1413
+ (Applicability::MaybeIncorrect, may_eq)
1414
+ } else if let ExprKind::Binary(
1415
+ Spanned { node: hir::BinOpKind::And | hir::BinOpKind::Or, .. },
1416
+ lhs_expr,
1417
+ _,
1418
+ ) = rhs.kind
1419
+ {
1420
+ // if x == 1 && y == 2 { .. }
1421
+ // +
1422
+ let actual_rhs = self.check_expr(lhs_expr);
1423
+ let may_eq = self.may_coerce(actual_rhs, lhs_ty) || refs_can_coerce(actual_rhs, lhs_ty);
1424
+ (Applicability::MaybeIncorrect, may_eq)
1425
+ } else {
1426
+ (Applicability::MaybeIncorrect, false)
1427
+ };
1428
+
1429
+ if !lhs.is_syntactic_place_expr()
1430
+ && lhs.is_approximately_pattern()
1431
+ && !matches!(lhs.kind, hir::ExprKind::Lit(_))
1432
+ {
1433
+ // Do not suggest `if let x = y` as `==` is way more likely to be the intention.
1434
+ if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
1435
+ self.tcx.parent_hir_node(expr.hir_id)
1436
+ {
1437
+ err.span_suggestion_verbose(
1438
+ expr.span.shrink_to_lo(),
1439
+ "you might have meant to use pattern matching",
1440
+ "let ",
1441
+ applicability,
1442
+ );
1443
+ };
1444
+ }
1445
+ if eq {
1446
+ err.span_suggestion_verbose(
1447
+ span.shrink_to_hi(),
1448
+ "you might have meant to compare for equality",
1449
+ '=',
1450
+ applicability,
1451
+ );
1452
+ }
1453
+
1454
+ // If the assignment expression itself is ill-formed, don't
1455
+ // bother emitting another error
1456
+ err.emit_unless(lhs_ty.references_error() || rhs_ty.references_error())
1457
+ }
1458
+
1453
1459
pub(super) fn check_expr_let(
1454
1460
&self,
1455
1461
let_expr: &'tcx hir::LetExpr<'tcx>,
0 commit comments