Reintroduce hir::ExprKind::If
This commit is contained in:
parent
c8915eebea
commit
f85fc264fe
97 changed files with 1046 additions and 787 deletions
|
@ -1,9 +1,9 @@
|
|||
use crate::check::coercion::CoerceMany;
|
||||
use crate::check::coercion::{AsCoercionSite, CoerceMany};
|
||||
use crate::check::{Diverges, Expectation, FnCtxt, Needs};
|
||||
use rustc_hir::{self as hir, ExprKind};
|
||||
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
|
||||
use rustc_infer::traits::Obligation;
|
||||
use rustc_middle::ty::{self, ToPredicate, Ty};
|
||||
use rustc_middle::ty::{self, ToPredicate, Ty, TyS};
|
||||
use rustc_span::Span;
|
||||
use rustc_trait_selection::opaque_types::InferCtxtExt as _;
|
||||
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt;
|
||||
|
@ -12,6 +12,41 @@ use rustc_trait_selection::traits::{
|
|||
StatementAsExpression,
|
||||
};
|
||||
|
||||
macro_rules! create_maybe_get_coercion_reason {
|
||||
($fn_name:ident, $node:expr) => {
|
||||
pub(crate) fn $fn_name(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> {
|
||||
let node = $node(self.tcx.hir(), hir_id);
|
||||
if let hir::Node::Block(block) = node {
|
||||
// check that the body's parent is an fn
|
||||
let parent = self.tcx.hir().get(
|
||||
self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(block.hir_id)),
|
||||
);
|
||||
if let (
|
||||
Some(expr),
|
||||
hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }),
|
||||
) = (&block.expr, parent)
|
||||
{
|
||||
// check that the `if` expr without `else` is the fn body's expr
|
||||
if expr.span == sp {
|
||||
return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| {
|
||||
let span = fn_decl.output.span();
|
||||
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?;
|
||||
Some((
|
||||
span,
|
||||
format!("expected `{}` because of this return type", snippet),
|
||||
))
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node {
|
||||
return Some((pat.span, "expected because of this assignment".to_string()));
|
||||
}
|
||||
None
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
pub fn check_match(
|
||||
&self,
|
||||
|
@ -25,7 +60,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
use hir::MatchSource::*;
|
||||
let (source_if, if_no_else, force_scrutinee_bool) = match match_src {
|
||||
IfDesugar { contains_else_clause } => (true, !contains_else_clause, true),
|
||||
IfLetDesugar { contains_else_clause, .. } => (true, !contains_else_clause, false),
|
||||
WhileDesugar => (false, false, true),
|
||||
_ => (false, false, false),
|
||||
|
@ -115,8 +149,12 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
let arm_ty = if source_if
|
||||
&& if_no_else
|
||||
&& i != 0
|
||||
&& self.if_fallback_coercion(expr.span, &arms[0].body, &mut coercion)
|
||||
{
|
||||
&& self.if_fallback_coercion(
|
||||
expr.span,
|
||||
&arms[0].body,
|
||||
&mut coercion,
|
||||
|hir_id, span| self.maybe_get_coercion_reason(hir_id, span),
|
||||
) {
|
||||
tcx.ty_error()
|
||||
} else {
|
||||
// Only call this if this is not an `if` expr with an expected type and no `else`
|
||||
|
@ -125,58 +163,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
all_arms_diverge &= self.diverges.get();
|
||||
|
||||
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
|
||||
// we check if the different arms would work with boxed trait objects instead and
|
||||
// provide a structured suggestion in that case.
|
||||
let opt_suggest_box_span = match (
|
||||
orig_expected,
|
||||
self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty)),
|
||||
) {
|
||||
(Expectation::ExpectHasType(expected), Some((id, ty)))
|
||||
if self.in_tail_expr && self.can_coerce(arm_ty, expected) =>
|
||||
{
|
||||
let impl_trait_ret_ty = self.infcx.instantiate_opaque_types(
|
||||
id,
|
||||
self.body_id,
|
||||
self.param_env,
|
||||
ty,
|
||||
arm.body.span,
|
||||
);
|
||||
let mut suggest_box = !impl_trait_ret_ty.obligations.is_empty();
|
||||
for o in impl_trait_ret_ty.obligations {
|
||||
match o.predicate.skip_binders_unchecked() {
|
||||
ty::PredicateAtom::Trait(t, constness) => {
|
||||
let pred = ty::PredicateAtom::Trait(
|
||||
ty::TraitPredicate {
|
||||
trait_ref: ty::TraitRef {
|
||||
def_id: t.def_id(),
|
||||
substs: self.infcx.tcx.mk_substs_trait(arm_ty, &[]),
|
||||
},
|
||||
},
|
||||
constness,
|
||||
);
|
||||
let obl = Obligation::new(
|
||||
o.cause.clone(),
|
||||
self.param_env,
|
||||
pred.to_predicate(self.infcx.tcx),
|
||||
);
|
||||
suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl);
|
||||
if !suggest_box {
|
||||
// We've encountered some obligation that didn't hold, so the
|
||||
// return expression can't just be boxed. We don't need to
|
||||
// evaluate the rest of the obligations.
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// If all the obligations hold (or there are no obligations) the tail expression
|
||||
// we can suggest to return a boxed trait object instead of an opaque type.
|
||||
if suggest_box { self.ret_type_span } else { None }
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let opt_suggest_box_span =
|
||||
self.opt_suggest_box_span(arm.body.span, arm_ty, orig_expected);
|
||||
|
||||
if source_if {
|
||||
let then_expr = &arms[0].body;
|
||||
|
@ -279,7 +267,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
) {
|
||||
use hir::MatchSource::*;
|
||||
let msg = match source {
|
||||
IfDesugar { .. } | IfLetDesugar { .. } => "block in `if` expression",
|
||||
IfLetDesugar { .. } => "block in `if` expression",
|
||||
WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression",
|
||||
_ => "arm",
|
||||
};
|
||||
|
@ -291,15 +279,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// Handle the fallback arm of a desugared if(-let) like a missing else.
|
||||
///
|
||||
/// Returns `true` if there was an error forcing the coercion to the `()` type.
|
||||
fn if_fallback_coercion(
|
||||
pub(crate) fn if_fallback_coercion<F, T>(
|
||||
&self,
|
||||
span: Span,
|
||||
then_expr: &'tcx hir::Expr<'tcx>,
|
||||
coercion: &mut CoerceMany<'tcx, '_, rustc_hir::Arm<'tcx>>,
|
||||
) -> bool {
|
||||
coercion: &mut CoerceMany<'tcx, '_, T>,
|
||||
ret_reason: F,
|
||||
) -> bool
|
||||
where
|
||||
F: Fn(hir::HirId, Span) -> Option<(Span, String)>,
|
||||
T: AsCoercionSite,
|
||||
{
|
||||
// If this `if` expr is the parent's function return expr,
|
||||
// the cause of the type coercion is the return type, point at it. (#25228)
|
||||
let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span);
|
||||
let ret_reason = ret_reason(then_expr.hir_id, span);
|
||||
let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
let mut error = false;
|
||||
coercion.coerce_forced_unit(
|
||||
|
@ -322,38 +315,25 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
error
|
||||
}
|
||||
|
||||
fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, span: Span) -> Option<(Span, String)> {
|
||||
use hir::Node::{Block, Item, Local};
|
||||
|
||||
let hir = self.tcx.hir();
|
||||
let arm_id = hir.get_parent_node(hir_id);
|
||||
let match_id = hir.get_parent_node(arm_id);
|
||||
let containing_id = hir.get_parent_node(match_id);
|
||||
|
||||
let node = hir.get(containing_id);
|
||||
if let Block(block) = node {
|
||||
// check that the body's parent is an fn
|
||||
let parent = hir.get(hir.get_parent_node(hir.get_parent_node(block.hir_id)));
|
||||
if let (Some(expr), Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) =
|
||||
(&block.expr, parent)
|
||||
{
|
||||
// check that the `if` expr without `else` is the fn body's expr
|
||||
if expr.span == span {
|
||||
return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| {
|
||||
let span = fn_decl.output.span();
|
||||
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?;
|
||||
Some((span, format!("expected `{}` because of this return type", snippet)))
|
||||
});
|
||||
}
|
||||
}
|
||||
create_maybe_get_coercion_reason!(
|
||||
maybe_get_coercion_reason,
|
||||
|hir: rustc_middle::hir::map::Map<'a>, id| {
|
||||
let arm_id = hir.get_parent_node(id);
|
||||
let match_id = hir.get_parent_node(arm_id);
|
||||
let containing_id = hir.get_parent_node(match_id);
|
||||
hir.get(containing_id)
|
||||
}
|
||||
if let Local(hir::Local { ty: Some(_), pat, .. }) = node {
|
||||
return Some((pat.span, "expected because of this assignment".to_string()));
|
||||
}
|
||||
None
|
||||
}
|
||||
);
|
||||
|
||||
fn if_cause(
|
||||
create_maybe_get_coercion_reason!(
|
||||
maybe_get_coercion_reason_if,
|
||||
|hir: rustc_middle::hir::map::Map<'a>, id| {
|
||||
let rslt = hir.get_parent_node(hir.get_parent_node(id));
|
||||
hir.get(rslt)
|
||||
}
|
||||
);
|
||||
|
||||
pub(crate) fn if_cause(
|
||||
&self,
|
||||
span: Span,
|
||||
then_expr: &'tcx hir::Expr<'tcx>,
|
||||
|
@ -546,6 +526,58 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
(block.span, None)
|
||||
}
|
||||
}
|
||||
|
||||
// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait`
|
||||
// we check if the different arms would work with boxed trait objects instead and
|
||||
// provide a structured suggestion in that case.
|
||||
pub(crate) fn opt_suggest_box_span(
|
||||
&self,
|
||||
span: Span,
|
||||
outer_ty: &'tcx TyS<'tcx>,
|
||||
orig_expected: Expectation<'tcx>,
|
||||
) -> Option<Span> {
|
||||
match (orig_expected, self.ret_coercion_impl_trait.map(|ty| (self.body_id.owner, ty))) {
|
||||
(Expectation::ExpectHasType(expected), Some((id, ty)))
|
||||
if self.in_tail_expr && self.can_coerce(outer_ty, expected) =>
|
||||
{
|
||||
let impl_trait_ret_ty =
|
||||
self.infcx.instantiate_opaque_types(id, self.body_id, self.param_env, ty, span);
|
||||
let mut suggest_box = !impl_trait_ret_ty.obligations.is_empty();
|
||||
for o in impl_trait_ret_ty.obligations {
|
||||
match o.predicate.skip_binders_unchecked() {
|
||||
ty::PredicateAtom::Trait(t, constness) => {
|
||||
let pred = ty::PredicateAtom::Trait(
|
||||
ty::TraitPredicate {
|
||||
trait_ref: ty::TraitRef {
|
||||
def_id: t.def_id(),
|
||||
substs: self.infcx.tcx.mk_substs_trait(outer_ty, &[]),
|
||||
},
|
||||
},
|
||||
constness,
|
||||
);
|
||||
let obl = Obligation::new(
|
||||
o.cause.clone(),
|
||||
self.param_env,
|
||||
pred.to_predicate(self.infcx.tcx),
|
||||
);
|
||||
suggest_box &= self.infcx.predicate_must_hold_modulo_regions(&obl);
|
||||
if !suggest_box {
|
||||
// We've encountered some obligation that didn't hold, so the
|
||||
// return expression can't just be boxed. We don't need to
|
||||
// evaluate the rest of the obligations.
|
||||
break;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// If all the obligations hold (or there are no obligations) the tail expression
|
||||
// we can suggest to return a boxed trait object instead of an opaque type.
|
||||
if suggest_box { self.ret_type_span } else { None }
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn arms_contain_ref_bindings(arms: &'tcx [hir::Arm<'tcx>]) -> Option<hir::Mutability> {
|
||||
|
|
|
@ -1443,14 +1443,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
|
|||
&mut err, expr, expected, found, cause.span, blk_id,
|
||||
);
|
||||
let parent = fcx.tcx.hir().get(parent_id);
|
||||
if let (Some(match_expr), true, false) = (
|
||||
fcx.tcx.hir().get_match_if_cause(expr.hir_id),
|
||||
if let (Some(cond_expr), true, false) = (
|
||||
fcx.tcx.hir().get_if_cause(expr.hir_id),
|
||||
expected.is_unit(),
|
||||
pointing_at_return_type,
|
||||
) {
|
||||
if match_expr.span.desugaring_kind().is_none() {
|
||||
err.span_label(match_expr.span, "expected this to be `()`");
|
||||
fcx.suggest_semicolon_at_end(match_expr.span, &mut err);
|
||||
if cond_expr.span.desugaring_kind().is_none() {
|
||||
err.span_label(cond_expr.span, "expected this to be `()`");
|
||||
fcx.suggest_semicolon_at_end(cond_expr.span, &mut err);
|
||||
}
|
||||
}
|
||||
fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main))
|
||||
|
|
|
@ -10,6 +10,7 @@ use crate::check::method::{probe, MethodError, SelfSource};
|
|||
use crate::check::report_unexpected_variant_res;
|
||||
use crate::check::BreakableCtxt;
|
||||
use crate::check::Diverges;
|
||||
use crate::check::DynamicCoerceMany;
|
||||
use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation};
|
||||
use crate::check::FnCtxt;
|
||||
use crate::check::Needs;
|
||||
|
@ -188,7 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
// Warn for non-block expressions with diverging children.
|
||||
match expr.kind {
|
||||
ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {}
|
||||
ExprKind::Block(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {}
|
||||
// If `expr` is a result of desugaring the try block and is an ok-wrapped
|
||||
// diverging expression (e.g. it arose from desugaring of `try { return }`),
|
||||
// we skip issuing a warning because it is autogenerated code.
|
||||
|
@ -285,6 +286,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.check_expr_eq_type(&e, ty);
|
||||
ty
|
||||
}
|
||||
ExprKind::If(ref cond, ref then_expr, ref opt_else_expr) => self.check_then_else(
|
||||
&cond,
|
||||
then_expr,
|
||||
opt_else_expr.as_ref().map(|e| &**e),
|
||||
expr.span,
|
||||
expected,
|
||||
),
|
||||
ExprKind::DropTemps(ref e) => self.check_expr_with_expectation(e, expected),
|
||||
ExprKind::Array(ref args) => self.check_expr_array(args, expected, expr),
|
||||
ExprKind::ConstBlock(ref anon_const) => self.to_const(anon_const).ty,
|
||||
|
@ -739,8 +747,67 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
err.emit();
|
||||
}
|
||||
|
||||
// A generic function for checking the 'then' and 'else' clauses in an 'if'
|
||||
// or 'if-else' expression.
|
||||
fn check_then_else(
|
||||
&self,
|
||||
cond_expr: &'tcx hir::Expr<'tcx>,
|
||||
then_expr: &'tcx hir::Expr<'tcx>,
|
||||
opt_else_expr: Option<&'tcx hir::Expr<'tcx>>,
|
||||
sp: Span,
|
||||
orig_expected: Expectation<'tcx>,
|
||||
) -> Ty<'tcx> {
|
||||
let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {});
|
||||
|
||||
self.warn_if_unreachable(cond_expr.hir_id, then_expr.span, "block in `if` expression");
|
||||
|
||||
let cond_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
let expected = orig_expected.adjust_for_branches(self);
|
||||
let then_ty = self.check_expr_with_expectation(then_expr, expected);
|
||||
let then_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
||||
// We've already taken the expected type's preferences
|
||||
// into account when typing the `then` branch. To figure
|
||||
// out the initial shot at a LUB, we thus only consider
|
||||
// `expected` if it represents a *hard* constraint
|
||||
// (`only_has_type`); otherwise, we just go with a
|
||||
// fresh type variable.
|
||||
let coerce_to_ty = expected.coercion_target_type(self, sp);
|
||||
let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty);
|
||||
|
||||
coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
|
||||
|
||||
if let Some(else_expr) = opt_else_expr {
|
||||
let else_ty = self.check_expr_with_expectation(else_expr, expected);
|
||||
let else_diverges = self.diverges.get();
|
||||
|
||||
let opt_suggest_box_span =
|
||||
self.opt_suggest_box_span(else_expr.span, else_ty, orig_expected);
|
||||
let if_cause =
|
||||
self.if_cause(sp, then_expr, else_expr, then_ty, else_ty, opt_suggest_box_span);
|
||||
|
||||
coerce.coerce(self, &if_cause, else_expr, else_ty);
|
||||
|
||||
// We won't diverge unless both branches do (or the condition does).
|
||||
self.diverges.set(cond_diverges | then_diverges & else_diverges);
|
||||
} else {
|
||||
self.if_fallback_coercion(sp, then_expr, &mut coerce, |hir_id, span| {
|
||||
self.maybe_get_coercion_reason_if(hir_id, span)
|
||||
});
|
||||
|
||||
// If the condition is false we can't diverge.
|
||||
self.diverges.set(cond_diverges);
|
||||
}
|
||||
|
||||
let result_ty = coerce.complete(self);
|
||||
if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty }
|
||||
}
|
||||
|
||||
/// Type check assignment expression `expr` of form `lhs = rhs`.
|
||||
/// The expected type is `()` and is passsed to the function for the purposes of diagnostics.
|
||||
/// The expected type is `()` and is passed to the function for the purposes of diagnostics.
|
||||
fn check_expr_assign(
|
||||
&self,
|
||||
expr: &'tcx hir::Expr<'tcx>,
|
||||
|
@ -765,17 +832,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
};
|
||||
if !lhs.is_syntactic_place_expr() {
|
||||
// Do not suggest `if let x = y` as `==` is way more likely to be the intention.
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind:
|
||||
ExprKind::Match(
|
||||
_,
|
||||
_,
|
||||
hir::MatchSource::IfDesugar { .. } | hir::MatchSource::WhileDesugar,
|
||||
),
|
||||
..
|
||||
}) = self.tcx.hir().get(
|
||||
self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)),
|
||||
) {
|
||||
let mut span_err = || {
|
||||
// Likely `if let` intended.
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.shrink_to_lo(),
|
||||
|
@ -783,6 +840,18 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
"let ".to_string(),
|
||||
applicability,
|
||||
);
|
||||
};
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind: ExprKind::Match(_, _, hir::MatchSource::WhileDesugar),
|
||||
..
|
||||
}) = self.tcx.hir().get(
|
||||
self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(expr.hir_id)),
|
||||
) {
|
||||
span_err();
|
||||
} else if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
|
||||
self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id))
|
||||
{
|
||||
span_err();
|
||||
}
|
||||
}
|
||||
if eq {
|
||||
|
|
|
@ -803,33 +803,39 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
/// // ^^^^ point at this instead of the whole `if` expression
|
||||
/// ```
|
||||
fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span {
|
||||
if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
|
||||
let arm_spans: Vec<Span> = arms
|
||||
.iter()
|
||||
.filter_map(|arm| {
|
||||
self.in_progress_typeck_results
|
||||
.and_then(|typeck_results| {
|
||||
typeck_results.borrow().node_type_opt(arm.body.hir_id)
|
||||
})
|
||||
.and_then(|arm_ty| {
|
||||
if arm_ty.is_never() {
|
||||
None
|
||||
} else {
|
||||
Some(match &arm.body.kind {
|
||||
// Point at the tail expression when possible.
|
||||
hir::ExprKind::Block(block, _) => {
|
||||
block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
|
||||
}
|
||||
_ => arm.body.span,
|
||||
})
|
||||
let check_in_progress = |elem: &hir::Expr<'_>| {
|
||||
self.in_progress_typeck_results
|
||||
.and_then(|typeck_results| typeck_results.borrow().node_type_opt(elem.hir_id))
|
||||
.and_then(|ty| {
|
||||
if ty.is_never() {
|
||||
None
|
||||
} else {
|
||||
Some(match &elem.kind {
|
||||
// Point at the tail expression when possible.
|
||||
hir::ExprKind::Block(block, _) => {
|
||||
block.expr.as_ref().map(|e| e.span).unwrap_or(block.span)
|
||||
}
|
||||
_ => elem.span,
|
||||
})
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
if arm_spans.len() == 1 {
|
||||
return arm_spans[0];
|
||||
};
|
||||
|
||||
if let hir::ExprKind::If(_, _, Some(el)) = &expr.kind {
|
||||
if let Some(rslt) = check_in_progress(el) {
|
||||
return rslt;
|
||||
}
|
||||
}
|
||||
|
||||
if let hir::ExprKind::Match(_, arms, _) = &expr.kind {
|
||||
let mut iter = arms.iter().filter_map(|arm| check_in_progress(&arm.body));
|
||||
if let Some(span) = iter.next() {
|
||||
if iter.next().is_none() {
|
||||
return span;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr.span
|
||||
}
|
||||
|
||||
|
|
|
@ -358,6 +358,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
ExprKind::Call(..)
|
||||
| ExprKind::MethodCall(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Match(..)
|
||||
| ExprKind::Block(..) => {
|
||||
err.span_suggestion(
|
||||
|
|
|
@ -219,6 +219,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
self.consume_exprs(exprs);
|
||||
}
|
||||
|
||||
hir::ExprKind::If(ref cond_expr, ref then_expr, ref opt_else_expr) => {
|
||||
self.consume_expr(&cond_expr);
|
||||
self.consume_expr(&then_expr);
|
||||
if let Some(ref else_expr) = *opt_else_expr {
|
||||
self.consume_expr(&else_expr);
|
||||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::Match(ref discr, arms, _) => {
|
||||
let discr_place = return_if_err!(self.mc.cat_expr(&discr));
|
||||
self.borrow_expr(&discr, ty::ImmBorrow);
|
||||
|
|
|
@ -364,6 +364,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
|||
| hir::ExprKind::Cast(..)
|
||||
| hir::ExprKind::DropTemps(..)
|
||||
| hir::ExprKind::Array(..)
|
||||
| hir::ExprKind::If(..)
|
||||
| hir::ExprKind::Tup(..)
|
||||
| hir::ExprKind::Binary(..)
|
||||
| hir::ExprKind::Block(..)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue