Introduce hir::ExprKind::Let - Take 2
This commit is contained in:
parent
2d9f2eae84
commit
6aa9937a76
128 changed files with 2080 additions and 2196 deletions
|
@ -1302,7 +1302,9 @@ pub enum ExprKind {
|
|||
Type(P<Expr>, P<Ty>),
|
||||
/// A `let pat = expr` expression that is only semantically allowed in the condition
|
||||
/// of `if` / `while` expressions. (e.g., `if let 0 = x { .. }`).
|
||||
Let(P<Pat>, P<Expr>),
|
||||
///
|
||||
/// `Span` represents the whole `let pat = expr` statement.
|
||||
Let(P<Pat>, P<Expr>, Span),
|
||||
/// An `if` block, with an optional `else` block.
|
||||
///
|
||||
/// `if expr { block } else { expr }`
|
||||
|
|
|
@ -1237,7 +1237,7 @@ pub fn noop_visit_expr<T: MutVisitor>(
|
|||
vis.visit_ty(ty);
|
||||
}
|
||||
ExprKind::AddrOf(_, _, ohs) => vis.visit_expr(ohs),
|
||||
ExprKind::Let(pat, scrutinee) => {
|
||||
ExprKind::Let(pat, scrutinee, _) => {
|
||||
vis.visit_pat(pat);
|
||||
vis.visit_expr(scrutinee);
|
||||
}
|
||||
|
|
|
@ -779,9 +779,9 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) {
|
|||
visitor.visit_expr(subexpression);
|
||||
visitor.visit_ty(typ)
|
||||
}
|
||||
ExprKind::Let(ref pat, ref scrutinee) => {
|
||||
ExprKind::Let(ref pat, ref expr, _) => {
|
||||
visitor.visit_pat(pat);
|
||||
visitor.visit_expr(scrutinee);
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
ExprKind::If(ref head_expression, ref if_block, ref optional_else) => {
|
||||
visitor.visit_expr(head_expression);
|
||||
|
|
|
@ -86,32 +86,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
let ohs = self.lower_expr(ohs);
|
||||
hir::ExprKind::AddrOf(k, m, ohs)
|
||||
}
|
||||
ExprKind::Let(ref pat, ref scrutinee) => {
|
||||
self.lower_expr_let(e.span, pat, scrutinee)
|
||||
ExprKind::Let(ref pat, ref scrutinee, span) => {
|
||||
hir::ExprKind::Let(self.lower_pat(pat), self.lower_expr(scrutinee), span)
|
||||
}
|
||||
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
||||
self.lower_expr_if(cond, then, else_opt.as_deref())
|
||||
}
|
||||
ExprKind::If(ref cond, ref then, ref else_opt) => match cond.kind {
|
||||
ExprKind::Let(ref pat, ref scrutinee) => {
|
||||
self.lower_expr_if_let(e.span, pat, scrutinee, then, else_opt.as_deref())
|
||||
}
|
||||
ExprKind::Paren(ref paren) => match paren.peel_parens().kind {
|
||||
ExprKind::Let(ref pat, ref scrutinee) => {
|
||||
// A user has written `if (let Some(x) = foo) {`, we want to avoid
|
||||
// confusing them with mentions of nightly features.
|
||||
// If this logic is changed, you will also likely need to touch
|
||||
// `unused::UnusedParens::check_expr`.
|
||||
self.if_let_expr_with_parens(cond, &paren.peel_parens());
|
||||
self.lower_expr_if_let(
|
||||
e.span,
|
||||
pat,
|
||||
scrutinee,
|
||||
then,
|
||||
else_opt.as_deref(),
|
||||
)
|
||||
}
|
||||
_ => self.lower_expr_if(cond, then, else_opt.as_deref()),
|
||||
},
|
||||
_ => self.lower_expr_if(cond, then, else_opt.as_deref()),
|
||||
},
|
||||
ExprKind::While(ref cond, ref body, opt_label) => self
|
||||
.with_loop_scope(e.id, |this| {
|
||||
this.lower_expr_while_in_loop_scope(e.span, cond, body, opt_label)
|
||||
|
@ -368,126 +348,51 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
hir::ExprKind::Call(f, self.lower_exprs(&real_args))
|
||||
}
|
||||
|
||||
fn if_let_expr_with_parens(&mut self, cond: &Expr, paren: &Expr) {
|
||||
let start = cond.span.until(paren.span);
|
||||
let end = paren.span.shrink_to_hi().until(cond.span.shrink_to_hi());
|
||||
self.sess
|
||||
.struct_span_err(
|
||||
vec![start, end],
|
||||
"invalid parentheses around `let` expression in `if let`",
|
||||
)
|
||||
.multipart_suggestion(
|
||||
"`if let` needs to be written without parentheses",
|
||||
vec![(start, String::new()), (end, String::new())],
|
||||
rustc_errors::Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
// Ideally, we'd remove the feature gating of a `let` expression since we are already
|
||||
// complaining about it here, but `feature_gate::check_crate` has already run by now:
|
||||
// self.sess.parse_sess.gated_spans.ungate_last(sym::let_chains, paren.span);
|
||||
}
|
||||
|
||||
/// Emit an error and lower `ast::ExprKind::Let(pat, scrutinee)` into:
|
||||
/// ```rust
|
||||
/// match scrutinee { pats => true, _ => false }
|
||||
/// ```
|
||||
fn lower_expr_let(&mut self, span: Span, pat: &Pat, scrutinee: &Expr) -> hir::ExprKind<'hir> {
|
||||
// If we got here, the `let` expression is not allowed.
|
||||
|
||||
if self.sess.opts.unstable_features.is_nightly_build() {
|
||||
self.sess
|
||||
.struct_span_err(span, "`let` expressions are not supported here")
|
||||
.note(
|
||||
"only supported directly without parentheses in conditions of `if`- and \
|
||||
`while`-expressions, as well as in `let` chains within parentheses",
|
||||
)
|
||||
.emit();
|
||||
} else {
|
||||
self.sess
|
||||
.struct_span_err(span, "expected expression, found statement (`let`)")
|
||||
.note("variable declaration using `let` is a statement")
|
||||
.emit();
|
||||
}
|
||||
|
||||
// For better recovery, we emit:
|
||||
// ```
|
||||
// match scrutinee { pat => true, _ => false }
|
||||
// ```
|
||||
// While this doesn't fully match the user's intent, it has key advantages:
|
||||
// 1. We can avoid using `abort_if_errors`.
|
||||
// 2. We can typeck both `pat` and `scrutinee`.
|
||||
// 3. `pat` is allowed to be refutable.
|
||||
// 4. The return type of the block is `bool` which seems like what the user wanted.
|
||||
let scrutinee = self.lower_expr(scrutinee);
|
||||
let then_arm = {
|
||||
let pat = self.lower_pat(pat);
|
||||
let expr = self.expr_bool(span, true);
|
||||
self.arm(pat, expr)
|
||||
};
|
||||
let else_arm = {
|
||||
let pat = self.pat_wild(span);
|
||||
let expr = self.expr_bool(span, false);
|
||||
self.arm(pat, expr)
|
||||
};
|
||||
hir::ExprKind::Match(
|
||||
scrutinee,
|
||||
arena_vec![self; then_arm, else_arm],
|
||||
hir::MatchSource::Normal,
|
||||
)
|
||||
}
|
||||
|
||||
fn lower_expr_if(
|
||||
&mut self,
|
||||
cond: &Expr,
|
||||
then: &Block,
|
||||
else_opt: Option<&Expr>,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
let cond = self.lower_expr(cond);
|
||||
let wrapped_cond = match cond.kind {
|
||||
hir::ExprKind::Let(..) => cond,
|
||||
_ => self.expr_drop_temps(cond.span, cond, AttrVec::new()),
|
||||
};
|
||||
let lowered_cond = self.lower_expr(cond);
|
||||
let new_cond = self.manage_let_cond(lowered_cond);
|
||||
let then_expr = self.lower_block_expr(then);
|
||||
if let Some(rslt) = else_opt {
|
||||
hir::ExprKind::If(
|
||||
wrapped_cond,
|
||||
self.arena.alloc(then_expr),
|
||||
Some(self.lower_expr(rslt)),
|
||||
)
|
||||
hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt)))
|
||||
} else {
|
||||
hir::ExprKind::If(wrapped_cond, self.arena.alloc(then_expr), None)
|
||||
hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), None)
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_expr_if_let(
|
||||
&mut self,
|
||||
span: Span,
|
||||
pat: &Pat,
|
||||
scrutinee: &Expr,
|
||||
then: &Block,
|
||||
else_opt: Option<&Expr>,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
// FIXME(#53667): handle lowering of && and parens.
|
||||
|
||||
// `_ => else_block` where `else_block` is `{}` if there's `None`:
|
||||
let else_pat = self.pat_wild(span);
|
||||
let (else_expr, contains_else_clause) = match else_opt {
|
||||
None => (self.expr_block_empty(span.shrink_to_hi()), false),
|
||||
Some(els) => (self.lower_expr(els), true),
|
||||
};
|
||||
let else_arm = self.arm(else_pat, else_expr);
|
||||
|
||||
// Handle then + scrutinee:
|
||||
let scrutinee = self.lower_expr(scrutinee);
|
||||
let then_pat = self.lower_pat(pat);
|
||||
|
||||
let then_expr = self.lower_block_expr(then);
|
||||
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
|
||||
|
||||
let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
|
||||
hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar)
|
||||
// If `cond` kind is `let`, returns `let`. Otherwise, wraps and returns `cond`
|
||||
// in a temporary block.
|
||||
fn manage_let_cond(&mut self, cond: &'hir hir::Expr<'hir>) -> &'hir hir::Expr<'hir> {
|
||||
match cond.kind {
|
||||
hir::ExprKind::Let(..) => cond,
|
||||
_ => {
|
||||
let span_block =
|
||||
self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None);
|
||||
self.expr_drop_temps(span_block, cond, AttrVec::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We desugar: `'label: while $cond $body` into:
|
||||
//
|
||||
// ```
|
||||
// 'label: loop {
|
||||
// if { let _t = $cond; _t } {
|
||||
// $body
|
||||
// }
|
||||
// else {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
|
||||
// to preserve drop semantics since `while $cond { ... }` does not
|
||||
// let temporaries live outside of `cond`.
|
||||
fn lower_expr_while_in_loop_scope(
|
||||
&mut self,
|
||||
span: Span,
|
||||
|
@ -495,72 +400,17 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
body: &Block,
|
||||
opt_label: Option<Label>,
|
||||
) -> hir::ExprKind<'hir> {
|
||||
// FIXME(#53667): handle lowering of && and parens.
|
||||
|
||||
// Note that the block AND the condition are evaluated in the loop scope.
|
||||
// This is done to allow `break` from inside the condition of the loop.
|
||||
|
||||
// `_ => break`:
|
||||
let else_arm = {
|
||||
let else_pat = self.pat_wild(span);
|
||||
let else_expr = self.expr_break(span, ThinVec::new());
|
||||
self.arm(else_pat, else_expr)
|
||||
};
|
||||
|
||||
// Handle then + scrutinee:
|
||||
let (then_pat, scrutinee, desugar, source) = match cond.kind {
|
||||
ExprKind::Let(ref pat, ref scrutinee) => {
|
||||
// to:
|
||||
//
|
||||
// [opt_ident]: loop {
|
||||
// match <sub_expr> {
|
||||
// <pat> => <body>,
|
||||
// _ => break
|
||||
// }
|
||||
// }
|
||||
let scrutinee = self.with_loop_condition_scope(|t| t.lower_expr(scrutinee));
|
||||
let pat = self.lower_pat(pat);
|
||||
(pat, scrutinee, hir::MatchSource::WhileLetDesugar, hir::LoopSource::WhileLet)
|
||||
}
|
||||
_ => {
|
||||
// We desugar: `'label: while $cond $body` into:
|
||||
//
|
||||
// ```
|
||||
// 'label: loop {
|
||||
// match drop-temps { $cond } {
|
||||
// true => $body,
|
||||
// _ => break,
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
|
||||
// Lower condition:
|
||||
let cond = self.with_loop_condition_scope(|this| this.lower_expr(cond));
|
||||
let span_block =
|
||||
self.mark_span_with_reason(DesugaringKind::CondTemporary, cond.span, None);
|
||||
// Wrap in a construct equivalent to `{ let _t = $cond; _t }`
|
||||
// to preserve drop semantics since `while cond { ... }` does not
|
||||
// let temporaries live outside of `cond`.
|
||||
let cond = self.expr_drop_temps(span_block, cond, ThinVec::new());
|
||||
// `true => <then>`:
|
||||
let pat = self.pat_bool(span, true);
|
||||
(pat, cond, hir::MatchSource::WhileDesugar, hir::LoopSource::While)
|
||||
}
|
||||
};
|
||||
let then_expr = self.lower_block_expr(body);
|
||||
let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
|
||||
|
||||
// `match <scrutinee> { ... }`
|
||||
let match_expr =
|
||||
self.expr_match(span, scrutinee, arena_vec![self; then_arm, else_arm], desugar);
|
||||
|
||||
// `[opt_ident]: loop { ... }`
|
||||
hir::ExprKind::Loop(
|
||||
self.block_expr(self.arena.alloc(match_expr)),
|
||||
opt_label,
|
||||
source,
|
||||
span.with_hi(cond.span.hi()),
|
||||
)
|
||||
let lowered_cond = self.with_loop_condition_scope(|t| t.lower_expr(cond));
|
||||
let new_cond = self.manage_let_cond(lowered_cond);
|
||||
let then = self.lower_block_expr(body);
|
||||
let expr_break = self.expr_break(span, ThinVec::new());
|
||||
let stmt_break = self.stmt_expr(span, expr_break);
|
||||
let else_blk = self.block_all(span, arena_vec![self; stmt_break], None);
|
||||
let else_expr = self.arena.alloc(self.expr_block(else_blk, ThinVec::new()));
|
||||
let if_kind = hir::ExprKind::If(new_cond, self.arena.alloc(then), Some(else_expr));
|
||||
let if_expr = self.expr(span, if_kind, ThinVec::new());
|
||||
let block = self.block_expr(self.arena.alloc(if_expr));
|
||||
hir::ExprKind::Loop(block, opt_label, hir::LoopSource::While, span.with_hi(cond.span.hi()))
|
||||
}
|
||||
|
||||
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
|
||||
|
@ -620,7 +470,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
||||
let pat = self.lower_pat(&arm.pat);
|
||||
let guard = arm.guard.as_ref().map(|cond| {
|
||||
if let ExprKind::Let(ref pat, ref scrutinee) = cond.kind {
|
||||
if let ExprKind::Let(ref pat, ref scrutinee, _) = cond.kind {
|
||||
hir::Guard::IfLet(self.lower_pat(pat), self.lower_expr(scrutinee))
|
||||
} else {
|
||||
hir::Guard::If(self.lower_expr(cond))
|
||||
|
@ -1468,7 +1318,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// `::std::option::Option::None => break`
|
||||
let break_arm = {
|
||||
let break_expr =
|
||||
self.with_loop_scope(e.id, |this| this.expr_break(e.span, ThinVec::new()));
|
||||
self.with_loop_scope(e.id, |this| this.expr_break_alloc(e.span, ThinVec::new()));
|
||||
let pat = self.pat_none(e.span);
|
||||
self.arm(pat, break_expr)
|
||||
};
|
||||
|
@ -1681,12 +1531,6 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
// Helper methods for building HIR.
|
||||
// =========================================================================
|
||||
|
||||
/// Constructs a `true` or `false` literal expression.
|
||||
pub(super) fn expr_bool(&mut self, span: Span, val: bool) -> &'hir hir::Expr<'hir> {
|
||||
let lit = Spanned { span, node: LitKind::Bool(val) };
|
||||
self.arena.alloc(self.expr(span, hir::ExprKind::Lit(lit), ThinVec::new()))
|
||||
}
|
||||
|
||||
/// Wrap the given `expr` in a terminating scope using `hir::ExprKind::DropTemps`.
|
||||
///
|
||||
/// In terms of drop order, it has the same effect as wrapping `expr` in
|
||||
|
@ -1721,9 +1565,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
|||
self.expr(span, hir::ExprKind::Match(arg, arms, source), ThinVec::new())
|
||||
}
|
||||
|
||||
fn expr_break(&mut self, span: Span, attrs: AttrVec) -> &'hir hir::Expr<'hir> {
|
||||
fn expr_break(&mut self, span: Span, attrs: AttrVec) -> hir::Expr<'hir> {
|
||||
let expr_break = hir::ExprKind::Break(self.lower_loop_destination(None), None);
|
||||
self.arena.alloc(self.expr(span, expr_break, attrs))
|
||||
self.expr(span, expr_break, attrs)
|
||||
}
|
||||
|
||||
fn expr_break_alloc(&mut self, span: Span, attrs: AttrVec) -> &'hir hir::Expr<'hir> {
|
||||
let expr_break = self.expr_break(span, attrs);
|
||||
self.arena.alloc(expr_break)
|
||||
}
|
||||
|
||||
fn expr_mut_addr_of(&mut self, span: Span, e: &'hir hir::Expr<'hir>) -> hir::Expr<'hir> {
|
||||
|
|
|
@ -2537,12 +2537,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
self.arena.alloc(blk)
|
||||
}
|
||||
|
||||
/// Constructs a `true` or `false` literal pattern.
|
||||
fn pat_bool(&mut self, span: Span, val: bool) -> &'hir hir::Pat<'hir> {
|
||||
let expr = self.expr_bool(span, val);
|
||||
self.pat(span, hir::PatKind::Lit(expr))
|
||||
}
|
||||
|
||||
fn pat_cf_continue(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
|
||||
let field = self.single_pat_field(span, pat);
|
||||
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowContinue, field)
|
||||
|
@ -2624,10 +2618,6 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
|||
)
|
||||
}
|
||||
|
||||
fn pat_wild(&mut self, span: Span) -> &'hir hir::Pat<'hir> {
|
||||
self.pat(span, hir::PatKind::Wild)
|
||||
}
|
||||
|
||||
fn pat(&mut self, span: Span, kind: hir::PatKind<'hir>) -> &'hir hir::Pat<'hir> {
|
||||
self.arena.alloc(hir::Pat {
|
||||
hir_id: self.next_id(),
|
||||
|
|
|
@ -18,6 +18,7 @@ use rustc_parse::validate_attr;
|
|||
use rustc_session::lint::builtin::PATTERNS_IN_FNS_WITHOUT_BODY;
|
||||
use rustc_session::lint::{BuiltinLintDiagnostics, LintBuffer};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::symbol::{kw, sym, Ident};
|
||||
use rustc_span::Span;
|
||||
use std::mem;
|
||||
|
@ -80,6 +81,9 @@ struct AstValidator<'a> {
|
|||
/// certain positions.
|
||||
is_assoc_ty_bound_banned: bool,
|
||||
|
||||
/// Used to allow `let` expressions in certain syntactic locations.
|
||||
is_let_allowed: bool,
|
||||
|
||||
lint_buffer: &'a mut LintBuffer,
|
||||
}
|
||||
|
||||
|
@ -96,6 +100,27 @@ impl<'a> AstValidator<'a> {
|
|||
self.is_impl_trait_banned = old;
|
||||
}
|
||||
|
||||
fn with_let_allowed(&mut self, allowed: bool, f: impl FnOnce(&mut Self, bool)) {
|
||||
let old = mem::replace(&mut self.is_let_allowed, allowed);
|
||||
f(self, old);
|
||||
self.is_let_allowed = old;
|
||||
}
|
||||
|
||||
/// Emits an error banning the `let` expression provided in the given location.
|
||||
fn ban_let_expr(&self, expr: &'a Expr) {
|
||||
let sess = &self.session;
|
||||
if sess.opts.unstable_features.is_nightly_build() {
|
||||
sess.struct_span_err(expr.span, "`let` expressions are not supported here")
|
||||
.note("only supported directly in conditions of `if`- and `while`-expressions")
|
||||
.note("as well as when nested within `&&` and parenthesis in those conditions")
|
||||
.emit();
|
||||
} else {
|
||||
sess.struct_span_err(expr.span, "expected expression, found statement (`let`)")
|
||||
.note("variable declaration using `let` is a statement")
|
||||
.emit();
|
||||
}
|
||||
}
|
||||
|
||||
fn with_banned_assoc_ty_bound(&mut self, f: impl FnOnce(&mut Self)) {
|
||||
let old = mem::replace(&mut self.is_assoc_ty_bound_banned, true);
|
||||
f(self);
|
||||
|
@ -978,20 +1003,49 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
|||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'a Expr) {
|
||||
match &expr.kind {
|
||||
ExprKind::LlvmInlineAsm(..) if !self.session.target.allow_asm => {
|
||||
self.with_let_allowed(false, |this, let_allowed| match &expr.kind {
|
||||
ExprKind::If(cond, then, opt_else) => {
|
||||
this.visit_block(then);
|
||||
walk_list!(this, visit_expr, opt_else);
|
||||
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
|
||||
return;
|
||||
}
|
||||
ExprKind::Let(..) if !let_allowed => this.ban_let_expr(expr),
|
||||
ExprKind::LlvmInlineAsm(..) if !this.session.target.allow_asm => {
|
||||
struct_span_err!(
|
||||
self.session,
|
||||
this.session,
|
||||
expr.span,
|
||||
E0472,
|
||||
"llvm_asm! is unsupported on this target"
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
visit::walk_expr(self, expr);
|
||||
ExprKind::Match(expr, arms) => {
|
||||
this.visit_expr(expr);
|
||||
for arm in arms {
|
||||
this.visit_expr(&arm.body);
|
||||
this.visit_pat(&arm.pat);
|
||||
walk_list!(this, visit_attribute, &arm.attrs);
|
||||
if let Some(ref guard) = arm.guard {
|
||||
if let ExprKind::Let(_, ref expr, _) = guard.kind {
|
||||
this.with_let_allowed(true, |this, _| this.visit_expr(expr));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => {
|
||||
this.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr));
|
||||
return;
|
||||
}
|
||||
ExprKind::While(cond, then, opt_label) => {
|
||||
walk_list!(this, visit_label, opt_label);
|
||||
this.visit_block(then);
|
||||
this.with_let_allowed(true, |this, _| this.visit_expr(cond));
|
||||
return;
|
||||
}
|
||||
_ => visit::walk_expr(this, expr),
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_ty(&mut self, ty: &'a Ty) {
|
||||
|
@ -1634,6 +1688,7 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) ->
|
|||
bound_context: None,
|
||||
is_impl_trait_banned: false,
|
||||
is_assoc_ty_bound_banned: false,
|
||||
is_let_allowed: false,
|
||||
lint_buffer: lints,
|
||||
};
|
||||
visit::walk_crate(&mut validator, krate);
|
||||
|
|
|
@ -1587,19 +1587,14 @@ impl<'a> State<'a> {
|
|||
self.ann.post(self, AnnNode::Block(blk))
|
||||
}
|
||||
|
||||
/// Print a `let pat = scrutinee` expression.
|
||||
crate fn print_let(&mut self, pat: &ast::Pat, scrutinee: &ast::Expr) {
|
||||
/// Print a `let pat = expr` expression.
|
||||
crate fn print_let(&mut self, pat: &ast::Pat, expr: &ast::Expr) {
|
||||
self.s.word("let ");
|
||||
|
||||
self.print_pat(pat);
|
||||
self.s.space();
|
||||
|
||||
self.word_space("=");
|
||||
self.print_expr_cond_paren(
|
||||
scrutinee,
|
||||
Self::cond_needs_par(scrutinee)
|
||||
|| parser::needs_par_as_let_scrutinee(scrutinee.precedence().order()),
|
||||
)
|
||||
let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
|
||||
}
|
||||
|
||||
fn print_else(&mut self, els: Option<&ast::Expr>) {
|
||||
|
@ -1632,10 +1627,8 @@ impl<'a> State<'a> {
|
|||
|
||||
crate fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) {
|
||||
self.head("if");
|
||||
|
||||
self.print_expr_as_cond(test);
|
||||
self.s.space();
|
||||
|
||||
self.print_block(blk);
|
||||
self.print_else(elseopt)
|
||||
}
|
||||
|
@ -1668,13 +1661,13 @@ impl<'a> State<'a> {
|
|||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
|
||||
}
|
||||
|
||||
/// Does `expr` need parenthesis when printed in a condition position?
|
||||
// Does `expr` need parenthesis when printed in a condition position?
|
||||
//
|
||||
// These cases need parens due to the parse error observed in #26461: `if return {}`
|
||||
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
|
||||
fn cond_needs_par(expr: &ast::Expr) -> bool {
|
||||
match expr.kind {
|
||||
// These cases need parens due to the parse error observed in #26461: `if return {}`
|
||||
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
|
||||
ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) | ast::ExprKind::Break(..) => true,
|
||||
|
||||
ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true,
|
||||
_ => parser::contains_exterior_struct_lit(expr),
|
||||
}
|
||||
}
|
||||
|
@ -1919,7 +1912,7 @@ impl<'a> State<'a> {
|
|||
self.word_space(":");
|
||||
self.print_type(ty);
|
||||
}
|
||||
ast::ExprKind::Let(ref pat, ref scrutinee) => {
|
||||
ast::ExprKind::Let(ref pat, ref scrutinee, _) => {
|
||||
self.print_let(pat, scrutinee);
|
||||
}
|
||||
ast::ExprKind::If(ref test, ref blk, ref elseopt) => {
|
||||
|
|
|
@ -354,6 +354,7 @@ pub trait MacResult {
|
|||
fn make_expr(self: Box<Self>) -> Option<P<ast::Expr>> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Creates zero or more items.
|
||||
fn make_items(self: Box<Self>) -> Option<SmallVec<[P<ast::Item>; 1]>> {
|
||||
None
|
||||
|
|
|
@ -1482,6 +1482,7 @@ impl Expr<'_> {
|
|||
ExprKind::Type(..) | ExprKind::Cast(..) => ExprPrecedence::Cast,
|
||||
ExprKind::DropTemps(ref expr, ..) => expr.precedence(),
|
||||
ExprKind::If(..) => ExprPrecedence::If,
|
||||
ExprKind::Let(..) => ExprPrecedence::Let,
|
||||
ExprKind::Loop(..) => ExprPrecedence::Loop,
|
||||
ExprKind::Match(..) => ExprPrecedence::Match,
|
||||
ExprKind::Closure(..) => ExprPrecedence::Closure,
|
||||
|
@ -1552,6 +1553,7 @@ impl Expr<'_> {
|
|||
| ExprKind::Break(..)
|
||||
| ExprKind::Continue(..)
|
||||
| ExprKind::Ret(..)
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Assign(..)
|
||||
| ExprKind::InlineAsm(..)
|
||||
|
@ -1634,6 +1636,7 @@ impl Expr<'_> {
|
|||
| ExprKind::Break(..)
|
||||
| ExprKind::Continue(..)
|
||||
| ExprKind::Ret(..)
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Loop(..)
|
||||
| ExprKind::Assign(..)
|
||||
| ExprKind::InlineAsm(..)
|
||||
|
@ -1725,6 +1728,11 @@ pub enum ExprKind<'hir> {
|
|||
/// This construct only exists to tweak the drop order in HIR lowering.
|
||||
/// An example of that is the desugaring of `for` loops.
|
||||
DropTemps(&'hir Expr<'hir>),
|
||||
/// A `let $pat = $expr` expression.
|
||||
///
|
||||
/// These are not `Local` and only occur as expressions.
|
||||
/// The `let Some(x) = foo()` in `if let Some(x) = foo()` is an example of `Let(..)`.
|
||||
Let(&'hir Pat<'hir>, &'hir Expr<'hir>, Span),
|
||||
/// An `if` block, with an optional else block.
|
||||
///
|
||||
/// I.e., `if <expr> { <expr> } else { <expr> }`.
|
||||
|
@ -1884,15 +1892,6 @@ pub enum LocalSource {
|
|||
pub enum MatchSource {
|
||||
/// A `match _ { .. }`.
|
||||
Normal,
|
||||
/// An `if let _ = _ { .. }` (optionally with `else { .. }`).
|
||||
IfLetDesugar { contains_else_clause: bool },
|
||||
/// An `if let _ = _ => { .. }` match guard.
|
||||
IfLetGuardDesugar,
|
||||
/// A `while _ { .. }` (which was desugared to a `loop { match _ { .. } }`).
|
||||
WhileDesugar,
|
||||
/// A `while let _ = _ { .. }` (which was desugared to a
|
||||
/// `loop { match _ { .. } }`).
|
||||
WhileLetDesugar,
|
||||
/// A desugared `for _ in _ { .. }` loop.
|
||||
ForLoopDesugar,
|
||||
/// A desugared `?` operator.
|
||||
|
@ -1902,12 +1901,11 @@ pub enum MatchSource {
|
|||
}
|
||||
|
||||
impl MatchSource {
|
||||
pub fn name(self) -> &'static str {
|
||||
#[inline]
|
||||
pub const fn name(self) -> &'static str {
|
||||
use MatchSource::*;
|
||||
match self {
|
||||
Normal => "match",
|
||||
IfLetDesugar { .. } | IfLetGuardDesugar => "if",
|
||||
WhileDesugar | WhileLetDesugar => "while",
|
||||
ForLoopDesugar => "for",
|
||||
TryDesugar => "?",
|
||||
AwaitDesugar => ".await",
|
||||
|
@ -1922,8 +1920,6 @@ pub enum LoopSource {
|
|||
Loop,
|
||||
/// A `while _ { .. }` loop.
|
||||
While,
|
||||
/// A `while let _ = _ { .. }` loop.
|
||||
WhileLet,
|
||||
/// A `for _ in _ { .. }` loop.
|
||||
ForLoop,
|
||||
}
|
||||
|
@ -1932,7 +1928,7 @@ impl LoopSource {
|
|||
pub fn name(self) -> &'static str {
|
||||
match self {
|
||||
LoopSource::Loop => "loop",
|
||||
LoopSource::While | LoopSource::WhileLet => "while",
|
||||
LoopSource::While => "while",
|
||||
LoopSource::ForLoop => "for",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1163,6 +1163,10 @@ pub fn walk_expr<'v, V: Visitor<'v>>(visitor: &mut V, expression: &'v Expr<'v>)
|
|||
ExprKind::DropTemps(ref subexpression) => {
|
||||
visitor.visit_expr(subexpression);
|
||||
}
|
||||
ExprKind::Let(ref pat, ref expr, _) => {
|
||||
visitor.visit_expr(expr);
|
||||
visitor.visit_pat(pat);
|
||||
}
|
||||
ExprKind::If(ref cond, ref then, ref else_opt) => {
|
||||
visitor.visit_expr(cond);
|
||||
visitor.visit_expr(then);
|
||||
|
|
|
@ -1092,53 +1092,30 @@ impl<'a> State<'a> {
|
|||
}
|
||||
|
||||
fn print_else(&mut self, els: Option<&hir::Expr<'_>>) {
|
||||
match els {
|
||||
Some(else_) => {
|
||||
match else_.kind {
|
||||
// "another else-if"
|
||||
hir::ExprKind::If(ref i, ref then, ref e) => {
|
||||
self.cbox(INDENT_UNIT - 1);
|
||||
self.ibox(0);
|
||||
self.s.word(" else if ");
|
||||
self.print_expr_as_cond(&i);
|
||||
self.s.space();
|
||||
self.print_expr(&then);
|
||||
self.print_else(e.as_ref().map(|e| &**e))
|
||||
}
|
||||
// "final else"
|
||||
hir::ExprKind::Block(ref b, _) => {
|
||||
self.cbox(INDENT_UNIT - 1);
|
||||
self.ibox(0);
|
||||
self.s.word(" else ");
|
||||
self.print_block(&b)
|
||||
}
|
||||
hir::ExprKind::Match(ref expr, arms, _) => {
|
||||
// else if let desugared to match
|
||||
assert!(arms.len() == 2, "if let desugars to match with two arms");
|
||||
|
||||
self.s.word(" else ");
|
||||
self.s.word("{");
|
||||
|
||||
self.cbox(INDENT_UNIT);
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.word_nbsp("match");
|
||||
self.print_expr_as_cond(&expr);
|
||||
self.s.space();
|
||||
self.bopen();
|
||||
for arm in arms {
|
||||
self.print_arm(arm);
|
||||
}
|
||||
self.bclose(expr.span);
|
||||
|
||||
self.s.word("}");
|
||||
}
|
||||
// BLEAH, constraints would be great here
|
||||
_ => {
|
||||
panic!("print_if saw if with weird alternative");
|
||||
}
|
||||
if let Some(els_inner) = els {
|
||||
match els_inner.kind {
|
||||
// Another `else if` block.
|
||||
hir::ExprKind::If(ref i, ref then, ref e) => {
|
||||
self.cbox(INDENT_UNIT - 1);
|
||||
self.ibox(0);
|
||||
self.s.word(" else if ");
|
||||
self.print_expr_as_cond(&i);
|
||||
self.s.space();
|
||||
self.print_expr(&then);
|
||||
self.print_else(e.as_ref().map(|e| &**e))
|
||||
}
|
||||
// Final `else` block.
|
||||
hir::ExprKind::Block(ref b, _) => {
|
||||
self.cbox(INDENT_UNIT - 1);
|
||||
self.ibox(0);
|
||||
self.s.word(" else ");
|
||||
self.print_block(&b)
|
||||
}
|
||||
// Constraints would be great here!
|
||||
_ => {
|
||||
panic!("print_if saw if with weird alternative");
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1165,34 +1142,49 @@ impl<'a> State<'a> {
|
|||
self.pclose()
|
||||
}
|
||||
|
||||
pub fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) {
|
||||
let needs_par = expr.precedence().order() < prec;
|
||||
fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) {
|
||||
self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
|
||||
}
|
||||
|
||||
/// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
|
||||
/// `if cond { ... }`.
|
||||
pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
|
||||
}
|
||||
|
||||
/// Prints `expr` or `(expr)` when `needs_par` holds.
|
||||
fn print_expr_cond_paren(&mut self, expr: &hir::Expr<'_>, needs_par: bool) {
|
||||
if needs_par {
|
||||
self.popen();
|
||||
}
|
||||
self.print_expr(expr);
|
||||
if let hir::ExprKind::DropTemps(ref actual_expr) = expr.kind {
|
||||
self.print_expr(actual_expr);
|
||||
} else {
|
||||
self.print_expr(expr);
|
||||
}
|
||||
if needs_par {
|
||||
self.pclose();
|
||||
}
|
||||
}
|
||||
|
||||
/// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in
|
||||
/// `if cond { ... }`.
|
||||
pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
|
||||
let needs_par = match expr.kind {
|
||||
// These cases need parens due to the parse error observed in #26461: `if return {}`
|
||||
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
|
||||
hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) => true,
|
||||
/// Print a `let pat = expr` expression.
|
||||
fn print_let(&mut self, pat: &hir::Pat<'_>, expr: &hir::Expr<'_>) {
|
||||
self.s.word("let ");
|
||||
self.print_pat(pat);
|
||||
self.s.space();
|
||||
self.word_space("=");
|
||||
let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
|
||||
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
|
||||
}
|
||||
|
||||
// Does `expr` need parenthesis when printed in a condition position?
|
||||
//
|
||||
// These cases need parens due to the parse error observed in #26461: `if return {}`
|
||||
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
|
||||
fn cond_needs_par(expr: &hir::Expr<'_>) -> bool {
|
||||
match expr.kind {
|
||||
hir::ExprKind::Break(..) | hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) => true,
|
||||
_ => contains_exterior_struct_lit(expr),
|
||||
};
|
||||
|
||||
if needs_par {
|
||||
self.popen();
|
||||
}
|
||||
self.print_expr(expr);
|
||||
if needs_par {
|
||||
self.pclose();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1314,6 +1306,9 @@ impl<'a> State<'a> {
|
|||
(&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => {
|
||||
parser::PREC_FORCE_PAREN
|
||||
}
|
||||
(&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
|
||||
parser::PREC_FORCE_PAREN
|
||||
}
|
||||
_ => left_prec,
|
||||
};
|
||||
|
||||
|
@ -1531,6 +1526,9 @@ impl<'a> State<'a> {
|
|||
// Print `}`:
|
||||
self.bclose_maybe_open(expr.span, true);
|
||||
}
|
||||
hir::ExprKind::Let(ref pat, ref scrutinee, _) => {
|
||||
self.print_let(pat, scrutinee);
|
||||
}
|
||||
hir::ExprKind::If(ref test, ref blk, ref elseopt) => {
|
||||
self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e));
|
||||
}
|
||||
|
|
|
@ -644,17 +644,6 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
|
|||
scrut_span,
|
||||
..
|
||||
}) => match source {
|
||||
hir::MatchSource::IfLetDesugar { .. } => {
|
||||
let msg = "`if let` arms have incompatible types";
|
||||
err.span_label(cause.span, msg);
|
||||
if let Some(ret_sp) = opt_suggest_box_span {
|
||||
self.suggest_boxing_for_return_impl_trait(
|
||||
err,
|
||||
ret_sp,
|
||||
prior_arms.iter().chain(std::iter::once(&arm_span)).map(|s| *s),
|
||||
);
|
||||
}
|
||||
}
|
||||
hir::MatchSource::TryDesugar => {
|
||||
if let Some(ty::error::ExpectedFound { expected, .. }) = exp_found {
|
||||
let scrut_expr = self.tcx.hir().expect_expr(scrut_hir_id);
|
||||
|
@ -2581,9 +2570,6 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
|||
CompareImplTypeObligation { .. } => Error0308("type not compatible with trait"),
|
||||
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => {
|
||||
Error0308(match source {
|
||||
hir::MatchSource::IfLetDesugar { .. } => {
|
||||
"`if let` arms have incompatible types"
|
||||
}
|
||||
hir::MatchSource::TryDesugar => {
|
||||
"try expression alternatives have incompatible types"
|
||||
}
|
||||
|
@ -2619,10 +2605,6 @@ impl<'tcx> ObligationCauseExt<'tcx> for ObligationCause<'tcx> {
|
|||
CompareImplMethodObligation { .. } => "method type is compatible with trait",
|
||||
CompareImplTypeObligation { .. } => "associated type is compatible with trait",
|
||||
ExprAssignable => "expression is assignable",
|
||||
MatchExpressionArm(box MatchExpressionArmCause { source, .. }) => match source {
|
||||
hir::MatchSource::IfLetDesugar { .. } => "`if let` arms have compatible types",
|
||||
_ => "`match` arms have compatible types",
|
||||
},
|
||||
IfExpression { .. } => "`if` and `else` have incompatible types",
|
||||
IfExpressionWithNoElse => "`if` missing an `else` returns `()`",
|
||||
MainFunctionType => "`main` function has the correct type",
|
||||
|
|
|
@ -614,7 +614,8 @@ trait UnusedDelimLint {
|
|||
let (value, ctx, followed_by_block, left_pos, right_pos) = match e.kind {
|
||||
// Do not lint `unused_braces` in `if let` expressions.
|
||||
If(ref cond, ref block, _)
|
||||
if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
|
||||
if !matches!(cond.kind, Let(_, _, _))
|
||||
|| Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
|
||||
{
|
||||
let left = e.span.lo() + rustc_span::BytePos(2);
|
||||
let right = block.span.lo();
|
||||
|
@ -623,7 +624,8 @@ trait UnusedDelimLint {
|
|||
|
||||
// Do not lint `unused_braces` in `while let` expressions.
|
||||
While(ref cond, ref block, ..)
|
||||
if !matches!(cond.kind, Let(_, _)) || Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
|
||||
if !matches!(cond.kind, Let(_, _, _))
|
||||
|| Self::LINT_EXPR_IN_PATTERN_MATCHING_CTX =>
|
||||
{
|
||||
let left = e.span.lo() + rustc_span::BytePos(5);
|
||||
let right = block.span.lo();
|
||||
|
@ -774,7 +776,7 @@ impl UnusedDelimLint for UnusedParens {
|
|||
self.emit_unused_delims_expr(cx, value, ctx, left_pos, right_pos)
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Let(_, ref expr) => {
|
||||
ast::ExprKind::Let(_, ref expr, _) => {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
expr,
|
||||
|
@ -828,7 +830,7 @@ impl UnusedParens {
|
|||
impl EarlyLintPass for UnusedParens {
|
||||
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
|
||||
match e.kind {
|
||||
ExprKind::Let(ref pat, _) | ExprKind::ForLoop(ref pat, ..) => {
|
||||
ExprKind::Let(ref pat, _, _) | ExprKind::ForLoop(ref pat, ..) => {
|
||||
self.check_unused_parens_pat(cx, pat, false, false);
|
||||
}
|
||||
// We ignore parens in cases like `if (((let Some(0) = Some(1))))` because we already
|
||||
|
@ -1012,7 +1014,7 @@ impl UnusedDelimLint for UnusedBraces {
|
|||
}
|
||||
}
|
||||
}
|
||||
ast::ExprKind::Let(_, ref expr) => {
|
||||
ast::ExprKind::Let(_, ref expr, _) => {
|
||||
self.check_unused_delims_expr(
|
||||
cx,
|
||||
expr,
|
||||
|
|
|
@ -292,6 +292,10 @@ pub enum ExprKind<'tcx> {
|
|||
Loop {
|
||||
body: ExprId,
|
||||
},
|
||||
Let {
|
||||
expr: ExprId,
|
||||
pat: Pat<'tcx>,
|
||||
},
|
||||
/// A `match` expression.
|
||||
Match {
|
||||
scrutinee: ExprId,
|
||||
|
|
|
@ -565,6 +565,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
| ExprKind::If { .. }
|
||||
| ExprKind::Loop { .. }
|
||||
| ExprKind::Block { .. }
|
||||
| ExprKind::Let { .. }
|
||||
| ExprKind::Assign { .. }
|
||||
| ExprKind::AssignOp { .. }
|
||||
| ExprKind::Break { .. }
|
||||
|
|
|
@ -284,6 +284,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
| ExprKind::LogicalOp { .. }
|
||||
| ExprKind::Call { .. }
|
||||
| ExprKind::Field { .. }
|
||||
| ExprKind::Let { .. }
|
||||
| ExprKind::Deref { .. }
|
||||
| ExprKind::Index { .. }
|
||||
| ExprKind::VarRef { .. }
|
||||
|
|
|
@ -46,6 +46,7 @@ impl Category {
|
|||
ExprKind::LogicalOp { .. }
|
||||
| ExprKind::Match { .. }
|
||||
| ExprKind::If { .. }
|
||||
| ExprKind::Let { .. }
|
||||
| ExprKind::NeverToAny { .. }
|
||||
| ExprKind::Use { .. }
|
||||
| ExprKind::Adt { .. }
|
||||
|
|
|
@ -53,48 +53,66 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
this.match_expr(destination, expr_span, block, &this.thir[scrutinee], arms)
|
||||
}
|
||||
ExprKind::If { cond, then, else_opt } => {
|
||||
let place = unpack!(
|
||||
block = this.as_temp(
|
||||
block,
|
||||
Some(this.local_scope()),
|
||||
&this.thir[cond],
|
||||
Mutability::Mut
|
||||
)
|
||||
);
|
||||
let operand = Operand::Move(Place::from(place));
|
||||
|
||||
let mut then_block = this.cfg.start_new_block();
|
||||
let mut else_block = this.cfg.start_new_block();
|
||||
let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
|
||||
this.cfg.terminate(block, source_info, term);
|
||||
|
||||
unpack!(
|
||||
then_block = this.expr_into_dest(destination, then_block, &this.thir[then])
|
||||
);
|
||||
else_block = if let Some(else_opt) = else_opt {
|
||||
unpack!(this.expr_into_dest(destination, else_block, &this.thir[else_opt]))
|
||||
let local_scope = this.local_scope();
|
||||
let (mut then_blk, mut else_blk) =
|
||||
this.then_else_blocks(block, &this.thir[cond], local_scope, source_info);
|
||||
unpack!(then_blk = this.expr_into_dest(destination, then_blk, &this.thir[then]));
|
||||
else_blk = if let Some(else_opt) = else_opt {
|
||||
unpack!(this.expr_into_dest(destination, else_blk, &this.thir[else_opt]))
|
||||
} else {
|
||||
// Body of the `if` expression without an `else` clause must return `()`, thus
|
||||
// we implicitly generate a `else {}` if it is not specified.
|
||||
let correct_si = this.source_info(expr_span.shrink_to_hi());
|
||||
this.cfg.push_assign_unit(else_block, correct_si, destination, this.tcx);
|
||||
else_block
|
||||
this.cfg.push_assign_unit(else_blk, correct_si, destination, this.tcx);
|
||||
else_blk
|
||||
};
|
||||
|
||||
let join_block = this.cfg.start_new_block();
|
||||
this.cfg.terminate(
|
||||
then_block,
|
||||
then_blk,
|
||||
source_info,
|
||||
TerminatorKind::Goto { target: join_block },
|
||||
);
|
||||
this.cfg.terminate(
|
||||
else_block,
|
||||
else_blk,
|
||||
source_info,
|
||||
TerminatorKind::Goto { target: join_block },
|
||||
);
|
||||
|
||||
join_block.unit()
|
||||
}
|
||||
ExprKind::Let { ref pat, expr } => {
|
||||
let (true_block, false_block) =
|
||||
this.lower_let(block, &this.thir[expr], pat, expr_span);
|
||||
|
||||
let join_block = this.cfg.start_new_block();
|
||||
|
||||
this.cfg.push_assign_constant(
|
||||
true_block,
|
||||
source_info,
|
||||
destination,
|
||||
Constant {
|
||||
span: expr_span,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_bool(this.tcx, true).into(),
|
||||
},
|
||||
);
|
||||
|
||||
this.cfg.push_assign_constant(
|
||||
false_block,
|
||||
source_info,
|
||||
destination,
|
||||
Constant {
|
||||
span: expr_span,
|
||||
user_ty: None,
|
||||
literal: ty::Const::from_bool(this.tcx, false).into(),
|
||||
},
|
||||
);
|
||||
|
||||
this.cfg.goto(true_block, source_info, join_block);
|
||||
this.cfg.goto(false_block, source_info, join_block);
|
||||
join_block.unit()
|
||||
}
|
||||
ExprKind::NeverToAny { source } => {
|
||||
let source = &this.thir[source];
|
||||
let is_call =
|
||||
|
|
|
@ -38,11 +38,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
pub(crate) fn then_else_blocks(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
expr: ExprRef<'tcx>,
|
||||
expr: &Expr<'tcx>,
|
||||
scope: region::Scope,
|
||||
source_info: SourceInfo,
|
||||
) -> (BasicBlock, BasicBlock) {
|
||||
let this = self;
|
||||
let expr = this.hir.mirror(expr);
|
||||
let expr_span = expr.span;
|
||||
|
||||
match expr.kind {
|
||||
|
@ -52,24 +52,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
let else_block = unpack!(
|
||||
then_block = this.in_scope(region_scope, lint_level, |this| {
|
||||
let (then_block, else_block) =
|
||||
this.then_else_blocks(block, value, source_info);
|
||||
this.then_else_blocks(block, &this.thir[value], scope, source_info);
|
||||
then_block.and(else_block)
|
||||
})
|
||||
);
|
||||
(then_block, else_block)
|
||||
}
|
||||
ExprKind::Let { expr, pat } => {
|
||||
// TODO: Use correct span.
|
||||
this.lower_let(block, &expr, &pat, expr_span)
|
||||
ExprKind::Let { expr, ref pat } => {
|
||||
// FIXME: Use correct span.
|
||||
this.lower_let(block, &this.thir[expr], pat, expr_span)
|
||||
}
|
||||
_ => {
|
||||
let local_scope = Some(this.local_scope());
|
||||
let place =
|
||||
unpack!(block = this.as_temp(block, local_scope, expr, Mutability::Mut));
|
||||
let mutability = Mutability::Mut;
|
||||
let place = unpack!(block = this.as_temp(block, Some(scope), expr, mutability));
|
||||
let operand = Operand::Move(Place::from(place));
|
||||
let then_block = this.cfg.start_new_block();
|
||||
let else_block = this.cfg.start_new_block();
|
||||
let term = TerminatorKind::if_(this.hir.tcx(), operand, then_block, else_block);
|
||||
let term = TerminatorKind::if_(this.tcx, operand, then_block, else_block);
|
||||
this.cfg.terminate(block, source_info, term);
|
||||
(then_block, else_block)
|
||||
}
|
||||
|
@ -1699,6 +1698,46 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
// Pat binding - used for `let` and function parameters as well.
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
pub fn lower_let(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
expr: &Expr<'tcx>,
|
||||
pat: &Pat<'tcx>,
|
||||
span: Span,
|
||||
) -> (BasicBlock, BasicBlock) {
|
||||
let expr_span = expr.span;
|
||||
let expr_place_builder = unpack!(block = self.lower_scrutinee(block, expr, expr_span));
|
||||
let mut guard_candidate = Candidate::new(expr_place_builder.clone(), &pat, false);
|
||||
let wildcard = Pat::wildcard_from_ty(pat.ty);
|
||||
let mut otherwise_candidate = Candidate::new(expr_place_builder.clone(), &wildcard, false);
|
||||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
pat.span,
|
||||
false,
|
||||
&mut [&mut guard_candidate, &mut otherwise_candidate],
|
||||
);
|
||||
let mut opt_expr_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
||||
let expr_place: Place<'tcx>;
|
||||
if let Ok(expr_builder) =
|
||||
expr_place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
expr_place = expr_builder.into_place(self.tcx, self.typeck_results);
|
||||
opt_expr_place = Some((Some(&expr_place), expr_span));
|
||||
}
|
||||
self.declare_bindings(None, pat.span.to(span), pat, ArmHasGuard(false), opt_expr_place);
|
||||
let post_guard_block = self.bind_pattern(
|
||||
self.source_info(pat.span),
|
||||
guard_candidate,
|
||||
None,
|
||||
&fake_borrow_temps,
|
||||
expr.span,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
|
||||
(post_guard_block, otherwise_post_guard_block)
|
||||
}
|
||||
|
||||
/// Initializes each of the bindings from the candidate by
|
||||
/// moving/copying/ref'ing the source as appropriate. Tests the guard, if
|
||||
/// any, and then branches to the arm. Returns the block for the case where
|
||||
|
@ -1852,48 +1891,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||
(e.span, self.test_bool(block, e, source_info))
|
||||
}
|
||||
Guard::IfLet(ref pat, scrutinee) => {
|
||||
let scrutinee = &self.thir[scrutinee];
|
||||
let scrutinee_span = scrutinee.span;
|
||||
let scrutinee_place_builder =
|
||||
unpack!(block = self.lower_scrutinee(block, scrutinee, scrutinee_span));
|
||||
let mut guard_candidate =
|
||||
Candidate::new(scrutinee_place_builder.clone(), &pat, false);
|
||||
let wildcard = Pat::wildcard_from_ty(pat.ty);
|
||||
let mut otherwise_candidate =
|
||||
Candidate::new(scrutinee_place_builder.clone(), &wildcard, false);
|
||||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
pat.span,
|
||||
false,
|
||||
&mut [&mut guard_candidate, &mut otherwise_candidate],
|
||||
);
|
||||
let mut opt_scrutinee_place: Option<(Option<&Place<'tcx>>, Span)> = None;
|
||||
let scrutinee_place: Place<'tcx>;
|
||||
if let Ok(scrutinee_builder) =
|
||||
scrutinee_place_builder.try_upvars_resolved(self.tcx, self.typeck_results)
|
||||
{
|
||||
scrutinee_place =
|
||||
scrutinee_builder.into_place(self.tcx, self.typeck_results);
|
||||
opt_scrutinee_place = Some((Some(&scrutinee_place), scrutinee_span));
|
||||
}
|
||||
self.declare_bindings(
|
||||
None,
|
||||
pat.span.to(arm_span.unwrap()),
|
||||
pat,
|
||||
ArmHasGuard(false),
|
||||
opt_scrutinee_place,
|
||||
);
|
||||
let post_guard_block = self.bind_pattern(
|
||||
self.source_info(pat.span),
|
||||
guard_candidate,
|
||||
None,
|
||||
&fake_borrow_temps,
|
||||
scrutinee_span,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
let otherwise_post_guard_block = otherwise_candidate.pre_binding_block.unwrap();
|
||||
(scrutinee_span, (post_guard_block, otherwise_post_guard_block))
|
||||
let s = &self.thir[scrutinee];
|
||||
(s.span, self.lower_let(block, s, pat, arm_span.unwrap()))
|
||||
}
|
||||
};
|
||||
let source_info = self.source_info(guard_span);
|
||||
|
|
|
@ -325,6 +325,7 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
| ExprKind::Return { .. }
|
||||
| ExprKind::Yield { .. }
|
||||
| ExprKind::Loop { .. }
|
||||
| ExprKind::Let { .. }
|
||||
| ExprKind::Match { .. }
|
||||
| ExprKind::Box { .. }
|
||||
| ExprKind::If { .. }
|
||||
|
@ -475,6 +476,14 @@ impl<'a, 'tcx> Visitor<'a, 'tcx> for UnsafetyVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
}
|
||||
ExprKind::Let { expr: expr_id, .. } => {
|
||||
let let_expr = &self.thir[expr_id];
|
||||
if let ty::Adt(adt_def, _) = let_expr.ty.kind() {
|
||||
if adt_def.is_union() {
|
||||
self.requires_unsafe(expr.span, AccessToUnionField);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
visit::walk_expr(self, expr);
|
||||
|
|
|
@ -590,6 +590,9 @@ impl<'tcx> Cx<'tcx> {
|
|||
},
|
||||
Err(err) => bug!("invalid loop id for continue: {}", err),
|
||||
},
|
||||
hir::ExprKind::Let(ref pat, ref expr, _) => {
|
||||
ExprKind::Let { expr: self.mirror_expr(expr), pat: self.pattern_from_hir(pat) }
|
||||
}
|
||||
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
||||
cond: self.mirror_expr(cond),
|
||||
then: self.mirror_expr(then),
|
||||
|
|
|
@ -55,9 +55,10 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, 'tcx> {
|
|||
|
||||
fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) {
|
||||
intravisit::walk_expr(self, ex);
|
||||
|
||||
if let hir::ExprKind::Match(ref scrut, ref arms, source) = ex.kind {
|
||||
self.check_match(scrut, arms, source);
|
||||
match &ex.kind {
|
||||
hir::ExprKind::Match(scrut, arms, source) => self.check_match(scrut, arms, *source),
|
||||
hir::ExprKind::Let(pat, scrut, span) => self.check_let(pat, scrut, *span),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -117,6 +118,31 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
check_for_bindings_named_same_as_variants(self, pat);
|
||||
}
|
||||
|
||||
fn let_source(&mut self, pat: &'tcx hir::Pat<'tcx>, _expr: &hir::Expr<'_>) -> LetSource {
|
||||
let hir = self.tcx.hir();
|
||||
let parent = hir.get_parent_node(pat.hir_id);
|
||||
let parent_parent = hir.get_parent_node(parent);
|
||||
let parent_parent_node = hir.get(parent_parent);
|
||||
|
||||
let parent_parent_parent = hir.get_parent_node(parent_parent);
|
||||
let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
|
||||
let parent_parent_parent_parent_node = hir.get(parent_parent_parent_parent);
|
||||
|
||||
if let hir::Node::Expr(hir::Expr {
|
||||
kind: hir::ExprKind::Loop(_, _, hir::LoopSource::While, _),
|
||||
..
|
||||
}) = parent_parent_parent_parent_node
|
||||
{
|
||||
LetSource::WhileLet
|
||||
} else if let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::If { .. }, .. }) =
|
||||
parent_parent_node
|
||||
{
|
||||
LetSource::IfLet
|
||||
} else {
|
||||
LetSource::GenericLet
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_pattern<'p>(
|
||||
&self,
|
||||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||
|
@ -144,6 +170,14 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_let(&mut self, pat: &'tcx hir::Pat<'tcx>, expr: &hir::Expr<'_>, span: Span) {
|
||||
self.check_patterns(pat);
|
||||
let ls = self.let_source(pat, expr);
|
||||
let mut cx = self.new_cx(expr.hir_id);
|
||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
||||
check_let_reachability(&mut cx, ls, pat.hir_id, &tpat, span);
|
||||
}
|
||||
|
||||
fn check_match(
|
||||
&mut self,
|
||||
scrut: &hir::Expr<'_>,
|
||||
|
@ -158,7 +192,13 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
if let Some(hir::Guard::IfLet(ref pat, _)) = arm.guard {
|
||||
self.check_patterns(pat);
|
||||
let tpat = self.lower_pattern(&mut cx, pat, &mut false).0;
|
||||
check_if_let_guard(&mut cx, &tpat, pat.hir_id);
|
||||
check_let_reachability(
|
||||
&mut cx,
|
||||
LetSource::IfLetGuard,
|
||||
pat.hir_id,
|
||||
&tpat,
|
||||
tpat.span,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -181,8 +221,16 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
let scrut_ty = self.typeck_results.expr_ty_adjusted(scrut);
|
||||
let report = compute_match_usefulness(&cx, &arms, scrut.hir_id, scrut_ty);
|
||||
|
||||
// Report unreachable arms.
|
||||
report_arm_reachability(&cx, &report, source);
|
||||
report_arm_reachability(&cx, &report, |_, arm_span, arm_hir_id, catchall| {
|
||||
match source {
|
||||
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
|
||||
unreachable_pattern(cx.tcx, arm_span, arm_hir_id, catchall);
|
||||
}
|
||||
// Unreachable patterns in try and await expressions occur when one of
|
||||
// the arms are an uninhabited type. Which is OK.
|
||||
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
|
||||
}
|
||||
});
|
||||
|
||||
// Check if the match is exhaustive.
|
||||
// Note: An empty match isn't the same as an empty matrix for diagnostics purposes,
|
||||
|
@ -349,89 +397,99 @@ fn unreachable_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, catchall: Option<
|
|||
});
|
||||
}
|
||||
|
||||
fn irrefutable_let_pattern(tcx: TyCtxt<'_>, span: Span, id: HirId, source: hir::MatchSource) {
|
||||
tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match source {
|
||||
hir::MatchSource::IfLetDesugar { .. } => {
|
||||
let mut diag = lint.build("irrefutable `if let` pattern");
|
||||
diag.note("this pattern will always match, so the `if let` is useless");
|
||||
diag.help("consider replacing the `if let` with a `let`");
|
||||
fn irrefutable_let_pattern(id: HirId, ls: LetSource, span: Span, tcx: TyCtxt<'_>) {
|
||||
macro_rules! emit_diag {
|
||||
(
|
||||
$lint:expr,
|
||||
$source_name:expr,
|
||||
$note_sufix:expr,
|
||||
$help_sufix:expr
|
||||
) => {{
|
||||
let mut diag = $lint.build(concat!("irrefutable ", $source_name, " pattern"));
|
||||
diag.note(concat!("this pattern will always match, so the ", $note_sufix));
|
||||
diag.help(concat!("consider ", $help_sufix));
|
||||
diag.emit()
|
||||
}};
|
||||
}
|
||||
|
||||
tcx.struct_span_lint_hir(IRREFUTABLE_LET_PATTERNS, id, span, |lint| match ls {
|
||||
LetSource::GenericLet => {
|
||||
emit_diag!(lint, "`let`", "`let` is useless", "removing `let`");
|
||||
}
|
||||
hir::MatchSource::WhileLetDesugar => {
|
||||
let mut diag = lint.build("irrefutable `while let` pattern");
|
||||
diag.note("this pattern will always match, so the loop will never exit");
|
||||
diag.help("consider instead using a `loop { ... }` with a `let` inside it");
|
||||
diag.emit()
|
||||
LetSource::IfLet => {
|
||||
emit_diag!(
|
||||
lint,
|
||||
"`if let`",
|
||||
"`if let` is useless",
|
||||
"replacing the `if let` with a `let`"
|
||||
);
|
||||
}
|
||||
hir::MatchSource::IfLetGuardDesugar => {
|
||||
let mut diag = lint.build("irrefutable `if let` guard pattern");
|
||||
diag.note("this pattern will always match, so the guard is useless");
|
||||
diag.help("consider removing the guard and adding a `let` inside the match arm");
|
||||
diag.emit()
|
||||
LetSource::IfLetGuard => {
|
||||
emit_diag!(
|
||||
lint,
|
||||
"`if let` guard",
|
||||
"guard is useless",
|
||||
"removing the guard and adding a `let` inside the match arm"
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
bug!(
|
||||
"expected `if let`, `while let`, or `if let` guard HIR match source, found {:?}",
|
||||
source,
|
||||
)
|
||||
LetSource::WhileLet => {
|
||||
emit_diag!(
|
||||
lint,
|
||||
"`while let`",
|
||||
"loop will never exit",
|
||||
"instead using a `loop { ... }` with a `let` inside it"
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn check_if_let_guard<'p, 'tcx>(
|
||||
fn check_let_reachability<'p, 'tcx>(
|
||||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||
pat: &'p super::Pat<'tcx>,
|
||||
ls: LetSource,
|
||||
pat_id: HirId,
|
||||
pat: &'p super::Pat<'tcx>,
|
||||
span: Span,
|
||||
) {
|
||||
let arms = [MatchArm { pat, hir_id: pat_id, has_guard: false }];
|
||||
let report = compute_match_usefulness(&cx, &arms, pat_id, pat.ty);
|
||||
report_arm_reachability(&cx, &report, hir::MatchSource::IfLetGuardDesugar);
|
||||
|
||||
report_arm_reachability(&cx, &report, |arm_index, arm_span, arm_hir_id, _| {
|
||||
match ls {
|
||||
LetSource::IfLet | LetSource::WhileLet => {
|
||||
match arm_index {
|
||||
// The arm with the user-specified pattern.
|
||||
0 => unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None),
|
||||
// The arm with the wildcard pattern.
|
||||
1 => irrefutable_let_pattern(pat_id, ls, arm_span, cx.tcx),
|
||||
_ => bug!(),
|
||||
}
|
||||
}
|
||||
LetSource::IfLetGuard if arm_index == 0 => {
|
||||
unreachable_pattern(cx.tcx, arm_span, arm_hir_id, None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
|
||||
if report.non_exhaustiveness_witnesses.is_empty() {
|
||||
// The match is exhaustive, i.e. the `if let` pattern is irrefutable.
|
||||
irrefutable_let_pattern(cx.tcx, pat.span, pat_id, hir::MatchSource::IfLetGuardDesugar)
|
||||
irrefutable_let_pattern(pat_id, ls, span, cx.tcx);
|
||||
}
|
||||
}
|
||||
|
||||
/// Report unreachable arms, if any.
|
||||
fn report_arm_reachability<'p, 'tcx>(
|
||||
fn report_arm_reachability<'p, 'tcx, F>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
report: &UsefulnessReport<'p, 'tcx>,
|
||||
source: hir::MatchSource,
|
||||
) {
|
||||
unreachable: F,
|
||||
) where
|
||||
F: Fn(usize, Span, HirId, Option<Span>),
|
||||
{
|
||||
use Reachability::*;
|
||||
let mut catchall = None;
|
||||
for (arm_index, (arm, is_useful)) in report.arm_usefulness.iter().enumerate() {
|
||||
match is_useful {
|
||||
Unreachable => {
|
||||
match source {
|
||||
hir::MatchSource::WhileDesugar => bug!(),
|
||||
|
||||
hir::MatchSource::IfLetDesugar { .. } | hir::MatchSource::WhileLetDesugar => {
|
||||
// Check which arm we're on.
|
||||
match arm_index {
|
||||
// The arm with the user-specified pattern.
|
||||
0 => unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None),
|
||||
// The arm with the wildcard pattern.
|
||||
1 => irrefutable_let_pattern(cx.tcx, arm.pat.span, arm.hir_id, source),
|
||||
_ => bug!(),
|
||||
}
|
||||
}
|
||||
|
||||
hir::MatchSource::IfLetGuardDesugar => {
|
||||
assert_eq!(arm_index, 0);
|
||||
unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, None);
|
||||
}
|
||||
|
||||
hir::MatchSource::ForLoopDesugar | hir::MatchSource::Normal => {
|
||||
unreachable_pattern(cx.tcx, arm.pat.span, arm.hir_id, catchall);
|
||||
}
|
||||
|
||||
// Unreachable patterns in try and await expressions occur when one of
|
||||
// the arms are an uninhabited type. Which is OK.
|
||||
hir::MatchSource::AwaitDesugar | hir::MatchSource::TryDesugar => {}
|
||||
}
|
||||
}
|
||||
Unreachable => unreachable(arm_index, arm.pat.span, arm.hir_id, catchall),
|
||||
Reachable(unreachables) if unreachables.is_empty() => {}
|
||||
// The arm is reachable, but contains unreachable subpatterns (from or-patterns).
|
||||
Reachable(unreachables) => {
|
||||
|
@ -723,3 +781,11 @@ fn check_borrow_conflicts_in_at_patterns(cx: &MatchVisitor<'_, '_>, pat: &Pat<'_
|
|||
err.emit();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub enum LetSource {
|
||||
GenericLet,
|
||||
IfLet,
|
||||
IfLetGuard,
|
||||
WhileLet,
|
||||
}
|
||||
|
|
|
@ -57,6 +57,9 @@ pub fn walk_expr<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, expr: &Exp
|
|||
Use { source } => visitor.visit_expr(&visitor.thir()[source]),
|
||||
NeverToAny { source } => visitor.visit_expr(&visitor.thir()[source]),
|
||||
Pointer { source, cast: _ } => visitor.visit_expr(&visitor.thir()[source]),
|
||||
Let { expr, .. } => {
|
||||
visitor.visit_expr(&visitor.thir()[expr]);
|
||||
}
|
||||
Loop { body } => visitor.visit_expr(&visitor.thir()[body]),
|
||||
Match { scrutinee, ref arms } => {
|
||||
visitor.visit_expr(&visitor.thir()[scrutinee]);
|
||||
|
|
|
@ -1867,7 +1867,7 @@ impl<'a> Parser<'a> {
|
|||
})?;
|
||||
let span = lo.to(expr.span);
|
||||
self.sess.gated_spans.gate(sym::let_chains, span);
|
||||
Ok(self.mk_expr(span, ExprKind::Let(pat, expr), attrs))
|
||||
Ok(self.mk_expr(span, ExprKind::Let(pat, expr, span), attrs))
|
||||
}
|
||||
|
||||
/// Parses an `else { ... }` expression (`else` token already eaten).
|
||||
|
|
|
@ -48,11 +48,8 @@ impl NonConstExpr {
|
|||
|
||||
Self::Match(TryDesugar) => &[sym::const_try],
|
||||
|
||||
Self::Match(IfLetGuardDesugar) => bug!("`if let` guard outside a `match` expression"),
|
||||
|
||||
// All other expressions are allowed.
|
||||
Self::Loop(Loop | While | WhileLet)
|
||||
| Self::Match(WhileDesugar | WhileLetDesugar | Normal | IfLetDesugar { .. }) => &[],
|
||||
Self::Loop(Loop | While) | Self::Match(Normal) => &[],
|
||||
};
|
||||
|
||||
Some(gates)
|
||||
|
@ -277,9 +274,7 @@ impl<'tcx> Visitor<'tcx> for CheckConstVisitor<'tcx> {
|
|||
hir::ExprKind::Match(_, _, source) => {
|
||||
let non_const_expr = match source {
|
||||
// These are handled by `ExprKind::Loop` above.
|
||||
hir::MatchSource::WhileDesugar
|
||||
| hir::MatchSource::WhileLetDesugar
|
||||
| hir::MatchSource::ForLoopDesugar => None,
|
||||
hir::MatchSource::ForLoopDesugar => None,
|
||||
|
||||
_ => Some(NonConstExpr::Match(*source)),
|
||||
};
|
||||
|
|
|
@ -429,6 +429,11 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
|
|||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
hir::ExprKind::Let(ref pat, ..) => {
|
||||
self.add_from_pat(pat);
|
||||
intravisit::walk_expr(self, expr);
|
||||
}
|
||||
|
||||
// live nodes required for interesting control flow:
|
||||
hir::ExprKind::If(..) | hir::ExprKind::Match(..) | hir::ExprKind::Loop(..) => {
|
||||
self.add_live_node_for_node(expr.hir_id, ExprNode(expr.span));
|
||||
|
@ -852,6 +857,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
|||
})
|
||||
}
|
||||
|
||||
hir::ExprKind::Let(ref pat, ref scrutinee, _) => {
|
||||
let succ = self.propagate_through_expr(scrutinee, succ);
|
||||
self.define_bindings_in_pat(pat, succ)
|
||||
}
|
||||
|
||||
// Note that labels have been resolved, so we don't need to look
|
||||
// at the label ident
|
||||
hir::ExprKind::Loop(ref blk, ..) => self.propagate_through_loop(expr, &blk, succ),
|
||||
|
@ -1303,6 +1313,7 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
|
|||
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
check_expr(self, ex);
|
||||
intravisit::walk_expr(self, ex);
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
||||
|
@ -1358,6 +1369,10 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::Let(ref pat, ..) => {
|
||||
this.check_unused_vars_in_pat(pat, None, |_, _, _, _| {});
|
||||
}
|
||||
|
||||
// no correctness conditions related to liveness
|
||||
hir::ExprKind::Call(..)
|
||||
| hir::ExprKind::MethodCall(..)
|
||||
|
@ -1388,8 +1403,6 @@ fn check_expr<'tcx>(this: &mut Liveness<'_, 'tcx>, expr: &'tcx Expr<'tcx>) {
|
|||
| hir::ExprKind::Type(..)
|
||||
| hir::ExprKind::Err => {}
|
||||
}
|
||||
|
||||
intravisit::walk_expr(this, expr);
|
||||
}
|
||||
|
||||
impl<'tcx> Liveness<'_, 'tcx> {
|
||||
|
|
|
@ -221,6 +221,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
|
|||
| ExprKind::Index(..)
|
||||
| ExprKind::Path(..)
|
||||
| ExprKind::AddrOf(..)
|
||||
| ExprKind::Let(..)
|
||||
| ExprKind::Break(..)
|
||||
| ExprKind::Continue(..)
|
||||
| ExprKind::Ret(..)
|
||||
|
|
|
@ -2309,7 +2309,7 @@ impl<'a: 'ast, 'b, 'ast> LateResolutionVisitor<'a, 'b, 'ast> {
|
|||
self.resolve_expr(e, Some(&expr));
|
||||
}
|
||||
|
||||
ExprKind::Let(ref pat, ref scrutinee) => {
|
||||
ExprKind::Let(ref pat, ref scrutinee, _) => {
|
||||
self.visit_expr(scrutinee);
|
||||
self.resolve_pattern_top(pat, PatternSource::Let);
|
||||
}
|
||||
|
|
|
@ -24,27 +24,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
) -> Ty<'tcx> {
|
||||
let tcx = self.tcx;
|
||||
|
||||
use hir::MatchSource::*;
|
||||
let (source_if, if_no_else, force_scrutinee_bool) = match match_src {
|
||||
IfLetDesugar { contains_else_clause, .. } => (true, !contains_else_clause, false),
|
||||
WhileDesugar => (false, false, true),
|
||||
_ => (false, false, false),
|
||||
};
|
||||
|
||||
// Type check the discriminant and get its type.
|
||||
let scrutinee_ty = if force_scrutinee_bool {
|
||||
// Here we want to ensure:
|
||||
//
|
||||
// 1. That default match bindings are *not* accepted in the condition of an
|
||||
// `if` expression. E.g. given `fn foo() -> &bool;` we reject `if foo() { .. }`.
|
||||
//
|
||||
// 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`.
|
||||
//
|
||||
// FIXME(60707): Consider removing hack with principled solution.
|
||||
self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {})
|
||||
} else {
|
||||
self.demand_scrutinee_type(scrut, arms_contain_ref_bindings(arms), arms.is_empty())
|
||||
};
|
||||
let acrb = arms_contain_ref_bindings(arms);
|
||||
let scrutinee_ty = self.demand_scrutinee_type(scrut, acrb, arms.is_empty());
|
||||
|
||||
// If there are no arms, that is a diverging match; a special case.
|
||||
if arms.is_empty() {
|
||||
|
@ -52,7 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
return tcx.types.never;
|
||||
}
|
||||
|
||||
self.warn_arms_when_scrutinee_diverges(arms, match_src);
|
||||
self.warn_arms_when_scrutinee_diverges(arms);
|
||||
|
||||
// Otherwise, we have to union together the types that the arms produce and so forth.
|
||||
let scrut_diverges = self.diverges.replace(Diverges::Maybe);
|
||||
|
@ -112,128 +93,95 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
let arm_ty = if source_if
|
||||
&& if_no_else
|
||||
&& i != 0
|
||||
&& self.if_fallback_coercion(
|
||||
expr.span,
|
||||
&arms[0].body,
|
||||
&mut coercion,
|
||||
|hir_id, span| self.coercion_reason_match(hir_id, span),
|
||||
) {
|
||||
tcx.ty_error()
|
||||
} else {
|
||||
// Only call this if this is not an `if` expr with an expected type and no `else`
|
||||
// clause to avoid duplicated type errors. (#60254)
|
||||
self.check_expr_with_expectation(&arm.body, expected)
|
||||
};
|
||||
|
||||
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
|
||||
all_arms_diverge &= self.diverges.get();
|
||||
|
||||
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;
|
||||
match (i, if_no_else) {
|
||||
(0, _) => coercion.coerce(self, &self.misc(expr.span), &arm.body, arm_ty),
|
||||
(_, true) => {} // Handled above to avoid duplicated type errors (#60254).
|
||||
(_, _) => {
|
||||
let then_ty = prior_arm_ty.unwrap();
|
||||
let cause = self.if_cause(
|
||||
expr.span,
|
||||
then_expr,
|
||||
&arm.body,
|
||||
then_ty,
|
||||
arm_ty,
|
||||
opt_suggest_box_span,
|
||||
);
|
||||
coercion.coerce(self, &cause, &arm.body, arm_ty);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let (arm_span, semi_span) =
|
||||
self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
|
||||
let (span, code) = match i {
|
||||
// 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)),
|
||||
_ => (
|
||||
expr.span,
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
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,
|
||||
}),
|
||||
),
|
||||
};
|
||||
let cause = self.cause(span, code);
|
||||
|
||||
// This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`.
|
||||
// We use it this way to be able to expand on the potential error and detect when a
|
||||
// `match` tail statement could be a tail expression instead. If so, we suggest
|
||||
// removing the stray semicolon.
|
||||
coercion.coerce_inner(
|
||||
self,
|
||||
&cause,
|
||||
Some(&arm.body),
|
||||
arm_ty,
|
||||
Some(&mut |err: &mut DiagnosticBuilder<'_>| {
|
||||
let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
|
||||
Some(ret_coercion) if self.in_tail_expr => {
|
||||
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))
|
||||
// The match arms need to unify for the case of `impl Trait`.
|
||||
&& !matches!(ret_ty.kind(), ty::Opaque(..))
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if let (Expectation::IsLast(stmt), Some(ret), true) =
|
||||
(orig_expected, self.ret_type_span, can_coerce_to_return_ty)
|
||||
{
|
||||
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
|
||||
let mut ret_span: MultiSpan = semi_span.into();
|
||||
ret_span.push_span_label(
|
||||
expr.span,
|
||||
"this could be implicitly returned but it is a statement, not a \
|
||||
tail expression"
|
||||
.to_owned(),
|
||||
);
|
||||
ret_span.push_span_label(
|
||||
ret,
|
||||
"the `match` arms can conform to this return type".to_owned(),
|
||||
);
|
||||
ret_span.push_span_label(
|
||||
semi_span,
|
||||
"the `match` is a statement because of this semicolon, consider \
|
||||
removing it"
|
||||
.to_owned(),
|
||||
);
|
||||
err.span_note(
|
||||
ret_span,
|
||||
"you might have meant to return the `match` expression",
|
||||
);
|
||||
err.tool_only_span_suggestion(
|
||||
semi_span,
|
||||
"remove this semicolon",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
let (arm_span, semi_span) =
|
||||
self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
|
||||
let (span, code) = match i {
|
||||
// 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)),
|
||||
_ => (
|
||||
expr.span,
|
||||
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
|
||||
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,
|
||||
}),
|
||||
false,
|
||||
);
|
||||
),
|
||||
};
|
||||
let cause = self.cause(span, code);
|
||||
|
||||
other_arms.push(arm_span);
|
||||
if other_arms.len() > 5 {
|
||||
other_arms.remove(0);
|
||||
}
|
||||
// This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`.
|
||||
// We use it this way to be able to expand on the potential error and detect when a
|
||||
// `match` tail statement could be a tail expression instead. If so, we suggest
|
||||
// removing the stray semicolon.
|
||||
coercion.coerce_inner(
|
||||
self,
|
||||
&cause,
|
||||
Some(&arm.body),
|
||||
arm_ty,
|
||||
Some(&mut |err: &mut DiagnosticBuilder<'_>| {
|
||||
let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
|
||||
Some(ret_coercion) if self.in_tail_expr => {
|
||||
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))
|
||||
// The match arms need to unify for the case of `impl Trait`.
|
||||
&& !matches!(ret_ty.kind(), ty::Opaque(..))
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if let (Expectation::IsLast(stmt), Some(ret), true) =
|
||||
(orig_expected, self.ret_type_span, can_coerce_to_return_ty)
|
||||
{
|
||||
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
|
||||
let mut ret_span: MultiSpan = semi_span.into();
|
||||
ret_span.push_span_label(
|
||||
expr.span,
|
||||
"this could be implicitly returned but it is a statement, not a \
|
||||
tail expression"
|
||||
.to_owned(),
|
||||
);
|
||||
ret_span.push_span_label(
|
||||
ret,
|
||||
"the `match` arms can conform to this return type".to_owned(),
|
||||
);
|
||||
ret_span.push_span_label(
|
||||
semi_span,
|
||||
"the `match` is a statement because of this semicolon, consider \
|
||||
removing it"
|
||||
.to_owned(),
|
||||
);
|
||||
err.span_note(
|
||||
ret_span,
|
||||
"you might have meant to return the `match` expression",
|
||||
);
|
||||
err.tool_only_span_suggestion(
|
||||
semi_span,
|
||||
"remove this semicolon",
|
||||
String::new(),
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}),
|
||||
false,
|
||||
);
|
||||
|
||||
other_arms.push(arm_span);
|
||||
if other_arms.len() > 5 {
|
||||
other_arms.remove(0);
|
||||
}
|
||||
prior_arm_ty = Some(arm_ty);
|
||||
}
|
||||
|
@ -283,39 +231,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
/// 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>],
|
||||
source: hir::MatchSource,
|
||||
) {
|
||||
use hir::MatchSource::*;
|
||||
let msg = match source {
|
||||
IfLetDesugar { .. } => "block in `if` expression",
|
||||
WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression",
|
||||
_ => "arm",
|
||||
};
|
||||
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {
|
||||
for arm in arms {
|
||||
self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg);
|
||||
self.warn_if_unreachable(arm.body.hir_id, arm.body.span, "arm");
|
||||
}
|
||||
}
|
||||
|
||||
/// 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.
|
||||
pub(crate) fn if_fallback_coercion<F, T>(
|
||||
pub(super) fn if_fallback_coercion<T>(
|
||||
&self,
|
||||
span: Span,
|
||||
then_expr: &'tcx hir::Expr<'tcx>,
|
||||
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 = ret_reason(then_expr.hir_id, span);
|
||||
let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span);
|
||||
let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
|
||||
let mut error = false;
|
||||
coercion.coerce_forced_unit(
|
||||
|
@ -338,55 +274,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
error
|
||||
}
|
||||
|
||||
pub(crate) fn coercion_reason_if(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
) -> Option<(Span, String)> {
|
||||
self.coercion_reason_inner(hir_id, span, 1)
|
||||
}
|
||||
|
||||
pub(crate) fn coercion_reason_match(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
) -> Option<(Span, String)> {
|
||||
self.coercion_reason_inner(hir_id, span, 2)
|
||||
}
|
||||
|
||||
fn coercion_reason_inner(
|
||||
&self,
|
||||
hir_id: hir::HirId,
|
||||
span: Span,
|
||||
parent_index: usize,
|
||||
) -> Option<(Span, String)> {
|
||||
let hir = self.tcx.hir();
|
||||
let mut parent_iter = hir.parent_iter(hir_id);
|
||||
let (_, node) = parent_iter.nth(parent_index)?;
|
||||
match node {
|
||||
hir::Node::Block(block) => {
|
||||
let expr = block.expr?;
|
||||
// check that the body's parent is an fn
|
||||
let (_, parent) = parent_iter.nth(1)?;
|
||||
if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) = parent {
|
||||
// check that the `if` expr without `else` is the fn body's expr
|
||||
if expr.span == span {
|
||||
let (fn_decl, _) = self.get_fn_decl(hir_id)?;
|
||||
fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> {
|
||||
let node = {
|
||||
let rslt = self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(hir_id));
|
||||
self.tcx.hir().get(rslt)
|
||||
};
|
||||
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()?;
|
||||
return Some((
|
||||
span,
|
||||
format!("expected `{}` because of this return type", snippet),
|
||||
));
|
||||
}
|
||||
Some((span, format!("expected `{}` because of this return type", snippet)))
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) => {
|
||||
Some((pat.span, "expected because of this assignment".to_string()))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node {
|
||||
return Some((pat.span, "expected because of this assignment".to_string()));
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub(crate) fn if_cause(
|
||||
|
@ -492,7 +407,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
)
|
||||
}
|
||||
|
||||
fn demand_scrutinee_type(
|
||||
pub(super) fn demand_scrutinee_type(
|
||||
&self,
|
||||
scrut: &'tcx hir::Expr<'tcx>,
|
||||
contains_ref_bindings: Option<hir::Mutability>,
|
||||
|
|
|
@ -187,7 +187,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
|
||||
// Warn for non-block expressions with diverging children.
|
||||
match expr.kind {
|
||||
ExprKind::Block(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {}
|
||||
ExprKind::Block(..)
|
||||
| ExprKind::If(..)
|
||||
| ExprKind::Let(..)
|
||||
| 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.
|
||||
|
@ -262,6 +266,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr),
|
||||
ExprKind::Let(pat, let_expr, _) => self.check_expr_let(let_expr, pat),
|
||||
ExprKind::Loop(body, _, source, _) => {
|
||||
self.check_expr_loop(body, source, expected, expr)
|
||||
}
|
||||
|
@ -802,7 +807,11 @@ impl<'a, 'tcx> FnCtxt<'a, '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");
|
||||
self.warn_if_unreachable(
|
||||
cond_expr.hir_id,
|
||||
then_expr.span,
|
||||
"block in `if` or `while` expression",
|
||||
);
|
||||
|
||||
let cond_diverges = self.diverges.get();
|
||||
self.diverges.set(Diverges::Maybe);
|
||||
|
@ -837,9 +846,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// 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.coercion_reason_if(hir_id, span)
|
||||
});
|
||||
self.if_fallback_coercion(sp, then_expr, &mut coerce);
|
||||
|
||||
// If the condition is false we can't diverge.
|
||||
self.diverges.set(cond_diverges);
|
||||
|
@ -875,26 +882,16 @@ 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.
|
||||
let mut span_err = || {
|
||||
// Likely `if let` intended.
|
||||
let hir = self.tcx.hir();
|
||||
if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) =
|
||||
hir.get(hir.get_parent_node(hir.get_parent_node(expr.hir_id)))
|
||||
{
|
||||
err.span_suggestion_verbose(
|
||||
expr.span.shrink_to_lo(),
|
||||
"you might have meant to use pattern matching",
|
||||
"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 {
|
||||
|
@ -929,6 +926,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn check_expr_let(&self, expr: &'tcx hir::Expr<'tcx>, pat: &'tcx hir::Pat<'tcx>) -> Ty<'tcx> {
|
||||
self.warn_if_unreachable(expr.hir_id, expr.span, "block in `let` expression");
|
||||
let expr_ty = self.demand_scrutinee_type(expr, pat.contains_explicit_ref_binding(), false);
|
||||
self.check_pat_top(pat, expr_ty, Some(expr.span), true);
|
||||
self.tcx.types.bool
|
||||
}
|
||||
|
||||
fn check_expr_loop(
|
||||
&self,
|
||||
body: &'tcx hir::Block<'tcx>,
|
||||
|
@ -943,7 +947,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
Some(CoerceMany::new(coerce_to))
|
||||
}
|
||||
|
||||
hir::LoopSource::While | hir::LoopSource::WhileLet | hir::LoopSource::ForLoop => None,
|
||||
hir::LoopSource::While | hir::LoopSource::ForLoop => None,
|
||||
};
|
||||
|
||||
let ctxt = BreakableCtxt {
|
||||
|
|
|
@ -229,6 +229,10 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
hir::ExprKind::Let(ref pat, ref expr, _) => {
|
||||
self.walk_local(expr, pat, |t| t.borrow_expr(&expr, ty::ImmBorrow));
|
||||
}
|
||||
|
||||
hir::ExprKind::Match(ref discr, arms, _) => {
|
||||
let discr_place = return_if_err!(self.mc.cat_expr(&discr));
|
||||
|
||||
|
@ -428,10 +432,12 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
|
||||
fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(ref local) => {
|
||||
self.walk_local(&local);
|
||||
hir::StmtKind::Local(hir::Local { pat, init: Some(ref expr), .. }) => {
|
||||
self.walk_local(expr, pat, |_| {});
|
||||
}
|
||||
|
||||
hir::StmtKind::Local(_) => {}
|
||||
|
||||
hir::StmtKind::Item(_) => {
|
||||
// We don't visit nested items in this visitor,
|
||||
// only the fn body we were given.
|
||||
|
@ -443,16 +449,14 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
fn walk_local(&mut self, local: &hir::Local<'_>) {
|
||||
if let Some(ref expr) = local.init {
|
||||
// Variable declarations with
|
||||
// initializers are considered
|
||||
// "assigns", which is handled by
|
||||
// `walk_pat`:
|
||||
self.walk_expr(&expr);
|
||||
let init_place = return_if_err!(self.mc.cat_expr(&expr));
|
||||
self.walk_irrefutable_pat(&init_place, &local.pat);
|
||||
}
|
||||
fn walk_local<F>(&mut self, expr: &hir::Expr<'_>, pat: &hir::Pat<'_>, mut f: F)
|
||||
where
|
||||
F: FnMut(&mut Self),
|
||||
{
|
||||
self.walk_expr(&expr);
|
||||
let expr_place = return_if_err!(self.mc.cat_expr(&expr));
|
||||
f(self);
|
||||
self.walk_irrefutable_pat(&expr_place, &pat);
|
||||
}
|
||||
|
||||
/// Indicates that the value of `blk` will be consumed, meaning either copied or moved
|
||||
|
|
|
@ -368,6 +368,7 @@ impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> {
|
|||
| hir::ExprKind::Tup(..)
|
||||
| hir::ExprKind::Binary(..)
|
||||
| hir::ExprKind::Block(..)
|
||||
| hir::ExprKind::Let(..)
|
||||
| hir::ExprKind::Loop(..)
|
||||
| hir::ExprKind::Match(..)
|
||||
| hir::ExprKind::Lit(..)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue