@@ -9,7 +9,6 @@ use rustc_span::Span;
99use rustc_trait_selection:: traits:: query:: evaluate_obligation:: InferCtxtExt ;
1010use rustc_trait_selection:: traits:: {
1111 IfExpressionCause , MatchExpressionArmCause , ObligationCause , ObligationCauseCode ,
12- StatementAsExpression ,
1312} ;
1413
1514impl < ' 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