Skip to content

Commit 92bebac

Browse files
authored
Rollup merge of rust-lang#99539 - compiler-errors:bidirectional-block-suggestions, r=fee1-dead
Improve suggestions for returning binding Fixes rust-lang#99525 Also reworks the cause codes for match and if a bit, I think cleaning them up in a positive way. We no longer need to call `could_remove_semicolon` in successful code, which might save a few cycles?
2 parents aec458b + 3eef023 commit 92bebac

File tree

13 files changed

+625
-467
lines changed

13 files changed

+625
-467
lines changed

compiler/rustc_hir/src/hir.rs

+10
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,16 @@ pub struct Block<'hir> {
954954
pub targeted_by_break: bool,
955955
}
956956

957+
impl<'hir> Block<'hir> {
958+
pub fn innermost_block(&self) -> &Block<'hir> {
959+
let mut block = self;
960+
while let Some(Expr { kind: ExprKind::Block(inner_block, _), .. }) = block.expr {
961+
block = inner_block;
962+
}
963+
block
964+
}
965+
}
966+
957967
#[derive(Debug, HashStable_Generic)]
958968
pub struct Pat<'hir> {
959969
#[stable_hasher(ignore)]

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+422-117
Large diffs are not rendered by default.

compiler/rustc_middle/src/traits/mod.rs

+14-9
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,7 @@ pub enum ObligationCauseCode<'tcx> {
351351
ConstPatternStructural,
352352

353353
/// Computing common supertype in an if expression
354-
IfExpression(Box<IfExpressionCause>),
354+
IfExpression(Box<IfExpressionCause<'tcx>>),
355355

356356
/// Computing common supertype of an if expression with no else counter-part
357357
IfExpressionWithNoElse,
@@ -488,22 +488,27 @@ impl<'tcx> ty::Lift<'tcx> for StatementAsExpression {
488488

489489
#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
490490
pub struct MatchExpressionArmCause<'tcx> {
491+
pub arm_block_id: Option<hir::HirId>,
492+
pub arm_ty: Ty<'tcx>,
491493
pub arm_span: Span,
494+
pub prior_arm_block_id: Option<hir::HirId>,
495+
pub prior_arm_ty: Ty<'tcx>,
496+
pub prior_arm_span: Span,
492497
pub scrut_span: Span,
493-
pub semi_span: Option<(Span, StatementAsExpression)>,
494498
pub source: hir::MatchSource,
495499
pub prior_arms: Vec<Span>,
496-
pub last_ty: Ty<'tcx>,
497500
pub scrut_hir_id: hir::HirId,
498501
pub opt_suggest_box_span: Option<Span>,
499502
}
500503

501-
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
502-
pub struct IfExpressionCause {
503-
pub then: Span,
504-
pub else_sp: Span,
505-
pub outer: Option<Span>,
506-
pub semicolon: Option<(Span, StatementAsExpression)>,
504+
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
505+
#[derive(Lift, TypeFoldable, TypeVisitable)]
506+
pub struct IfExpressionCause<'tcx> {
507+
pub then_id: hir::HirId,
508+
pub else_id: hir::HirId,
509+
pub then_ty: Ty<'tcx>,
510+
pub else_ty: Ty<'tcx>,
511+
pub outer_span: Option<Span>,
507512
pub opt_suggest_box_span: Option<Span>,
508513
}
509514

compiler/rustc_middle/src/traits/structural_impls.rs

-1
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,6 @@ impl<N: fmt::Debug> fmt::Debug for traits::ImplSourceConstDestructData<N> {
130130
// Lift implementations
131131

132132
TrivialTypeTraversalAndLiftImpls! {
133-
super::IfExpressionCause,
134133
super::ImplSourceDiscriminantKindData,
135134
super::ImplSourcePointeeData,
136135
}

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ use rustc_hir::intravisit::Visitor;
2222
use rustc_hir::GenericParam;
2323
use rustc_hir::Item;
2424
use rustc_hir::Node;
25-
use rustc_infer::infer::error_reporting::same_type_modulo_infer;
2625
use rustc_infer::traits::TraitEngine;
2726
use rustc_middle::traits::select::OverflowError;
2827
use rustc_middle::ty::abstract_const::NotConstEvaluatable;
@@ -640,7 +639,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
640639
if expected.len() == 1 { "" } else { "s" },
641640
)
642641
);
643-
} else if !same_type_modulo_infer(given_ty, expected_ty) {
642+
} else if !self.same_type_modulo_infer(given_ty, expected_ty) {
644643
// Print type mismatch
645644
let (expected_args, given_args) =
646645
self.cmp(given_ty, expected_ty);

compiler/rustc_typeck/src/check/_match.rs

+68-101
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ use rustc_span::Span;
99
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
1010
use rustc_trait_selection::traits::{
1111
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
12-
StatementAsExpression,
1312
};
1413

1514
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
@@ -75,8 +74,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
7574
};
7675

