@@ -3366,37 +3366,44 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
33663366 let coerce_to_ty = expected. coercion_target_type ( self , sp) ;
33673367 let mut coerce: DynamicCoerceMany = CoerceMany :: new ( coerce_to_ty) ;
33683368
3369- let mut outer_sp = if self . tcx . sess . source_map ( ) . is_multiline ( sp) {
3370- // The `if`/`else` isn't in one line in the output, include some context to make it
3371- // clear it is an if/else expression:
3372- // ```
3373- // LL | let x = if true {
3374- // | _____________-
3375- // LL || 10i32
3376- // || ----- expected because of this
3377- // LL || } else {
3378- // LL || 10u32
3379- // || ^^^^^ expected i32, found u32
3380- // LL || };
3381- // ||_____- if and else have incompatible types
3382- // ```
3383- Some ( sp)
3384- } else {
3385- // The entire expression is in one line, only point at the arms
3386- // ```
3387- // LL | let x = if true { 10i32 } else { 10u32 };
3388- // | ----- ^^^^^ expected i32, found u32
3389- // | |
3390- // | expected because of this
3391- // ```
3392- None
3393- } ;
3394- let error_sp = opt_else_expr. map ( |expr| {
3395- if let ExprKind :: Block ( block, _) = & expr. node {
3369+ coerce. coerce ( self , & self . misc ( sp) , then_expr, then_ty) ;
3370+
3371+ if let Some ( else_expr) = opt_else_expr {
3372+ let else_ty = self . check_expr_with_expectation ( else_expr, expected) ;
3373+ let else_diverges = self . diverges . get ( ) ;
3374+
3375+ let mut outer_sp = if self . tcx . sess . source_map ( ) . is_multiline ( sp) {
3376+ // The `if`/`else` isn't in one line in the output, include some context to make it
3377+ // clear it is an if/else expression:
3378+ // ```
3379+ // LL | let x = if true {
3380+ // | _____________-
3381+ // LL || 10i32
3382+ // || ----- expected because of this
3383+ // LL || } else {
3384+ // LL || 10u32
3385+ // || ^^^^^ expected i32, found u32
3386+ // LL || };
3387+ // ||_____- if and else have incompatible types
3388+ // ```
3389+ Some ( sp)
3390+ } else {
3391+ // The entire expression is in one line, only point at the arms
3392+ // ```
3393+ // LL | let x = if true { 10i32 } else { 10u32 };
3394+ // | ----- ^^^^^ expected i32, found u32
3395+ // | |
3396+ // | expected because of this
3397+ // ```
3398+ None
3399+ } ;
3400+ let mut remove_semicolon = None ;
3401+ let error_sp = if let ExprKind :: Block ( block, _) = & else_expr. node {
33963402 if let Some ( expr) = & block. expr {
33973403 expr. span
33983404 } else if let Some ( stmt) = block. stmts . last ( ) {
33993405 // possibly incorrect trailing `;` in the else arm
3406+ remove_semicolon = self . could_remove_semicolon ( block, then_ty) ;
34003407 stmt. span
34013408 } else { // empty block, point at its entirety
34023409 // Avoid overlapping spans that aren't as readable:
@@ -3429,35 +3436,32 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
34293436 if outer_sp. is_some ( ) {
34303437 outer_sp = Some ( self . tcx . sess . source_map ( ) . def_span ( sp) ) ;
34313438 }
3432- expr . span
3439+ else_expr . span
34333440 }
34343441 } else { // shouldn't happen unless the parser has done something weird
3435- expr. span
3436- }
3437- } ) . unwrap_or ( sp) ; // shouldn't be needed
3438- let then_sp = if let ExprKind :: Block ( block, _) = & then_expr. node {
3439- if let Some ( expr) = & block. expr {
3440- expr. span
3441- } else if let Some ( stmt) = block. stmts . last ( ) {
3442- // possibly incorrect trailing `;` in the else arm
3443- stmt. span
3444- } else { // empty block, point at its entirety
3445- outer_sp = None ; // same as in `error_sp`, cleanup output
3442+ else_expr. span
3443+ } ;
3444+ let then_sp = if let ExprKind :: Block ( block, _) = & then_expr. node {
3445+ if let Some ( expr) = & block. expr {
3446+ expr. span
3447+ } else if let Some ( stmt) = block. stmts . last ( ) {
3448+ // possibly incorrect trailing `;` in the else arm
3449+ remove_semicolon = remove_semicolon. or (
3450+ self . could_remove_semicolon ( block, else_ty) ) ;
3451+ stmt. span
3452+ } else { // empty block, point at its entirety
3453+ outer_sp = None ; // same as in `error_sp`, cleanup output
3454+ then_expr. span
3455+ }
3456+ } else { // shouldn't happen unless the parser has done something weird
34463457 then_expr. span
3447- }
3448- } else { // shouldn't happen unless the parser has done something weird
3449- then_expr. span
3450- } ;
3451-
3452- let if_cause = self . cause ( error_sp, ObligationCauseCode :: IfExpression {
3453- then : then_sp,
3454- outer : outer_sp,
3455- } ) ;
3456- coerce. coerce ( self , & if_cause, then_expr, then_ty) ;
3458+ } ;
34573459
3458- if let Some ( else_expr) = opt_else_expr {
3459- let else_ty = self . check_expr_with_expectation ( else_expr, expected) ;
3460- let else_diverges = self . diverges . get ( ) ;
3460+ let if_cause = self . cause ( error_sp, ObligationCauseCode :: IfExpression {
3461+ then : then_sp,
3462+ outer : outer_sp,
3463+ semicolon : remove_semicolon,
3464+ } ) ;
34613465
34623466 coerce. coerce ( self , & if_cause, else_expr, else_ty) ;
34633467
@@ -5230,7 +5234,6 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
52305234 }
52315235 }
52325236
5233-
52345237 /// A common error is to add an extra semicolon:
52355238 ///
52365239 /// ```
@@ -5242,31 +5245,43 @@ impl<'a, 'gcx, 'tcx> FnCtxt<'a, 'gcx, 'tcx> {
52425245 /// This routine checks if the final statement in a block is an
52435246 /// expression with an explicit semicolon whose type is compatible
52445247 /// with `expected_ty`. If so, it suggests removing the semicolon.
5245- fn consider_hint_about_removing_semicolon ( & self ,
5246- blk : & ' gcx hir:: Block ,
5247- expected_ty : Ty < ' tcx > ,
5248- err : & mut DiagnosticBuilder ) {
5248+ fn consider_hint_about_removing_semicolon (
5249+ & self ,
5250+ blk : & ' gcx hir:: Block ,
5251+ expected_ty : Ty < ' tcx > ,
5252+ err : & mut DiagnosticBuilder ,
5253+ ) {
5254+ if let Some ( span_semi) = self . could_remove_semicolon ( blk, expected_ty) {
5255+ err. span_suggestion_with_applicability (
5256+ span_semi,
5257+ "consider removing this semicolon" ,
5258+ String :: new ( ) ,
5259+ Applicability :: MachineApplicable ,
5260+ ) ;
5261+ }
5262+ }
5263+
5264+ fn could_remove_semicolon (
5265+ & self ,
5266+ blk : & ' gcx hir:: Block ,
5267+ expected_ty : Ty < ' tcx > ,
5268+ ) -> Option < Span > {
52495269 // Be helpful when the user wrote `{... expr;}` and
52505270 // taking the `;` off is enough to fix the error.
52515271 let last_stmt = match blk. stmts . last ( ) {
52525272 Some ( s) => s,
5253- None => return ,
5273+ None => return None ,
52545274 } ;
52555275 let last_expr = match last_stmt. node {
52565276 hir:: StmtKind :: Semi ( ref e, _) => e,
5257- _ => return ,
5277+ _ => return None ,
52585278 } ;
52595279 let last_expr_ty = self . node_ty ( last_expr. hir_id ) ;
52605280 if self . can_sub ( self . param_env , last_expr_ty, expected_ty) . is_err ( ) {
5261- return ;
5281+ return None ;
52625282 }
52635283 let original_span = original_sp ( last_stmt. span , blk. span ) ;
5264- let span_semi = original_span. with_lo ( original_span. hi ( ) - BytePos ( 1 ) ) ;
5265- err. span_suggestion_with_applicability (
5266- span_semi,
5267- "consider removing this semicolon" ,
5268- String :: new ( ) ,
5269- Applicability :: MachineApplicable ) ;
5284+ Some ( original_span. with_lo ( original_span. hi ( ) - BytePos ( 1 ) ) )
52705285 }
52715286
52725287 // Instantiates the given path, which must refer to an item with the given
0 commit comments