And for patterns too
This commit is contained in:
parent
99c32570bb
commit
8926dac549
@ -614,13 +614,16 @@ fn note_error_origin(
|
||||
err.span_label(span, "expected due to this");
|
||||
}
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
semi_span,
|
||||
arm_block_id,
|
||||
arm_span,
|
||||
arm_ty,
|
||||
prior_arm_block_id,
|
||||
prior_arm_span,
|
||||
prior_arm_ty,
|
||||
source,
|
||||
ref prior_arms,
|
||||
last_ty,
|
||||
scrut_hir_id,
|
||||
opt_suggest_box_span,
|
||||
arm_span,
|
||||
scrut_span,
|
||||
..
|
||||
}) => match source {
|
||||
@ -651,10 +654,10 @@ fn note_error_origin(
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// `last_ty` can be `!`, `expected` will have better info when present.
|
||||
// `prior_arm_ty` can be `!`, `expected` will have better info when present.
|
||||
let t = self.resolve_vars_if_possible(match exp_found {
|
||||
Some(ty::error::ExpectedFound { expected, .. }) => expected,
|
||||
_ => last_ty,
|
||||
_ => prior_arm_ty,
|
||||
});
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
let mut any_multiline_arm = source_map.is_multiline(arm_span);
|
||||
@ -679,37 +682,15 @@ fn note_error_origin(
|
||||
};
|
||||
let msg = "`match` arms have incompatible types";
|
||||
err.span_label(outer_error_span, msg);
|
||||
if let Some((sp, boxed)) = semi_span {
|
||||
if let (StatementAsExpression::NeedsBoxing, [.., prior_arm]) =
|
||||
(boxed, &prior_arms[..])
|
||||
{
|
||||
err.multipart_suggestion(
|
||||
"consider removing this semicolon and boxing the expressions",
|
||||
vec![
|
||||
(prior_arm.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(prior_arm.shrink_to_hi(), ")".to_string()),
|
||||
(arm_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(arm_span.shrink_to_hi(), ")".to_string()),
|
||||
(sp, String::new()),
|
||||
],
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
} else if matches!(boxed, StatementAsExpression::NeedsBoxing) {
|
||||
err.span_suggestion_short(
|
||||
sp,
|
||||
"consider removing this semicolon and boxing the expressions",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion_short(
|
||||
sp,
|
||||
"consider removing this semicolon",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
}
|
||||
self.suggest_remove_semi_or_return_binding(
|
||||
err,
|
||||
prior_arm_block_id,
|
||||
prior_arm_ty,
|
||||
prior_arm_span,
|
||||
arm_block_id,
|
||||
arm_ty,
|
||||
arm_span,
|
||||
);
|
||||
if let Some(ret_sp) = opt_suggest_box_span {
|
||||
// Get return type span and point to it.
|
||||
self.suggest_boxing_for_return_impl_trait(
|
||||
@ -734,48 +715,15 @@ fn note_error_origin(
|
||||
if let Some(sp) = outer_span {
|
||||
err.span_label(sp, "`if` and `else` have incompatible types");
|
||||
}
|
||||
let semicolon = if let hir::Node::Block(blk) = self.tcx.hir().get(then_id)
|
||||
&& let Some(remove_semicolon) = self.could_remove_semicolon(blk, else_ty)
|
||||
{
|
||||
Some(remove_semicolon)
|
||||
} else if let hir::Node::Block(blk) = self.tcx.hir().get(else_id)
|
||||
&& let Some(remove_semicolon) = self.could_remove_semicolon(blk, then_ty)
|
||||
{
|
||||
Some(remove_semicolon)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some((sp, boxed)) = semicolon {
|
||||
if matches!(boxed, StatementAsExpression::NeedsBoxing) {
|
||||
err.multipart_suggestion(
|
||||
"consider removing this semicolon and boxing the expression",
|
||||
vec![
|
||||
(then_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(then_span.shrink_to_hi(), ")".to_string()),
|
||||
(else_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(else_span.shrink_to_hi(), ")".to_string()),
|
||||
(sp, String::new()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion_short(
|
||||
sp,
|
||||
"consider removing this semicolon",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let suggested = if let hir::Node::Block(blk) = self.tcx.hir().get(then_id) {
|
||||
self.consider_returning_binding(blk, else_ty, err)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !suggested && let hir::Node::Block(blk) = self.tcx.hir().get(else_id) {
|
||||
self.consider_returning_binding(blk, then_ty, err);
|
||||
}
|
||||
}
|
||||
self.suggest_remove_semi_or_return_binding(
|
||||
err,
|
||||
Some(then_id),
|
||||
then_ty,
|
||||
then_span,
|
||||
Some(else_id),
|
||||
else_ty,
|
||||
else_span,
|
||||
);
|
||||
if let Some(ret_sp) = opt_suggest_box_span {
|
||||
self.suggest_boxing_for_return_impl_trait(
|
||||
err,
|
||||
@ -800,6 +748,69 @@ fn note_error_origin(
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_remove_semi_or_return_binding(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
first_id: Option<hir::HirId>,
|
||||
first_ty: Ty<'tcx>,
|
||||
first_span: Span,
|
||||
second_id: Option<hir::HirId>,
|
||||
second_ty: Ty<'tcx>,
|
||||
second_span: Span,
|
||||
) {
|
||||
let semicolon =
|
||||
if let Some(first_id) = first_id
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir().get(first_id)
|
||||
&& let Some(remove_semicolon) = self.could_remove_semicolon(blk, second_ty)
|
||||
{
|
||||
Some(remove_semicolon)
|
||||
} else if let Some(second_id) = second_id
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir().get(second_id)
|
||||
&& let Some(remove_semicolon) = self.could_remove_semicolon(blk, first_ty)
|
||||
{
|
||||
Some(remove_semicolon)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if let Some((sp, boxed)) = semicolon {
|
||||
if matches!(boxed, StatementAsExpression::NeedsBoxing) {
|
||||
err.multipart_suggestion(
|
||||
"consider removing this semicolon and boxing the expressions",
|
||||
vec![
|
||||
(first_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(first_span.shrink_to_hi(), ")".to_string()),
|
||||
(second_span.shrink_to_lo(), "Box::new(".to_string()),
|
||||
(second_span.shrink_to_hi(), ")".to_string()),
|
||||
(sp, String::new()),
|
||||
],
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
} else {
|
||||
err.span_suggestion_short(
|
||||
sp,
|
||||
"consider removing this semicolon",
|
||||
"",
|
||||
Applicability::MachineApplicable,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
let suggested =
|
||||
if let Some(first_id) = first_id
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir().get(first_id)
|
||||
{
|
||||
self.consider_returning_binding(blk, second_ty, err)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if !suggested
|
||||
&& let Some(second_id) = second_id
|
||||
&& let hir::Node::Block(blk) = self.tcx.hir().get(second_id)
|
||||
{
|
||||
self.consider_returning_binding(blk, first_ty, err);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn suggest_boxing_for_return_impl_trait(
|
||||
&self,
|
||||
err: &mut Diagnostic,
|
||||
|
@ -488,12 +488,15 @@ fn lift_to_tcx(self, _tcx: TyCtxt<'tcx>) -> Option<StatementAsExpression> {
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Lift)]
|
||||
pub struct MatchExpressionArmCause<'tcx> {
|
||||
pub arm_block_id: Option<hir::HirId>,
|
||||
pub arm_ty: Ty<'tcx>,
|
||||
pub arm_span: Span,
|
||||
pub prior_arm_block_id: Option<hir::HirId>,
|
||||
pub prior_arm_ty: Ty<'tcx>,
|
||||
pub prior_arm_span: Span,
|
||||
pub scrut_span: Span,
|
||||
pub semi_span: Option<(Span, StatementAsExpression)>,
|
||||
pub source: hir::MatchSource,
|
||||
pub prior_arms: Vec<Span>,
|
||||
pub last_ty: Ty<'tcx>,
|
||||
pub scrut_hir_id: hir::HirId,
|
||||
pub opt_suggest_box_span: Option<Span>,
|
||||
}
|
||||
|
@ -9,7 +9,6 @@
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
use rustc_trait_selection::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
StatementAsExpression,
|
||||
};
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
@ -75,8 +74,8 @@ pub fn check_match(
|
||||
};
|
||||
|
||||
let mut other_arms = vec![]; // Used only for diagnostics.
|
||||
let mut prior_arm_ty = None;
|
||||
for (i, arm) in arms.iter().enumerate() {
|
||||
let mut prior_arm = None;
|
||||
for arm in arms {
|
||||
if let Some(g) = &arm.guard {
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
match g {
|
||||
@ -96,21 +95,28 @@ pub fn check_match(
|
||||
|
||||
let opt_suggest_box_span = self.opt_suggest_box_span(arm_ty, orig_expected);
|
||||
|
||||
let (arm_span, semi_span) =
|
||||
self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
|
||||
let (span, code) = match i {
|
||||
let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind {
|
||||
(Some(blk.hir_id), self.find_block_span(blk))
|
||||
} else {
|
||||
(None, arm.body.span)
|
||||
};
|
||||
|
||||
let (span, code) = match prior_arm {
|
||||
// The reason for the first arm to fail is not that the match arms diverge,
|
||||
// but rather that there's a prior obligation that doesn't hold.
|
||||
0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
|
||||
_ => (
|
||||
None => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
|
||||
Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => (
|
||||
expr.span,
|
||||
ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause {
|
||||
arm_block_id,
|
||||
arm_span,
|
||||
arm_ty,
|
||||
prior_arm_block_id,
|
||||
prior_arm_ty,
|
||||
prior_arm_span,
|
||||
scrut_span: scrut.span,
|
||||
semi_span,
|
||||
source: match_src,
|
||||
prior_arms: other_arms.clone(),
|
||||
last_ty: prior_arm_ty.unwrap(),
|
||||
scrut_hir_id: scrut.hir_id,
|
||||
opt_suggest_box_span,
|
||||
})),
|
||||
@ -139,7 +145,7 @@ pub fn check_match(
|
||||
let ret_ty = ret_coercion.borrow().expected_ty();
|
||||
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
|
||||
self.can_coerce(arm_ty, ret_ty)
|
||||
&& prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty))
|
||||
&& prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty))
|
||||
// The match arms need to unify for the case of `impl Trait`.
|
||||
&& !matches!(ret_ty.kind(), ty::Opaque(..))
|
||||
}
|
||||
@ -181,7 +187,8 @@ pub fn check_match(
|
||||
if other_arms.len() > 5 {
|
||||
other_arms.remove(0);
|
||||
}
|
||||
prior_arm_ty = Some(arm_ty);
|
||||
|
||||
prior_arm = Some((arm_block_id, arm_ty, arm_span));
|
||||
}
|
||||
|
||||
// If all of the arms in the `match` diverge,
|
||||
@ -207,32 +214,6 @@ pub fn check_match(
|
||||
match_ty
|
||||
}
|
||||
|
||||
fn get_appropriate_arm_semicolon_removal_span(
|
||||
&self,
|
||||
arms: &'tcx [hir::Arm<'tcx>],
|
||||
i: usize,
|
||||
prior_arm_ty: Option<Ty<'tcx>>,
|
||||
arm_ty: Ty<'tcx>,
|
||||
) -> (Span, Option<(Span, StatementAsExpression)>) {
|
||||
let arm = &arms[i];
|
||||
let (arm_span, mut semi_span) = if let hir::ExprKind::Block(blk, _) = &arm.body.kind {
|
||||
(
|
||||
self.find_block_span(blk),
|
||||
prior_arm_ty
|
||||
.and_then(|prior_arm_ty| self.could_remove_semicolon(blk, prior_arm_ty)),
|
||||
)
|
||||
} else {
|
||||
(arm.body.span, None)
|
||||
};
|
||||
if semi_span.is_none() && i > 0 {
|
||||
if let hir::ExprKind::Block(blk, _) = &arms[i - 1].body.kind {
|
||||
let semi_span_prev = self.could_remove_semicolon(blk, arm_ty);
|
||||
semi_span = semi_span_prev;
|
||||
}
|
||||
}
|
||||
(arm_span, semi_span)
|
||||
}
|
||||
|
||||
/// When the previously checked expression (the scrutinee) diverges,
|
||||
/// warn the user about the match arms being unreachable.
|
||||
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {
|
||||
|
@ -24,4 +24,28 @@ fn d(opt_str: Option<String>) {
|
||||
};
|
||||
}
|
||||
|
||||
fn d2(opt_str: Option<String>) {
|
||||
let s = if let Some(s) = opt_str {
|
||||
} else {
|
||||
String::new()
|
||||
//~^ ERROR `if` and `else` have incompatible types
|
||||
};
|
||||
}
|
||||
|
||||
fn e(opt_str: Option<String>) {
|
||||
let s: String = match opt_str {
|
||||
Some(s) => {}
|
||||
//~^ ERROR mismatched types
|
||||
None => String::new(),
|
||||
};
|
||||
}
|
||||
|
||||
fn e2(opt_str: Option<String>) {
|
||||
let s = match opt_str {
|
||||
Some(s) => {}
|
||||
None => String::new(),
|
||||
//~^ ERROR `match` arms have incompatible types
|
||||
};
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -59,6 +59,52 @@ LL + s
|
||||
LL ~
|
||||
|
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
error[E0308]: `if` and `else` have incompatible types
|
||||
--> $DIR/return-bindings.rs:30:9
|
||||
|
|
||||
LL | let s = if let Some(s) = opt_str {
|
||||
| ______________________________________-
|
||||
LL | | } else {
|
||||
| |_____- expected because of this
|
||||
LL | String::new()
|
||||
| ^^^^^^^^^^^^^ expected `()`, found struct `String`
|
||||
|
|
||||
help: consider returning the local binding `s`
|
||||
|
|
||||
LL ~ let s = if let Some(s) = opt_str {
|
||||
LL + s
|
||||
LL ~ } else {
|
||||
|
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/return-bindings.rs:37:20
|
||||
|
|
||||
LL | Some(s) => {}
|
||||
| ^^ expected struct `String`, found `()`
|
||||
|
|
||||
help: consider returning the local binding `s`
|
||||
|
|
||||
LL | Some(s) => { s }
|
||||
| +
|
||||
|
||||
error[E0308]: `match` arms have incompatible types
|
||||
--> $DIR/return-bindings.rs:46:17
|
||||
|
|
||||
LL | let s = match opt_str {
|
||||
| _____________-
|
||||
LL | | Some(s) => {}
|
||||
| | -- this is found to be of type `()`
|
||||
LL | | None => String::new(),
|
||||
| | ^^^^^^^^^^^^^ expected `()`, found struct `String`
|
||||
LL | |
|
||||
LL | | };
|
||||
| |_____- `match` arms have incompatible types
|
||||
|
|
||||
help: consider returning the local binding `s`
|
||||
|
|
||||
LL | Some(s) => { s }
|
||||
| +
|
||||
|
||||
error: aborting due to 7 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
Loading…
Reference in New Issue
Block a user