7776
let mut other_arms = vec![]; // Used only for diagnostics.
78-
let mut prior_arm_ty = None;
79-
for (i, arm) in arms.iter().enumerate() {
77+
let mut prior_arm = None;
78+
for arm in arms {
8079
if let Some(g) = &arm.guard {
8180
self.diverges.set(Diverges::Maybe);
8281
match g {
@@ -96,21 +95,28 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
9695

9796
let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
9897

99-
let (arm_span, semi_span) =
100-
self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
101-
let (span, code) = match i {
98+
let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
99+
(Some(blk.hir_id), self.find_block_span(blk))
100+
} else {
101+
(None, arm.body.span)
102+
};
103+
104+
let (span, code) = match prior_arm {
102105
// The reason for the first arm to fail is not that the match arms diverge,
103106
// but rather that there's a prior obligation that doesn't hold.
104-
0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
105-
_ => (
107+
None => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
108+
Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => (
106109
expr.span,
107110
ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause {
111+
arm_block_id,
108112
arm_span,
113+
arm_ty,
114+
prior_arm_block_id,
115+
prior_arm_ty,
116+
prior_arm_span,
109117
scrut_span: scrut.span,
110-
semi_span,
111118
source: match_src,
112119
prior_arms: other_arms.clone(),
113-
last_ty: prior_arm_ty.unwrap(),
114120
scrut_hir_id: scrut.hir_id,
115121
opt_suggest_box_span,
116122
})),
@@ -139,7 +145,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
139145
let ret_ty = ret_coercion.borrow().expected_ty();
140146
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
141147
self.can_coerce(arm_ty, ret_ty)
142-
&& prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty))
148+
&& prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty))
143149
// The match arms need to unify for the case of `impl Trait`.
144150
&& !matches!(ret_ty.kind(), ty::Opaque(..))
145151
}
@@ -181,7 +187,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
181187
if other_arms.len() > 5 {
182188
other_arms.remove(0);
183189
}
184-
prior_arm_ty = Some(arm_ty);
190+
191+
prior_arm = Some((arm_block_id, arm_ty, arm_span));
185192
}
186193

187194
// If all of the arms in the `match` diverge,
@@ -207,28 +214,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
207214
match_ty
208215
}
209216

210-
fn get_appropriate_arm_semicolon_removal_span(
211-
&self,
212-
arms: &'tcx [hir::Arm<'tcx>],
213-
i: usize,
214-
prior_arm_ty: Option<Ty<'tcx>>,
215-
arm_ty: Ty<'tcx>,
216-
) -> (Span, Option<(Span, StatementAsExpression)>) {
217-
let arm = &arms[i];
218-
let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
219-
self.find_block_span(blk, prior_arm_ty)
220-
} else {
221-
(arm.body.span, None)
222-
};
223-
if semi_span.is_none() && i > 0 {
224-
if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
225-
let (_, semi_span_prev) = self.find_block_span(blk, Some(arm_ty));
226-
semi_span = semi_span_prev;
227-
}
228-
}
229-
(arm_span, semi_span)
230-
}
231-
232217
/// When the previously checked expression (the scrutinee) diverges,
233218
/// warn the user about the match arms being unreachable.
234219
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {
@@ -313,7 +298,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
313298
else_ty: Ty<'tcx>,
314299
opt_suggest_box_span: Option<Span>,
315300
) -> ObligationCause<'tcx> {
316-
let mut outer_sp = if self.tcx.sess.source_map().is_multiline(span) {
301+
let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) {
317302
// The `if`/`else` isn't in one line in the output, include some context to make it
318303
// clear it is an if/else expression:
319304
// ```
@@ -339,69 +324,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
339324
None
340325
};
341326

