@@ -1320,84 +1320,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
13201320 ) -> Ty<'tcx> {
13211321 let expected_ty = expected.coercion_target_type(self, expr.span);
13221322 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);
14011325 }
14021326
14031327 let lhs_ty = self.check_expr_with_needs(lhs, Needs::MutPlace);
@@ -1450,6 +1374,88 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
14501374 }
14511375 }
14521376
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+
14531459 pub(super) fn check_expr_let(
14541460 &self,
14551461 let_expr: &'tcx hir::LetExpr<'tcx>,
0 commit comments