Skip to content

Commit 9567544

Browse files
committed
Suggest removal of semicolon when appropriate
1 parent 7fc1685 commit 9567544

File tree

6 files changed

+169
-91
lines changed

6 files changed

+169
-91
lines changed

src/librustc/infer/error_reporting/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -511,9 +511,17 @@ impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> {
511511
}
512512
}
513513
},
514-
ObligationCauseCode::IfExpression { then, outer } => {
514+
ObligationCauseCode::IfExpression { then, outer, semicolon } => {
515515
err.span_label(then, "expected because of this");
516516
outer.map(|sp| err.span_label(sp, "if and else have incompatible types"));
517+
if let Some(sp) = semicolon {
518+
err.span_suggestion_short_with_applicability(
519+
sp,
520+
"consider removing this semicolon",
521+
String::new(),
522+
Applicability::MachineApplicable,
523+
);
524+
}
517525
}
518526
_ => (),
519527
}

src/librustc/traits/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,7 @@ pub enum ObligationCauseCode<'tcx> {
232232
IfExpression {
233233
then: Span,
234234
outer: Option<Span>,
235+
semicolon: Option<Span>,
235236
},
236237

237238
/// Computing common supertype of an if expression with no else counter-part

src/librustc/traits/structural_impls.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -520,7 +520,11 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCauseCode<'a> {
520520
super::MatchExpressionArmPattern { span, ty } => {
521521
tcx.lift(&ty).map(|ty| super::MatchExpressionArmPattern { span, ty })
522522
}
523-
super::IfExpression { then, outer } => Some(super::IfExpression { then, outer }),
523+
super::IfExpression { then, outer, semicolon } => Some(super::IfExpression {
524+
then,
525+
outer,
526+
semicolon,
527+
}),
524528
super::IfExpressionWithNoElse => Some(super::IfExpressionWithNoElse),
525529
super::MainFunctionType => Some(super::MainFunctionType),
526530
super::StartFunctionType => Some(super::StartFunctionType),

src/librustc_typeck/check/mod.rs

+81-66
Original file line numberDiff line numberDiff line change
@@ -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

src/test/ui/if-else-type-mismatch.rs

+20-8
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,44 @@
11
fn main() {
22
let _ = if true {
3-
42i32
3+
1i32
44
} else {
5-
42u32
5+
2u32
66
};
77
//~^^ ERROR if and else have incompatible types
88
let _ = if true { 42i32 } else { 42u32 };
99
//~^ ERROR if and else have incompatible types
1010
let _ = if true {
11-
42i32;
11+
3u32;
1212
} else {
13-
42u32
13+
4u32
1414
};
1515
//~^^ ERROR if and else have incompatible types
1616
let _ = if true {
17-
42i32
17+
5u32
1818
} else {
19-
42u32;
19+
6u32;
20+
};
21+
//~^^ ERROR if and else have incompatible types
22+
let _ = if true {
23+
7i32;
24+
} else {
25+
8u32
26+
};
27+
//~^^ ERROR if and else have incompatible types
28+
let _ = if true {
29+
9i32
30+
} else {
31+
10u32;
2032
};
2133
//~^^ ERROR if and else have incompatible types
2234
let _ = if true {
2335

2436
} else {
25-
42u32
37+
11u32
2638
};
2739
//~^^ ERROR if and else have incompatible types
2840
let _ = if true {
29-
42i32
41+
12i32
3042
} else {
3143

3244
};

0 commit comments

Comments
 (0)