342-
let mut remove_semicolon = None;
343-
let error_sp = if let ExprKind::Block(block, _) = &else_expr.kind {
344-
let (error_sp, semi_sp) = self.find_block_span(block, Some(then_ty));
345-
remove_semicolon = semi_sp;
346-
if block.expr.is_none() && block.stmts.is_empty() {
347-
// Avoid overlapping spans that aren't as readable:
348-
// ```
349-
// 2 | let x = if true {
350-
// | _____________-
351-
// 3 | | 3
352-
// | | - expected because of this
353-
// 4 | | } else {
354-
// | |____________^
355-
// 5 | ||
356-
// 6 | || };
357-
// | || ^
358-
// | ||_____|
359-
// | |______if and else have incompatible types
360-
// | expected integer, found `()`
361-
// ```
362-
// by not pointing at the entire expression:
363-
// ```
364-
// 2 | let x = if true {
365-
// | ------- `if` and `else` have incompatible types
366-
// 3 | 3
367-
// | - expected because of this
368-
// 4 | } else {
369-
// | ____________^
370-
// 5 | |
371-
// 6 | | };
372-
// | |_____^ expected integer, found `()`
373-
// ```
374-
if outer_sp.is_some() {
375-
outer_sp = Some(self.tcx.sess.source_map().guess_head_span(span));
376-
}
327+
let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind {
328+
let block = block.innermost_block();
329+
330+
// Avoid overlapping spans that aren't as readable:
331+
// ```
332+
// 2 | let x = if true {
333+
// | _____________-
334+
// 3 | | 3
335+
// | | - expected because of this
336+
// 4 | | } else {
337+
// | |____________^
338+
// 5 | ||
339+
// 6 | || };
340+
// | || ^
341+
// | ||_____|
342+
// | |______if and else have incompatible types
343+
// | expected integer, found `()`
344+
// ```
345+
// by not pointing at the entire expression:
346+
// ```
347+
// 2 | let x = if true {
348+
// | ------- `if` and `else` have incompatible types
349+
// 3 | 3
350+
// | - expected because of this
351+
// 4 | } else {
352+
// | ____________^
353+
// 5 | |
354+
// 6 | | };
355+
// | |_____^ expected integer, found `()`
356+
// ```
357+
if block.expr.is_none() && block.stmts.is_empty()
358+
&& let Some(outer_span) = &mut outer_span
359+
{
360+
*outer_span = self.tcx.sess.source_map().guess_head_span(*outer_span);
377361
}
378-
error_sp
362+
363+
(self.find_block_span(block), block.hir_id)
379364
} else {
380-
// shouldn't happen unless the parser has done something weird
381-
else_expr.span
365+
(else_expr.span, else_expr.hir_id)
382366
};
383367

384-
// Compute `Span` of `then` part of `if`-expression.
385-
let then_sp = if let ExprKind::Block(block, _) = &then_expr.kind {
386-
let (then_sp, semi_sp) = self.find_block_span(block, Some(else_ty));
387-
remove_semicolon = remove_semicolon.or(semi_sp);
368+
let then_id = if let ExprKind::Block(block, _) = &then_expr.kind {
369+
let block = block.innermost_block();
370+
// Exclude overlapping spans
388371
if block.expr.is_none() && block.stmts.is_empty() {
389-
outer_sp = None; // same as in `error_sp`; cleanup output
372+
outer_span = None;
390373
}
391-
then_sp
374+
block.hir_id
392375
} else {
393-
// shouldn't happen unless the parser has done something weird
394-
then_expr.span
376+
then_expr.hir_id
395377
};
396378

397379
// Finally construct the cause:
398380
self.cause(
399381
error_sp,
400382
ObligationCauseCode::IfExpression(Box::new(IfExpressionCause {
401-
then: then_sp,
402-
else_sp: error_sp,
403-
outer: outer_sp,
404-
semicolon: remove_semicolon,
383+
else_id,
384+
then_id,
385+
then_ty,
386+
else_ty,
387+
outer_span,
405388
opt_suggest_box_span,
406389
})),
407390
)
@@ -482,22 +465,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
482465
}
483466
}
484467

485-
fn find_block_span(
486-
&self,
487-
block: &'tcx hir::Block<'tcx>,
488-
expected_ty: Option<Ty<'tcx>>,
489-
) -> (Span, Option<(Span, StatementAsExpression)>) {
490-
if let Some(expr) = &block.expr {
491-
(expr.span, None)
492-
} else if let Some(stmt) = block.stmts.last() {
493-
// possibly incorrect trailing `;` in the else arm
494-
(stmt.span, expected_ty.and_then(|ty| self.could_remove_semicolon(block, ty)))
495-
} else {
496-
// empty block; point at its entirety
497-
(block.span, None)
498-
}
499-
}
500-
501468
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
502469
// we check if the different arms would work with boxed trait objects instead and
503470
// provide a structured suggestion in that case.

0 commit comments

Comments
 (0)