diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs
index bd6e4c30fc34c..575a00cdd0e43 100644
--- a/compiler/rustc_ast/src/ast.rs
+++ b/compiler/rustc_ast/src/ast.rs
@@ -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 }`
diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs
index 87950b44083ef..c824583118722 100644
--- a/compiler/rustc_ast/src/mut_visit.rs
+++ b/compiler/rustc_ast/src/mut_visit.rs
@@ -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);
}
diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs
index 1ebfcf367110f..a377763983a4b 100644
--- a/compiler/rustc_ast/src/visit.rs
+++ b/compiler/rustc_ast/src/visit.rs
@@ -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);
diff --git a/compiler/rustc_ast_lowering/src/expr.rs b/compiler/rustc_ast_lowering/src/expr.rs
index bacf5662bc005..bf7589e84adc4 100644
--- a/compiler/rustc_ast_lowering/src/expr.rs
+++ b/compiler/rustc_ast_lowering/src/expr.rs
@@ -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,115 +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 then = self.arena.alloc(self.lower_block_expr(then));
- let els = else_opt.map(|els| self.lower_expr(els));
- hir::ExprKind::If(cond, then, els)
- }
-
- 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 lowered_cond = self.lower_expr(cond);
+ let new_cond = self.manage_let_cond(lowered_cond);
let then_expr = self.lower_block_expr(then);
- let then_arm = self.arm(then_pat, self.arena.alloc(then_expr));
+ if let Some(rslt) = else_opt {
+ hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), Some(self.lower_expr(rslt)))
+ } else {
+ hir::ExprKind::If(new_cond, self.arena.alloc(then_expr), None)
+ }
+ }
- 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,
@@ -484,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>) }`,
@@ -609,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))
@@ -1457,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)
};
@@ -1670,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
@@ -1710,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> {
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 581f177ad14f6..d41b3b8146624 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -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(),
diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs
index 0bdc4258bfbc3..cff1b70dda907 100644
--- a/compiler/rustc_ast_passes/src/ast_validation.rs
+++ b/compiler/rustc_ast_passes/src/ast_validation.rs
@@ -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);
diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs
index 763c1b12bf883..b09c668273aa8 100644
--- a/compiler/rustc_ast_pretty/src/pprust/state.rs
+++ b/compiler/rustc_ast_pretty/src/pprust/state.rs
@@ -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) => {
diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs
index b454737fb8077..3ed99eab95c62 100644
--- a/compiler/rustc_expand/src/base.rs
+++ b/compiler/rustc_expand/src/base.rs
@@ -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
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index 3879d6052922a..4e233ed14577d 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -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",
}
}
diff --git a/compiler/rustc_hir/src/intravisit.rs b/compiler/rustc_hir/src/intravisit.rs
index ae186d66004d7..1cbe3b12c8203 100644
--- a/compiler/rustc_hir/src/intravisit.rs
+++ b/compiler/rustc_hir/src/intravisit.rs
@@ -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);
diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs
index 9ad40db73ceea..9c286ad5ccfe9 100644
--- a/compiler/rustc_hir_pretty/src/lib.rs
+++ b/compiler/rustc_hir_pretty/src/lib.rs
@@ -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,37 +1142,52 @@ 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;
- if needs_par {
- self.popen();
- }
- self.print_expr(expr);
- if needs_par {
- self.pclose();
- }
+ fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) {
+ self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
}
- /// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in
+ /// 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<'_>) {
- 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,
-
- _ => contains_exterior_struct_lit(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 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),
+ }
+ }
+
fn print_expr_vec(&mut self, exprs: &[hir::Expr<'_>]) {
self.ibox(INDENT_UNIT);
self.s.word("[");
@@ -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));
}
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
index 50048534aaee5..b628381d5aadc 100644
--- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs
+++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs
@@ -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",
diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs
index 2d2897f3f9899..f59ad0ac82d80 100644
--- a/compiler/rustc_lint/src/unused.rs
+++ b/compiler/rustc_lint/src/unused.rs
@@ -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,
diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs
index cdefc9effa1e9..48337b8cb9617 100644
--- a/compiler/rustc_middle/src/thir.rs
+++ b/compiler/rustc_middle/src/thir.rs
@@ -292,6 +292,10 @@ pub enum ExprKind<'tcx> {
Loop {
body: ExprId,
},
+ Let {
+ expr: ExprId,
+ pat: Pat<'tcx>,
+ },
/// A `match` expression.
Match {
scrutinee: ExprId,
diff --git a/compiler/rustc_mir_build/src/build/expr/as_place.rs b/compiler/rustc_mir_build/src/build/expr/as_place.rs
index bedb8b1c58b82..4a546a5021577 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_place.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_place.rs
@@ -565,6 +565,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::If { .. }
| ExprKind::Loop { .. }
| ExprKind::Block { .. }
+ | ExprKind::Let { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::Break { .. }
diff --git a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
index 69786c14ee8dd..1719f96f26d8a 100644
--- a/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
+++ b/compiler/rustc_mir_build/src/build/expr/as_rvalue.rs
@@ -284,6 +284,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::LogicalOp { .. }
| ExprKind::Call { .. }
| ExprKind::Field { .. }
+ | ExprKind::Let { .. }
| ExprKind::Deref { .. }
| ExprKind::Index { .. }
| ExprKind::VarRef { .. }
diff --git a/compiler/rustc_mir_build/src/build/expr/category.rs b/compiler/rustc_mir_build/src/build/expr/category.rs
index c834ce6ce68fd..fcda52e558126 100644
--- a/compiler/rustc_mir_build/src/build/expr/category.rs
+++ b/compiler/rustc_mir_build/src/build/expr/category.rs
@@ -46,6 +46,7 @@ impl Category {
ExprKind::LogicalOp { .. }
| ExprKind::Match { .. }
| ExprKind::If { .. }
+ | ExprKind::Let { .. }
| ExprKind::NeverToAny { .. }
| ExprKind::Use { .. }
| ExprKind::Adt { .. }
diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs
index d7b3a85c15d8f..3878cf4db99c9 100644
--- a/compiler/rustc_mir_build/src/build/expr/into.rs
+++ b/compiler/rustc_mir_build/src/build/expr/into.rs
@@ -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 =
diff --git a/compiler/rustc_mir_build/src/build/matches/mod.rs b/compiler/rustc_mir_build/src/build/matches/mod.rs
index 8164529dd1ff7..f2a8109eb0507 100644
--- a/compiler/rustc_mir_build/src/build/matches/mod.rs
+++ b/compiler/rustc_mir_build/src/build/matches/mod.rs
@@ -35,6 +35,46 @@ use std::convert::TryFrom;
use std::mem;
impl<'a, 'tcx> Builder<'a, 'tcx> {
+ pub(crate) fn then_else_blocks(
+ &mut self,
+ mut block: BasicBlock,
+ expr: &Expr<'tcx>,
+ scope: region::Scope,
+ source_info: SourceInfo,
+ ) -> (BasicBlock, BasicBlock) {
+ let this = self;
+ let expr_span = expr.span;
+
+ match expr.kind {
+ ExprKind::Scope { region_scope, lint_level, value } => {
+ let region_scope = (region_scope, source_info);
+ let then_block;
+ let else_block = unpack!(
+ then_block = this.in_scope(region_scope, lint_level, |this| {
+ let (then_block, else_block) =
+ this.then_else_blocks(block, &this.thir[value], scope, source_info);
+ then_block.and(else_block)
+ })
+ );
+ (then_block, else_block)
+ }
+ ExprKind::Let { expr, ref pat } => {
+ // FIXME: Use correct span.
+ this.lower_let(block, &this.thir[expr], pat, expr_span)
+ }
+ _ => {
+ 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.tcx, operand, then_block, else_block);
+ this.cfg.terminate(block, source_info, term);
+ (then_block, else_block)
+ }
+ }
+ }
+
/// Generates MIR for a `match` expression.
///
/// The MIR that we generate for a match looks like this.
@@ -1658,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
@@ -1811,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);
diff --git a/compiler/rustc_mir_build/src/check_unsafety.rs b/compiler/rustc_mir_build/src/check_unsafety.rs
index 42e4fc3839e09..05a5fcef16ae5 100644
--- a/compiler/rustc_mir_build/src/check_unsafety.rs
+++ b/compiler/rustc_mir_build/src/check_unsafety.rs
@@ -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);
diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs
index c3908ddd4fbe8..2b0aa41a4bd0d 100644
--- a/compiler/rustc_mir_build/src/thir/cx/expr.rs
+++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs
@@ -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),
diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
index 57ea6c86e7a1c..50cbe0f71f552 100644
--- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
+++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs
@@ -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,
+}
diff --git a/compiler/rustc_mir_build/src/thir/visit.rs b/compiler/rustc_mir_build/src/thir/visit.rs
index ce5d4362c086c..dda6c74d4deca 100644
--- a/compiler/rustc_mir_build/src/thir/visit.rs
+++ b/compiler/rustc_mir_build/src/thir/visit.rs
@@ -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]);
diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs
index 824f2316e5850..6259eff5a1a0f 100644
--- a/compiler/rustc_parse/src/parser/expr.rs
+++ b/compiler/rustc_parse/src/parser/expr.rs
@@ -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).
diff --git a/compiler/rustc_passes/src/check_const.rs b/compiler/rustc_passes/src/check_const.rs
index f6a93f5e02d5f..4a82252a32b13 100644
--- a/compiler/rustc_passes/src/check_const.rs
+++ b/compiler/rustc_passes/src/check_const.rs
@@ -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)),
};
diff --git a/compiler/rustc_passes/src/liveness.rs b/compiler/rustc_passes/src/liveness.rs
index f2c2521ab43e4..2cd780e1b9bbe 100644
--- a/compiler/rustc_passes/src/liveness.rs
+++ b/compiler/rustc_passes/src/liveness.rs
@@ -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> {
diff --git a/compiler/rustc_passes/src/naked_functions.rs b/compiler/rustc_passes/src/naked_functions.rs
index 899503cc556e5..d3ecd18a93c5a 100644
--- a/compiler/rustc_passes/src/naked_functions.rs
+++ b/compiler/rustc_passes/src/naked_functions.rs
@@ -221,6 +221,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
| ExprKind::Index(..)
| ExprKind::Path(..)
| ExprKind::AddrOf(..)
+ | ExprKind::Let(..)
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)
diff --git a/compiler/rustc_passes/src/region.rs b/compiler/rustc_passes/src/region.rs
index c133f1a041719..7403e51c7341b 100644
--- a/compiler/rustc_passes/src/region.rs
+++ b/compiler/rustc_passes/src/region.rs
@@ -233,14 +233,12 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
terminating(r.hir_id.local_id);
}
- hir::ExprKind::If(ref expr, ref then, Some(ref otherwise)) => {
- terminating(expr.hir_id.local_id);
+ hir::ExprKind::If(_, ref then, Some(ref otherwise)) => {
terminating(then.hir_id.local_id);
terminating(otherwise.hir_id.local_id);
}
- hir::ExprKind::If(ref expr, ref then, None) => {
- terminating(expr.hir_id.local_id);
+ hir::ExprKind::If(_, ref then, None) => {
terminating(then.hir_id.local_id);
}
@@ -392,6 +390,24 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
}
}
+ hir::ExprKind::If(ref cond, ref then, Some(ref otherwise)) => {
+ // FIXME(matthewjasper): ideally the scope we use here would only
+ // contain the condition and then expression. This works, but
+ // can result in some extra drop flags.
+ visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_expr(cond);
+ visitor.cx.var_parent = prev_cx.var_parent;
+ visitor.visit_expr(then);
+ visitor.visit_expr(otherwise);
+ }
+
+ hir::ExprKind::If(ref cond, ref then, None) => {
+ visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_expr(cond);
+ visitor.cx.var_parent = prev_cx.var_parent;
+ visitor.visit_expr(then);
+ }
+
_ => intravisit::walk_expr(visitor, expr),
}
diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs
index a21d8197bdbb3..00d291946df68 100644
--- a/compiler/rustc_resolve/src/late.rs
+++ b/compiler/rustc_resolve/src/late.rs
@@ -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);
}
diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs
index 4f8c8ca899f90..846c5fc09f2cf 100644
--- a/compiler/rustc_typeck/src/check/_match.rs
+++ b/compiler/rustc_typeck/src/check/_match.rs
@@ -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(
+ 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,
- then_expr,
- &arm.body,
- then_ty,
- arm_ty,
- opt_suggest_box_span,
+ "this could be implicitly returned but it is a statement, not a \
+ tail expression"
+ .to_owned(),
);
- 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,
+ ret_span.push_span_label(
+ ret,
+ "the `match` arms can conform to this return type".to_owned(),
+ );
+ ret_span.push_span_label(
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,
- );
- }
- }),
- false,
- );
+ "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);
- }
+ 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>,
diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs
index a536285651102..23ce4275d4009 100644
--- a/compiler/rustc_typeck/src/check/expr.rs
+++ b/compiler/rustc_typeck/src/check/expr.rs
@@ -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 {
diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs
index 1d7852d964c1d..3b241317aa3ef 100644
--- a/compiler/rustc_typeck/src/expr_use_visitor.rs
+++ b/compiler/rustc_typeck/src/expr_use_visitor.rs
@@ -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
diff --git a/compiler/rustc_typeck/src/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs
index 14af11097cf8b..f876d0f251367 100644
--- a/compiler/rustc_typeck/src/mem_categorization.rs
+++ b/compiler/rustc_typeck/src/mem_categorization.rs
@@ -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(..)
diff --git a/src/test/incremental/hashes/if_expressions.rs b/src/test/incremental/hashes/if_expressions.rs
index ca8daae152cdd..ddae9c9f03256 100644
--- a/src/test/incremental/hashes/if_expressions.rs
+++ b/src/test/incremental/hashes/if_expressions.rs
@@ -191,7 +191,7 @@ pub fn add_else_branch_if_let(x: Option<u32>) -> u32 {
}
#[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")]
#[rustc_clean(cfg="cfail3")]
pub fn add_else_branch_if_let(x: Option<u32>) -> u32 {
let mut ret = 1;
diff --git a/src/test/incremental/hashes/while_let_loops.rs b/src/test/incremental/hashes/while_let_loops.rs
index 290f1b66a7369..d385974893ca3 100644
--- a/src/test/incremental/hashes/while_let_loops.rs
+++ b/src/test/incremental/hashes/while_let_loops.rs
@@ -141,7 +141,7 @@ pub fn change_break_label() {
}
#[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")]
#[rustc_clean(cfg="cfail3")]
pub fn change_break_label() {
let mut _x = 0;
@@ -153,8 +153,6 @@ pub fn change_break_label() {
}
}
-
-
// Add loop label to continue
#[cfg(cfail1)]
pub fn add_loop_label_to_continue() {
@@ -191,7 +189,7 @@ pub fn change_continue_label() {
}
#[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")]
#[rustc_clean(cfg="cfail3")]
pub fn change_continue_label() {
let mut _x = 0;
diff --git a/src/test/incremental/hashes/while_loops.rs b/src/test/incremental/hashes/while_loops.rs
index 1049dabacf2e1..299622e45c67b 100644
--- a/src/test/incremental/hashes/while_loops.rs
+++ b/src/test/incremental/hashes/while_loops.rs
@@ -141,7 +141,7 @@ pub fn change_break_label() {
}
#[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="hir_owner_nodes, optimized_mir")]
+#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,optimized_mir,typeck")]
#[rustc_clean(cfg="cfail3")]
pub fn change_break_label() {
let mut _x = 0;
@@ -191,7 +191,7 @@ pub fn change_continue_label() {
}
#[cfg(not(cfail1))]
-#[rustc_clean(cfg="cfail2", except="hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2", except="hir_owner_nodes,typeck")]
#[rustc_clean(cfg="cfail3")]
pub fn change_continue_label() {
let mut _x = 0;
diff --git a/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff b/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff
index 3a50ed224b552..a7e4a131bfb72 100644
--- a/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff
+++ b/src/test/mir-opt/funky_arms.float_to_exponential_common.ConstProp.diff
@@ -94,35 +94,34 @@
bb7: {
StorageLive(_10); // scope 2 at $DIR/funky_arms.rs:24:17: 24:26
_10 = ((_7 as Some).0: usize); // scope 2 at $DIR/funky_arms.rs:24:17: 24:26
- StorageLive(_11); // scope 3 at $DIR/funky_arms.rs:26:43: 26:46
- _11 = &mut (*_1); // scope 3 at $DIR/funky_arms.rs:26:43: 26:46
- StorageLive(_12); // scope 3 at $DIR/funky_arms.rs:26:48: 26:51
- _12 = _2; // scope 3 at $DIR/funky_arms.rs:26:48: 26:51
- StorageLive(_13); // scope 3 at $DIR/funky_arms.rs:26:53: 26:57
- _13 = _6; // scope 3 at $DIR/funky_arms.rs:26:53: 26:57
- StorageLive(_14); // scope 3 at $DIR/funky_arms.rs:26:59: 26:79
- StorageLive(_15); // scope 3 at $DIR/funky_arms.rs:26:59: 26:75
- StorageLive(_16); // scope 3 at $DIR/funky_arms.rs:26:59: 26:68
- _16 = _10; // scope 3 at $DIR/funky_arms.rs:26:59: 26:68
- _15 = move _16 as u32 (Misc); // scope 3 at $DIR/funky_arms.rs:26:59: 26:75
- StorageDead(_16); // scope 3 at $DIR/funky_arms.rs:26:74: 26:75
- _14 = Add(move _15, const 1_u32); // scope 3 at $DIR/funky_arms.rs:26:59: 26:79
- StorageDead(_15); // scope 3 at $DIR/funky_arms.rs:26:78: 26:79
- StorageLive(_17); // scope 3 at $DIR/funky_arms.rs:26:81: 26:86
- _17 = _3; // scope 3 at $DIR/funky_arms.rs:26:81: 26:86
- _0 = float_to_exponential_common_exact::<T>(move _11, move _12, move _13, move _14, move _17) -> bb8; // scope 3 at $DIR/funky_arms.rs:26:9: 26:87
+ StorageLive(_11); // scope 2 at $DIR/funky_arms.rs:26:43: 26:46
+ _11 = &mut (*_1); // scope 2 at $DIR/funky_arms.rs:26:43: 26:46
+ StorageLive(_12); // scope 2 at $DIR/funky_arms.rs:26:48: 26:51
+ _12 = _2; // scope 2 at $DIR/funky_arms.rs:26:48: 26:51
+ StorageLive(_13); // scope 2 at $DIR/funky_arms.rs:26:53: 26:57
+ _13 = _6; // scope 2 at $DIR/funky_arms.rs:26:53: 26:57
+ StorageLive(_14); // scope 2 at $DIR/funky_arms.rs:26:59: 26:79
+ StorageLive(_15); // scope 2 at $DIR/funky_arms.rs:26:59: 26:75
+ StorageLive(_16); // scope 2 at $DIR/funky_arms.rs:26:59: 26:68
+ _16 = _10; // scope 2 at $DIR/funky_arms.rs:26:59: 26:68
+ _15 = move _16 as u32 (Misc); // scope 2 at $DIR/funky_arms.rs:26:59: 26:75
+ StorageDead(_16); // scope 2 at $DIR/funky_arms.rs:26:74: 26:75
+ _14 = Add(move _15, const 1_u32); // scope 2 at $DIR/funky_arms.rs:26:59: 26:79
+ StorageDead(_15); // scope 2 at $DIR/funky_arms.rs:26:78: 26:79
+ StorageLive(_17); // scope 2 at $DIR/funky_arms.rs:26:81: 26:86
+ _17 = _3; // scope 2 at $DIR/funky_arms.rs:26:81: 26:86
+ _0 = float_to_exponential_common_exact::<T>(move _11, move _12, move _13, move _14, move _17) -> bb8; // scope 2 at $DIR/funky_arms.rs:26:9: 26:87
// mir::Constant
// + span: $DIR/funky_arms.rs:26:9: 26:42
// + literal: Const { ty: for<'r, 's, 't0> fn(&'r mut std::fmt::Formatter<'s>, &'t0 T, core::num::flt2dec::Sign, u32, bool) -> std::result::Result<(), std::fmt::Error> {float_to_exponential_common_exact::<T>}, val: Value(Scalar(<ZST>)) }
}
bb8: {
- StorageDead(_17); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87
- StorageDead(_14); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87
- StorageDead(_13); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87
- StorageDead(_12); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87
- StorageDead(_11); // scope 3 at $DIR/funky_arms.rs:26:86: 26:87
- StorageDead(_10); // scope 2 at $DIR/funky_arms.rs:27:5: 27:6
+ StorageDead(_17); // scope 2 at $DIR/funky_arms.rs:26:86: 26:87
+ StorageDead(_14); // scope 2 at $DIR/funky_arms.rs:26:86: 26:87
+ StorageDead(_13); // scope 2 at $DIR/funky_arms.rs:26:86: 26:87
+ StorageDead(_12); // scope 2 at $DIR/funky_arms.rs:26:86: 26:87
+ StorageDead(_11); // scope 2 at $DIR/funky_arms.rs:26:86: 26:87
goto -> bb10; // scope 2 at $DIR/funky_arms.rs:24:5: 29:6
}
@@ -135,6 +134,7 @@
}
bb10: {
+ StorageDead(_10); // scope 2 at $DIR/funky_arms.rs:29:5: 29:6
StorageDead(_6); // scope 1 at $DIR/funky_arms.rs:30:1: 30:2
StorageDead(_4); // scope 0 at $DIR/funky_arms.rs:30:1: 30:2
StorageDead(_7); // scope 0 at $DIR/funky_arms.rs:30:1: 30:2
diff --git a/src/test/mir-opt/issue_41888.main.ElaborateDrops.after.mir b/src/test/mir-opt/issue_41888.main.ElaborateDrops.after.mir
index 488fcb5dd70e8..0417ed8c88b13 100644
--- a/src/test/mir-opt/issue_41888.main.ElaborateDrops.after.mir
+++ b/src/test/mir-opt/issue_41888.main.ElaborateDrops.after.mir
@@ -26,7 +26,7 @@ fn main() -> () {
_8 = const false; // scope 0 at $DIR/issue-41888.rs:7:9: 7:10
StorageLive(_1); // scope 0 at $DIR/issue-41888.rs:7:9: 7:10
StorageLive(_2); // scope 1 at $DIR/issue-41888.rs:8:8: 8:14
- _2 = cond() -> [return: bb1, unwind: bb11]; // scope 1 at $DIR/issue-41888.rs:8:8: 8:14
+ _2 = cond() -> [return: bb1, unwind: bb12]; // scope 1 at $DIR/issue-41888.rs:8:8: 8:14
// mir::Constant
// + span: $DIR/issue-41888.rs:8:8: 8:12
// + literal: Const { ty: fn() -> bool {cond}, val: Value(Scalar(<ZST>)) }
@@ -42,12 +42,12 @@ fn main() -> () {
_4 = K; // scope 1 at $DIR/issue-41888.rs:9:18: 9:19
_3 = E::F(move _4); // scope 1 at $DIR/issue-41888.rs:9:13: 9:20
StorageDead(_4); // scope 1 at $DIR/issue-41888.rs:9:19: 9:20
- goto -> bb14; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
+ goto -> bb15; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
}
bb3: {
_0 = const (); // scope 1 at $DIR/issue-41888.rs:14:6: 14:6
- goto -> bb8; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6
+ goto -> bb9; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6
}
bb4: {
@@ -69,17 +69,21 @@ fn main() -> () {
StorageLive(_6); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23
_9 = const false; // scope 1 at $DIR/issue-41888.rs:10:21: 10:23
_6 = move ((_1 as F).0: K); // scope 1 at $DIR/issue-41888.rs:10:21: 10:23
- _0 = const (); // scope 2 at $DIR/issue-41888.rs:10:29: 13:10
- StorageDead(_6); // scope 1 at $DIR/issue-41888.rs:13:9: 13:10
+ _0 = const (); // scope 1 at $DIR/issue-41888.rs:10:29: 13:10
goto -> bb8; // scope 1 at $DIR/issue-41888.rs:10:9: 13:10
}
bb8: {
- StorageDead(_2); // scope 1 at $DIR/issue-41888.rs:14:5: 14:6
- goto -> bb20; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ StorageDead(_6); // scope 1 at $DIR/issue-41888.rs:13:9: 13:10
+ goto -> bb9; // scope 1 at $DIR/issue-41888.rs:8:5: 14:6
}
bb9: {
+ StorageDead(_2); // scope 1 at $DIR/issue-41888.rs:14:5: 14:6
+ goto -> bb21; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ }
+
+ bb10: {
_7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
_8 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
_9 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
@@ -87,27 +91,27 @@ fn main() -> () {
return; // scope 0 at $DIR/issue-41888.rs:15:2: 15:2
}
- bb10 (cleanup): {
- goto -> bb11; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20
- }
-
bb11 (cleanup): {
- goto -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ goto -> bb12; // scope 1 at $DIR/issue-41888.rs:9:19: 9:20
}
bb12 (cleanup): {
- resume; // scope 0 at $DIR/issue-41888.rs:6:1: 15:2
+ goto -> bb13; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
bb13 (cleanup): {
+ resume; // scope 0 at $DIR/issue-41888.rs:6:1: 15:2
+ }
+
+ bb14 (cleanup): {
_7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
_8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
_9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
_1 = move _3; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
- goto -> bb10; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
+ goto -> bb11; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
}
- bb14: {
+ bb15: {
_7 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
_8 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
_9 = const true; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
@@ -115,38 +119,38 @@ fn main() -> () {
goto -> bb4; // scope 1 at $DIR/issue-41888.rs:9:9: 9:10
}
- bb15: {
+ bb16: {
_7 = const false; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
- goto -> bb9; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ goto -> bb10; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
- bb16 (cleanup): {
- goto -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ bb17 (cleanup): {
+ goto -> bb13; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
- bb17: {
- drop(_1) -> [return: bb15, unwind: bb12]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ bb18: {
+ drop(_1) -> [return: bb16, unwind: bb13]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
- bb18 (cleanup): {
- drop(_1) -> bb12; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ bb19 (cleanup): {
+ drop(_1) -> bb13; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
- bb19: {
+ bb20: {
_10 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
- switchInt(move _10) -> [0_isize: bb15, otherwise: bb17]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ switchInt(move _10) -> [0_isize: bb16, otherwise: bb18]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
- bb20: {
- switchInt(_7) -> [false: bb15, otherwise: bb19]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ bb21: {
+ switchInt(_7) -> [false: bb16, otherwise: bb20]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
- bb21 (cleanup): {
+ bb22 (cleanup): {
_11 = discriminant(_1); // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
- switchInt(move _11) -> [0_isize: bb16, otherwise: bb18]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ switchInt(move _11) -> [0_isize: bb17, otherwise: bb19]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
- bb22 (cleanup): {
- switchInt(_7) -> [false: bb12, otherwise: bb21]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
+ bb23 (cleanup): {
+ switchInt(_7) -> [false: bb13, otherwise: bb22]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
}
diff --git a/src/test/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff b/src/test/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff
index 4d1b6bedd5fab..89ba5eeeef4e6 100644
--- a/src/test/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff
+++ b/src/test/mir-opt/issues/issue_75439.foo.MatchBranchSimplification.diff
@@ -13,8 +13,8 @@
let _4: u32; // in scope 1 at $DIR/issue-75439.rs:9:27: 9:29
scope 3 {
debug ip => _4; // in scope 3 at $DIR/issue-75439.rs:9:27: 9:29
- scope 4 {
- }
+ }
+ scope 4 {
}
}
scope 2 {
@@ -49,7 +49,7 @@
}
bb5: {
- StorageLive(_5); // scope 3 at $DIR/issue-75439.rs:10:14: 10:38
+ StorageLive(_5); // scope 1 at $DIR/issue-75439.rs:10:14: 10:38
StorageLive(_6); // scope 4 at $DIR/issue-75439.rs:10:33: 10:35
_6 = _4; // scope 4 at $DIR/issue-75439.rs:10:33: 10:35
_5 = transmute::<u32, [u8; 4]>(move _6) -> bb8; // scope 4 at $DIR/issue-75439.rs:10:23: 10:36
@@ -61,25 +61,25 @@
bb6: {
StorageLive(_4); // scope 1 at $DIR/issue-75439.rs:9:27: 9:29
_4 = _2[3 of 4]; // scope 1 at $DIR/issue-75439.rs:9:27: 9:29
- goto -> bb5; // scope 1 at $DIR/issue-75439.rs:9:5: 13:6
+ goto -> bb5; // scope 1 at $DIR/issue-75439.rs:9:12: 9:30
}
bb7: {
StorageLive(_4); // scope 1 at $DIR/issue-75439.rs:9:27: 9:29
_4 = _2[3 of 4]; // scope 1 at $DIR/issue-75439.rs:9:27: 9:29
- goto -> bb5; // scope 1 at $DIR/issue-75439.rs:9:5: 13:6
+ goto -> bb5; // scope 1 at $DIR/issue-75439.rs:9:12: 9:30
}
bb8: {
StorageDead(_6); // scope 4 at $DIR/issue-75439.rs:10:35: 10:36
- ((_0 as Some).0: [u8; 4]) = move _5; // scope 3 at $DIR/issue-75439.rs:10:9: 10:39
- discriminant(_0) = 1; // scope 3 at $DIR/issue-75439.rs:10:9: 10:39
- StorageDead(_5); // scope 3 at $DIR/issue-75439.rs:10:38: 10:39
- StorageDead(_4); // scope 1 at $DIR/issue-75439.rs:11:5: 11:6
+ ((_0 as Some).0: [u8; 4]) = move _5; // scope 1 at $DIR/issue-75439.rs:10:9: 10:39
+ discriminant(_0) = 1; // scope 1 at $DIR/issue-75439.rs:10:9: 10:39
+ StorageDead(_5); // scope 1 at $DIR/issue-75439.rs:10:38: 10:39
goto -> bb9; // scope 1 at $DIR/issue-75439.rs:9:5: 13:6
}
bb9: {
+ StorageDead(_4); // scope 1 at $DIR/issue-75439.rs:13:5: 13:6
StorageDead(_2); // scope 0 at $DIR/issue-75439.rs:14:1: 14:2
return; // scope 0 at $DIR/issue-75439.rs:14:2: 14:2
}
diff --git a/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff b/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff
index 8b6e38843a862..41a6fe30412d0 100644
--- a/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff
+++ b/src/test/mir-opt/simplify_locals_fixedpoint.foo.SimplifyLocals.diff
@@ -37,17 +37,17 @@
bb2: {
StorageLive(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19
_6 = (((_1.0: std::option::Option<u8>) as Some).0: u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:18: 4:19
-- StorageLive(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20
-- StorageLive(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13
-- _8 = _6; // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13
-- _7 = Gt(move _8, const 42_u8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20
-- StorageDead(_8); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20
-- StorageDead(_7); // scope 1 at $DIR/simplify-locals-fixedpoint.rs:7:9: 7:10
- StorageDead(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6
+- StorageLive(_7); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20
+- StorageLive(_8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13
+- _8 = _6; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:13
+- _7 = Gt(move _8, const 42_u8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:12: 5:20
+- StorageDead(_8); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:5:19: 5:20
+- StorageDead(_7); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:7:9: 7:10
goto -> bb3; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:4:5: 8:6
}
bb3: {
+ StorageDead(_6); // scope 0 at $DIR/simplify-locals-fixedpoint.rs:8:5: 8:6
drop(_1) -> bb4; // scope 0 at $DIR/simplify-locals-fixedpoint.rs:9:1: 9:2
}
diff --git a/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff
index 23a3fbd5f4651..ff05951a99b63 100644
--- a/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff
+++ b/src/test/mir-opt/unreachable.main.UnreachablePropagation.diff
@@ -6,15 +6,15 @@
let mut _1: std::option::Option<Empty>; // in scope 0 at $DIR/unreachable.rs:9:23: 9:30
let mut _2: isize; // in scope 0 at $DIR/unreachable.rs:9:12: 9:20
let _3: Empty; // in scope 0 at $DIR/unreachable.rs:9:17: 9:19
+ let mut _4: i32; // in scope 0 at $DIR/unreachable.rs:10:13: 10:19
let _5: (); // in scope 0 at $DIR/unreachable.rs:12:9: 16:10
let mut _6: bool; // in scope 0 at $DIR/unreachable.rs:12:12: 12:16
let mut _7: !; // in scope 0 at $DIR/unreachable.rs:18:9: 18:21
scope 1 {
debug _x => _3; // in scope 1 at $DIR/unreachable.rs:9:17: 9:19
- let mut _4: i32; // in scope 1 at $DIR/unreachable.rs:10:13: 10:19
- scope 2 {
- debug _y => _4; // in scope 2 at $DIR/unreachable.rs:10:13: 10:19
- }
+ }
+ scope 2 {
+ debug _y => _4; // in scope 2 at $DIR/unreachable.rs:10:13: 10:19
}
bb0: {
@@ -33,6 +33,7 @@
bb2: {
_0 = const (); // scope 0 at $DIR/unreachable.rs:19:6: 19:6
+ StorageDead(_3); // scope 0 at $DIR/unreachable.rs:19:5: 19:6
StorageDead(_1); // scope 0 at $DIR/unreachable.rs:20:1: 20:2
return; // scope 0 at $DIR/unreachable.rs:20:2: 20:2
- }
@@ -40,7 +41,7 @@
- bb3: {
- StorageLive(_3); // scope 0 at $DIR/unreachable.rs:9:17: 9:19
- _3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable.rs:9:17: 9:19
-- StorageLive(_4); // scope 1 at $DIR/unreachable.rs:10:13: 10:19
+- StorageLive(_4); // scope 0 at $DIR/unreachable.rs:10:13: 10:19
- StorageLive(_5); // scope 2 at $DIR/unreachable.rs:12:9: 16:10
- StorageLive(_6); // scope 2 at $DIR/unreachable.rs:12:12: 12:16
- _6 = const true; // scope 2 at $DIR/unreachable.rs:12:12: 12:16
diff --git a/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff
index 3eda18317057b..b94441d59195d 100644
--- a/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff
+++ b/src/test/mir-opt/unreachable_asm.main.UnreachablePropagation.diff
@@ -6,17 +6,17 @@
let mut _1: std::option::Option<Empty>; // in scope 0 at $DIR/unreachable_asm.rs:11:23: 11:30
let mut _2: isize; // in scope 0 at $DIR/unreachable_asm.rs:11:12: 11:20
let _3: Empty; // in scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19
+ let mut _4: i32; // in scope 0 at $DIR/unreachable_asm.rs:12:13: 12:19
let _5: (); // in scope 0 at $DIR/unreachable_asm.rs:14:9: 18:10
let mut _6: bool; // in scope 0 at $DIR/unreachable_asm.rs:14:12: 14:16
let _7: (); // in scope 0 at $DIR/unreachable_asm.rs:21:9: 21:37
let mut _8: !; // in scope 0 at $DIR/unreachable_asm.rs:22:9: 22:21
scope 1 {
debug _x => _3; // in scope 1 at $DIR/unreachable_asm.rs:11:17: 11:19
- let mut _4: i32; // in scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19
- scope 2 {
- debug _y => _4; // in scope 2 at $DIR/unreachable_asm.rs:12:13: 12:19
- scope 3 {
- }
+ }
+ scope 2 {
+ debug _y => _4; // in scope 2 at $DIR/unreachable_asm.rs:12:13: 12:19
+ scope 3 {
}
}
@@ -35,6 +35,7 @@
bb2: {
_0 = const (); // scope 0 at $DIR/unreachable_asm.rs:23:6: 23:6
+ StorageDead(_3); // scope 0 at $DIR/unreachable_asm.rs:23:5: 23:6
StorageDead(_1); // scope 0 at $DIR/unreachable_asm.rs:24:1: 24:2
return; // scope 0 at $DIR/unreachable_asm.rs:24:2: 24:2
}
@@ -42,7 +43,7 @@
bb3: {
StorageLive(_3); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19
_3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm.rs:11:17: 11:19
- StorageLive(_4); // scope 1 at $DIR/unreachable_asm.rs:12:13: 12:19
+ StorageLive(_4); // scope 0 at $DIR/unreachable_asm.rs:12:13: 12:19
StorageLive(_5); // scope 2 at $DIR/unreachable_asm.rs:14:9: 18:10
StorageLive(_6); // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16
_6 = const true; // scope 2 at $DIR/unreachable_asm.rs:14:12: 14:16
diff --git a/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff
index d0d52e869d624..666843eab3d09 100644
--- a/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff
+++ b/src/test/mir-opt/unreachable_asm_2.main.UnreachablePropagation.diff
@@ -6,6 +6,7 @@
let mut _1: std::option::Option<Empty>; // in scope 0 at $DIR/unreachable_asm_2.rs:11:23: 11:30
let mut _2: isize; // in scope 0 at $DIR/unreachable_asm_2.rs:11:12: 11:20
let _3: Empty; // in scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19
+ let mut _4: i32; // in scope 0 at $DIR/unreachable_asm_2.rs:12:13: 12:19
let _5: (); // in scope 0 at $DIR/unreachable_asm_2.rs:14:9: 22:10
let mut _6: bool; // in scope 0 at $DIR/unreachable_asm_2.rs:14:12: 14:16
let _7: (); // in scope 0 at $DIR/unreachable_asm_2.rs:16:13: 16:41
@@ -13,13 +14,12 @@
let mut _9: !; // in scope 0 at $DIR/unreachable_asm_2.rs:24:9: 24:21
scope 1 {
debug _x => _3; // in scope 1 at $DIR/unreachable_asm_2.rs:11:17: 11:19
- let mut _4: i32; // in scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19
- scope 2 {
- debug _y => _4; // in scope 2 at $DIR/unreachable_asm_2.rs:12:13: 12:19
- scope 3 {
- }
- scope 4 {
- }
+ }
+ scope 2 {
+ debug _y => _4; // in scope 2 at $DIR/unreachable_asm_2.rs:12:13: 12:19
+ scope 3 {
+ }
+ scope 4 {
}
}
@@ -38,6 +38,7 @@
bb2: {
_0 = const (); // scope 0 at $DIR/unreachable_asm_2.rs:25:6: 25:6
+ StorageDead(_3); // scope 0 at $DIR/unreachable_asm_2.rs:25:5: 25:6
StorageDead(_1); // scope 0 at $DIR/unreachable_asm_2.rs:26:1: 26:2
return; // scope 0 at $DIR/unreachable_asm_2.rs:26:2: 26:2
}
@@ -45,7 +46,7 @@
bb3: {
StorageLive(_3); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19
_3 = move ((_1 as Some).0: Empty); // scope 0 at $DIR/unreachable_asm_2.rs:11:17: 11:19
- StorageLive(_4); // scope 1 at $DIR/unreachable_asm_2.rs:12:13: 12:19
+ StorageLive(_4); // scope 0 at $DIR/unreachable_asm_2.rs:12:13: 12:19
StorageLive(_5); // scope 2 at $DIR/unreachable_asm_2.rs:14:9: 22:10
StorageLive(_6); // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16
_6 = const true; // scope 2 at $DIR/unreachable_asm_2.rs:14:12: 14:16
diff --git a/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff b/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff
index 2c0cc04085efa..32a067f22453d 100644
--- a/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff
+++ b/src/test/mir-opt/unreachable_diverging.main.UnreachablePropagation.diff
@@ -34,6 +34,7 @@
bb2: {
_0 = const (); // scope 1 at $DIR/unreachable_diverging.rs:19:6: 19:6
+ StorageDead(_4); // scope 1 at $DIR/unreachable_diverging.rs:19:5: 19:6
StorageDead(_1); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
StorageDead(_2); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
return; // scope 0 at $DIR/unreachable_diverging.rs:20:2: 20:2
@@ -42,31 +43,31 @@
bb3: {
StorageLive(_4); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21
_4 = move ((_2 as Some).0: Empty); // scope 1 at $DIR/unreachable_diverging.rs:14:17: 14:21
- StorageLive(_5); // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10
- StorageLive(_6); // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13
- _6 = _1; // scope 2 at $DIR/unreachable_diverging.rs:15:12: 15:13
-- switchInt(move _6) -> [false: bb5, otherwise: bb4]; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10
-+ goto -> bb4; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10
+ StorageLive(_5); // scope 1 at $DIR/unreachable_diverging.rs:15:9: 17:10
+ StorageLive(_6); // scope 1 at $DIR/unreachable_diverging.rs:15:12: 15:13
+ _6 = _1; // scope 1 at $DIR/unreachable_diverging.rs:15:12: 15:13
+- switchInt(move _6) -> [false: bb5, otherwise: bb4]; // scope 1 at $DIR/unreachable_diverging.rs:15:9: 17:10
++ goto -> bb4; // scope 1 at $DIR/unreachable_diverging.rs:15:9: 17:10
}
bb4: {
-- _5 = loop_forever() -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27
-+ _5 = loop_forever() -> bb5; // scope 2 at $DIR/unreachable_diverging.rs:16:13: 16:27
+- _5 = loop_forever() -> bb6; // scope 1 at $DIR/unreachable_diverging.rs:16:13: 16:27
++ _5 = loop_forever() -> bb5; // scope 1 at $DIR/unreachable_diverging.rs:16:13: 16:27
// mir::Constant
// + span: $DIR/unreachable_diverging.rs:16:13: 16:25
// + literal: Const { ty: fn() {loop_forever}, val: Value(Scalar(<ZST>)) }
}
bb5: {
-- _5 = const (); // scope 2 at $DIR/unreachable_diverging.rs:17:10: 17:10
-- goto -> bb6; // scope 2 at $DIR/unreachable_diverging.rs:15:9: 17:10
+- _5 = const (); // scope 1 at $DIR/unreachable_diverging.rs:17:10: 17:10
+- goto -> bb6; // scope 1 at $DIR/unreachable_diverging.rs:15:9: 17:10
- }
-
- bb6: {
- StorageDead(_6); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10
- StorageDead(_5); // scope 2 at $DIR/unreachable_diverging.rs:17:9: 17:10
- StorageLive(_7); // scope 2 at $DIR/unreachable_diverging.rs:18:9: 18:22
- unreachable; // scope 2 at $DIR/unreachable_diverging.rs:18:15: 18:19
+ StorageDead(_6); // scope 1 at $DIR/unreachable_diverging.rs:17:9: 17:10
+ StorageDead(_5); // scope 1 at $DIR/unreachable_diverging.rs:17:9: 17:10
+ StorageLive(_7); // scope 1 at $DIR/unreachable_diverging.rs:18:9: 18:22
+ unreachable; // scope 1 at $DIR/unreachable_diverging.rs:18:15: 18:19
}
}
diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.32bit.diff b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.32bit.diff
index de8a29ea25fcd..91927dc7f1676 100644
--- a/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.32bit.diff
+++ b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.32bit.diff
@@ -9,6 +9,8 @@
let mut _4: isize; // in scope 0 at $DIR/while_let_loops.rs:7:15: 7:25
let mut _5: !; // in scope 0 at $DIR/while_let_loops.rs:7:33: 10:6
let mut _6: !; // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6
+ let _7: (); // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6
+ let mut _8: !; // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6
scope 1 {
debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15
}
@@ -25,8 +27,10 @@
}
bb1: {
+ StorageLive(_7); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6
nop; // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6
- goto -> bb4; // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6
+ StorageDead(_7); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6
+ goto -> bb4; // scope 1 at no-location
}
bb2: {
diff --git a/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.64bit.diff b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.64bit.diff
index de8a29ea25fcd..91927dc7f1676 100644
--- a/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.64bit.diff
+++ b/src/test/mir-opt/while_let_loops.change_loop_body.ConstProp.64bit.diff
@@ -9,6 +9,8 @@
let mut _4: isize; // in scope 0 at $DIR/while_let_loops.rs:7:15: 7:25
let mut _5: !; // in scope 0 at $DIR/while_let_loops.rs:7:33: 10:6
let mut _6: !; // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6
+ let _7: (); // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6
+ let mut _8: !; // in scope 0 at $DIR/while_let_loops.rs:7:5: 10:6
scope 1 {
debug _x => _1; // in scope 1 at $DIR/while_let_loops.rs:6:9: 6:15
}
@@ -25,8 +27,10 @@
}
bb1: {
+ StorageLive(_7); // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6
nop; // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6
- goto -> bb4; // scope 1 at $DIR/while_let_loops.rs:7:5: 10:6
+ StorageDead(_7); // scope 1 at $DIR/while_let_loops.rs:10:5: 10:6
+ goto -> bb4; // scope 1 at no-location
}
bb2: {
diff --git a/src/test/mir-opt/while_storage.while_loop.PreCodegen.after.mir b/src/test/mir-opt/while_storage.while_loop.PreCodegen.after.mir
index bf9c2d138a0f6..325b8591c7338 100644
--- a/src/test/mir-opt/while_storage.while_loop.PreCodegen.after.mir
+++ b/src/test/mir-opt/while_storage.while_loop.PreCodegen.after.mir
@@ -20,36 +20,40 @@ fn while_loop(_1: bool) -> () {
bb1: {
StorageDead(_3); // scope 0 at $DIR/while-storage.rs:10:21: 10:22
- switchInt(_2) -> [false: bb6, otherwise: bb2]; // scope 0 at $DIR/while-storage.rs:10:5: 14:6
+ switchInt(move _2) -> [false: bb3, otherwise: bb2]; // scope 0 at $DIR/while-storage.rs:10:5: 14:6
}
bb2: {
StorageLive(_4); // scope 0 at $DIR/while-storage.rs:11:12: 11:23
StorageLive(_5); // scope 0 at $DIR/while-storage.rs:11:21: 11:22
_5 = _1; // scope 0 at $DIR/while-storage.rs:11:21: 11:22
- _4 = get_bool(move _5) -> bb3; // scope 0 at $DIR/while-storage.rs:11:12: 11:23
+ _4 = get_bool(move _5) -> bb4; // scope 0 at $DIR/while-storage.rs:11:12: 11:23
// mir::Constant
// + span: $DIR/while-storage.rs:11:12: 11:20
// + literal: Const { ty: fn(bool) -> bool {get_bool}, val: Value(Scalar(<ZST>)) }
}
bb3: {
- StorageDead(_5); // scope 0 at $DIR/while-storage.rs:11:22: 11:23
- switchInt(move _4) -> [false: bb5, otherwise: bb4]; // scope 0 at $DIR/while-storage.rs:11:9: 13:10
+ goto -> bb7; // scope 0 at no-location
}
bb4: {
- StorageDead(_4); // scope 0 at $DIR/while-storage.rs:13:9: 13:10
- goto -> bb6; // scope 0 at no-location
+ StorageDead(_5); // scope 0 at $DIR/while-storage.rs:11:22: 11:23
+ switchInt(move _4) -> [false: bb6, otherwise: bb5]; // scope 0 at $DIR/while-storage.rs:11:9: 13:10
}
bb5: {
+ StorageDead(_4); // scope 0 at $DIR/while-storage.rs:13:9: 13:10
+ goto -> bb7; // scope 0 at no-location
+ }
+
+ bb6: {
StorageDead(_4); // scope 0 at $DIR/while-storage.rs:13:9: 13:10
StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6
goto -> bb0; // scope 0 at $DIR/while-storage.rs:10:5: 14:6
}
- bb6: {
+ bb7: {
StorageDead(_2); // scope 0 at $DIR/while-storage.rs:14:5: 14:6
return; // scope 0 at $DIR/while-storage.rs:15:2: 15:2
}
diff --git a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
index 36ff8b01fd454..ed8e498b6b746 100644
--- a/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
+++ b/src/test/ui-fulldeps/pprust-expr-roundtrip.rs
@@ -161,7 +161,7 @@ fn iter_exprs(depth: usize, f: &mut dyn FnMut(P<Expr>)) {
19 => {
let pat =
P(Pat { id: DUMMY_NODE_ID, kind: PatKind::Wild, span: DUMMY_SP, tokens: None });
- iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e)))
+ iter_exprs(depth - 1, &mut |e| g(ExprKind::Let(pat.clone(), e, DUMMY_SP)))
}
_ => panic!("bad counter value in iter_exprs"),
}
diff --git a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr
index 7e5da949cb299..41b675f7960c5 100644
--- a/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr
+++ b/src/test/ui/closures/2229_closure_analysis/migrations/issue-78720.stderr
@@ -1,11 +1,8 @@
warning: irrefutable `if let` pattern
- --> $DIR/issue-78720.rs:6:5
+ --> $DIR/issue-78720.rs:6:8
|
-LL | / if let a = "" {
-LL | |
-LL | | drop(|_: ()| drop(a));
-LL | | }
- | |_____^
+LL | if let a = "" {
+ | ^^^^^^^^^^
|
= note: `#[warn(irrefutable_let_patterns)]` on by default
= note: this pattern will always match, so the `if let` is useless
diff --git a/src/test/ui/expr/if/if-let.rs b/src/test/ui/expr/if/if-let.rs
index 7208e388a1621..7fdd2be955b98 100644
--- a/src/test/ui/expr/if/if-let.rs
+++ b/src/test/ui/expr/if/if-let.rs
@@ -1,7 +1,7 @@
// check-pass
fn macros() {
- macro_rules! foo{
+ macro_rules! foo {
($p:pat, $e:expr, $b:block) => {{
if let $p = $e $b
//~^ WARN irrefutable `if let`
diff --git a/src/test/ui/expr/if/if-let.stderr b/src/test/ui/expr/if/if-let.stderr
index bcc3f536c40d5..7975a9dca0dc0 100644
--- a/src/test/ui/expr/if/if-let.stderr
+++ b/src/test/ui/expr/if/if-let.stderr
@@ -1,8 +1,8 @@
warning: irrefutable `if let` pattern
- --> $DIR/if-let.rs:6:13
+ --> $DIR/if-let.rs:6:16
|
LL | if let $p = $e $b
- | ^^^^^^^^^^^^^^^^^
+ | ^^^
...
LL | / foo!(a, 1, {
LL | | println!("irrefutable pattern");
@@ -15,10 +15,10 @@ LL | | });
= note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: irrefutable `if let` pattern
- --> $DIR/if-let.rs:6:13
+ --> $DIR/if-let.rs:6:16
|
LL | if let $p = $e $b
- | ^^^^^^^^^^^^^^^^^
+ | ^^^
...
LL | / bar!(a, 1, {
LL | | println!("irrefutable pattern");
@@ -30,51 +30,37 @@ LL | | });
= note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: irrefutable `if let` pattern
- --> $DIR/if-let.rs:26:5
+ --> $DIR/if-let.rs:26:8
|
-LL | / if let a = 1 {
-LL | | println!("irrefutable pattern");
-LL | | }
- | |_____^
+LL | if let a = 1 {
+ | ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
warning: irrefutable `if let` pattern
- --> $DIR/if-let.rs:30:5
+ --> $DIR/if-let.rs:30:8
|
-LL | / if let a = 1 {
-LL | | println!("irrefutable pattern");
-LL | | } else if true {
-LL | | println!("else-if in irrefutable `if let`");
-LL | | } else {
-LL | | println!("else in irrefutable `if let`");
-LL | | }
- | |_____^
+LL | if let a = 1 {
+ | ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
warning: irrefutable `if let` pattern
- --> $DIR/if-let.rs:40:12
+ --> $DIR/if-let.rs:40:15
|
-LL | } else if let a = 1 {
- | ____________^
-LL | | println!("irrefutable pattern");
-LL | | }
- | |_____^
+LL | } else if let a = 1 {
+ | ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
warning: irrefutable `if let` pattern
- --> $DIR/if-let.rs:46:12
+ --> $DIR/if-let.rs:46:15
|
-LL | } else if let a = 1 {
- | ____________^
-LL | | println!("irrefutable pattern");
-LL | | }
- | |_____^
+LL | } else if let a = 1 {
+ | ^^^^^^^^^
|
= note: this pattern will always match, so the `if let` is useless
= help: consider replacing the `if let` with a `let`
diff --git a/src/test/ui/expr/if/if-ret.rs b/src/test/ui/expr/if/if-ret.rs
index 6bb0141848af4..896072ce728eb 100644
--- a/src/test/ui/expr/if/if-ret.rs
+++ b/src/test/ui/expr/if/if-ret.rs
@@ -3,6 +3,6 @@
#![allow(unused_parens)]
// pretty-expanded FIXME #23616
-fn foo() { if (return) { } } //~ WARNING unreachable block in `if` expression
+fn foo() { if (return) { } } //~ WARNING unreachable block in `if`
pub fn main() { foo(); }
diff --git a/src/test/ui/expr/if/if-ret.stderr b/src/test/ui/expr/if/if-ret.stderr
index 41bbd791862be..8ced271aabc2b 100644
--- a/src/test/ui/expr/if/if-ret.stderr
+++ b/src/test/ui/expr/if/if-ret.stderr
@@ -1,8 +1,8 @@
-warning: unreachable block in `if` expression
+warning: unreachable block in `if` or `while` expression
--> $DIR/if-ret.rs:6:24
|
LL | fn foo() { if (return) { } }
- | -------- ^^^ unreachable block in `if` expression
+ | -------- ^^^ unreachable block in `if` or `while` expression
| |
| any code following this expression is unreachable
|
diff --git a/src/test/ui/issues/issue-14091.stderr b/src/test/ui/issues/issue-14091.stderr
index 9e9c7a596961f..7db4734780817 100644
--- a/src/test/ui/issues/issue-14091.stderr
+++ b/src/test/ui/issues/issue-14091.stderr
@@ -3,8 +3,6 @@ error[E0308]: mismatched types
|
LL | assert!(1,1);
| ^^^^^^^^^^^^^ expected `bool`, found integer
- |
- = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
diff --git a/src/test/ui/match/issue-82392.stdout b/src/test/ui/match/issue-82392.stdout
index 8ff76c64fc789..4f46e32dc3070 100644
--- a/src/test/ui/match/issue-82392.stdout
+++ b/src/test/ui/match/issue-82392.stdout
@@ -9,12 +9,11 @@ extern crate std;
pub fn main() ({
(if (true as bool)
({ } as
- ()) else {match ((Some as
+ ()) else if (let Some(a) =
+ ((Some as
fn(i32) -> Option<i32> {Option::<i32>::Some})((3
as
i32))
- as Option<i32>) {
- Some(a) => { }
- _ => { }
- }} as ())
- } as ())
+ as Option<i32>) as bool)
+ ({ } as ()) as ())
+ } as ())
diff --git a/src/test/ui/pattern/issue-82290.rs b/src/test/ui/pattern/issue-82290.rs
index 67f0274fe7436..d8da0ac8aa6c5 100644
--- a/src/test/ui/pattern/issue-82290.rs
+++ b/src/test/ui/pattern/issue-82290.rs
@@ -1,7 +1,9 @@
+// check-pass
+
#![feature(let_chains)] //~ WARN the feature `let_chains` is incomplete
fn main() {
- if true && let x = 1 { //~ ERROR `let` expressions are not supported here
+ if true && let x = 1 { //~ WARN irrefutable `let` pattern
let _ = x;
}
}
diff --git a/src/test/ui/pattern/issue-82290.stderr b/src/test/ui/pattern/issue-82290.stderr
index 666b1e785bf6b..0a3cf2c794f47 100644
--- a/src/test/ui/pattern/issue-82290.stderr
+++ b/src/test/ui/pattern/issue-82290.stderr
@@ -1,13 +1,5 @@
-error: `let` expressions are not supported here
- --> $DIR/issue-82290.rs:4:16
- |
-LL | if true && let x = 1 {
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
warning: the feature `let_chains` is incomplete and may not be safe to use and/or cause compiler crashes
- --> $DIR/issue-82290.rs:1:12
+ --> $DIR/issue-82290.rs:3:12
|
LL | #![feature(let_chains)]
| ^^^^^^^^^^
@@ -15,5 +7,15 @@ LL | #![feature(let_chains)]
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #53667 <https://github.com/rust-lang/rust/issues/53667> for more information
-error: aborting due to previous error; 1 warning emitted
+warning: irrefutable `let` pattern
+ --> $DIR/issue-82290.rs:6:16
+ |
+LL | if true && let x = 1 {
+ | ^^^^^^^^^
+ |
+ = note: `#[warn(irrefutable_let_patterns)]` on by default
+ = note: this pattern will always match, so the `let` is useless
+ = help: consider removing `let`
+
+warning: 2 warnings emitted
diff --git a/src/test/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr b/src/test/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr
index d6926ee12eeaa..0caa79a91529d 100644
--- a/src/test/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr
+++ b/src/test/ui/pattern/usefulness/deny-irrefutable-let-patterns.stderr
@@ -1,8 +1,8 @@
error: irrefutable `if let` pattern
- --> $DIR/deny-irrefutable-let-patterns.rs:7:5
+ --> $DIR/deny-irrefutable-let-patterns.rs:7:8
|
LL | if let _ = 5 {}
- | ^^^^^^^^^^^^^^^
+ | ^^^^^^^^^
|
note: the lint level is defined here
--> $DIR/deny-irrefutable-let-patterns.rs:4:9
@@ -13,12 +13,10 @@ LL | #![deny(irrefutable_let_patterns)]
= help: consider replacing the `if let` with a `let`
error: irrefutable `while let` pattern
- --> $DIR/deny-irrefutable-let-patterns.rs:9:5
+ --> $DIR/deny-irrefutable-let-patterns.rs:9:11
|
-LL | / while let _ = 5 {
-LL | | break;
-LL | | }
- | |_____^
+LL | while let _ = 5 {
+ | ^^^^^^^^^
|
= note: this pattern will always match, so the loop will never exit
= help: consider instead using a `loop { ... }` with a `let` inside it
diff --git a/src/test/ui/reachable/expr_if.rs b/src/test/ui/reachable/expr_if.rs
index a3d54892de5bb..3c04eaf480a2c 100644
--- a/src/test/ui/reachable/expr_if.rs
+++ b/src/test/ui/reachable/expr_if.rs
@@ -4,7 +4,7 @@
#![deny(unreachable_code)]
fn foo() {
- if {return} { //~ ERROR unreachable block in `if` expression
+ if {return} { //~ ERROR unreachable block in `if`
println!("Hello, world!");
}
}
diff --git a/src/test/ui/reachable/expr_if.stderr b/src/test/ui/reachable/expr_if.stderr
index 71cc94be3ee44..1112273f441c6 100644
--- a/src/test/ui/reachable/expr_if.stderr
+++ b/src/test/ui/reachable/expr_if.stderr
@@ -1,4 +1,4 @@
-error: unreachable block in `if` expression
+error: unreachable block in `if` or `while` expression
--> $DIR/expr_if.rs:7:17
|
LL | if {return} {
@@ -7,7 +7,7 @@ LL | if {return} {
| | any code following this expression is unreachable
LL | | println!("Hello, world!");
LL | | }
- | |_____^ unreachable block in `if` expression
+ | |_____^ unreachable block in `if` or `while` expression
|
note: the lint level is defined here
--> $DIR/expr_if.rs:4:9
diff --git a/src/test/ui/reachable/expr_while.rs b/src/test/ui/reachable/expr_while.rs
index 10a4b69939f12..5005f3833a0b3 100644
--- a/src/test/ui/reachable/expr_while.rs
+++ b/src/test/ui/reachable/expr_while.rs
@@ -5,7 +5,7 @@
fn foo() {
while {return} {
- //~^ ERROR unreachable block in `while` expression
+ //~^ ERROR unreachable block in `if`
println!("Hello, world!");
}
}
@@ -20,7 +20,7 @@ fn bar() {
fn baz() {
// Here, we cite the `while` loop as dead.
while {return} {
- //~^ ERROR unreachable block in `while` expression
+ //~^ ERROR unreachable block in `if`
println!("I am dead.");
}
println!("I am, too.");
diff --git a/src/test/ui/reachable/expr_while.stderr b/src/test/ui/reachable/expr_while.stderr
index 83354574eafc4..b1859f6193423 100644
--- a/src/test/ui/reachable/expr_while.stderr
+++ b/src/test/ui/reachable/expr_while.stderr
@@ -1,4 +1,4 @@
-error: unreachable block in `while` expression
+error: unreachable block in `if` or `while` expression
--> $DIR/expr_while.rs:7:20
|
LL | while {return} {
@@ -8,7 +8,7 @@ LL | while {return} {
LL | |
LL | | println!("Hello, world!");
LL | | }
- | |_____^ unreachable block in `while` expression
+ | |_____^ unreachable block in `if` or `while` expression
|
note: the lint level is defined here
--> $DIR/expr_while.rs:4:9
@@ -16,7 +16,7 @@ note: the lint level is defined here
LL | #![deny(unreachable_code)]
| ^^^^^^^^^^^^^^^^
-error: unreachable block in `while` expression
+error: unreachable block in `if` or `while` expression
--> $DIR/expr_while.rs:22:20
|
LL | while {return} {
@@ -26,7 +26,7 @@ LL | while {return} {
LL | |
LL | | println!("I am dead.");
LL | | }
- | |_____^ unreachable block in `while` expression
+ | |_____^ unreachable block in `if` or `while` expression
error: aborting due to 2 previous errors
diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs
index 4ba7e1eeefaa6..2a2c0be52630b 100644
--- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs
+++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.rs
@@ -9,33 +9,25 @@ fn _if_let_guard() {
() if (let 0 = 1) => {}
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
() if (((let 0 = 1))) => {}
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
() if true && let 0 = 1 => {}
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
() if let 0 = 1 && true => {}
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
() if (let 0 = 1) && true => {}
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
() if true && (let 0 = 1) => {}
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
() if (let 0 = 1) && (let 0 = 1) => {}
//~^ ERROR `let` expressions in this position are experimental
//~| ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
() if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
//~^ ERROR `let` expressions in this position are experimental
@@ -43,15 +35,9 @@ fn _if_let_guard() {
//~| ERROR `let` expressions in this position are experimental
//~| ERROR `let` expressions in this position are experimental
//~| ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
() if let Range { start: _, end: _ } = (true..true) && false => {}
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
_ => {}
}
}
@@ -67,10 +53,8 @@ fn _macros() {
}
use_expr!((let 0 = 1 && 0 == 0));
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
use_expr!((let 0 = 1));
//~^ ERROR `let` expressions in this position are experimental
- //~| ERROR `let` expressions are not supported here
match () {
#[cfg(FALSE)]
() if let 0 = 1 => {}
diff --git a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
index 00811fe3044df..bedcdcb019ba9 100644
--- a/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
+++ b/src/test/ui/rfc-2294-if-let-guard/feature-gate.stderr
@@ -1,5 +1,5 @@
error: no rules expected the token `let`
- --> $DIR/feature-gate.rs:80:15
+ --> $DIR/feature-gate.rs:64:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
@@ -18,7 +18,7 @@ LL | () if let 0 = 1 => {}
= help: you can write `if matches!(<expr>, <pattern>)` instead of `if let <pattern> = <expr>`
error[E0658]: `if let` guards are experimental
- --> $DIR/feature-gate.rs:76:12
+ --> $DIR/feature-gate.rs:60:12
|
LL | () if let 0 = 1 => {}
| ^^^^^^^^^^^^
@@ -38,7 +38,7 @@ LL | () if (let 0 = 1) => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:14:18
+ --> $DIR/feature-gate.rs:13:18
|
LL | () if (((let 0 = 1))) => {}
| ^^^^^^^^^
@@ -48,7 +48,7 @@ LL | () if (((let 0 = 1))) => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:18:23
+ --> $DIR/feature-gate.rs:16:23
|
LL | () if true && let 0 = 1 => {}
| ^^^^^^^^^
@@ -58,7 +58,7 @@ LL | () if true && let 0 = 1 => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:22:15
+ --> $DIR/feature-gate.rs:19:15
|
LL | () if let 0 = 1 && true => {}
| ^^^^^^^^^
@@ -68,7 +68,7 @@ LL | () if let 0 = 1 && true => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:26:16
+ --> $DIR/feature-gate.rs:22:16
|
LL | () if (let 0 = 1) && true => {}
| ^^^^^^^^^
@@ -78,7 +78,7 @@ LL | () if (let 0 = 1) && true => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:30:24
+ --> $DIR/feature-gate.rs:25:24
|
LL | () if true && (let 0 = 1) => {}
| ^^^^^^^^^
@@ -88,7 +88,7 @@ LL | () if true && (let 0 = 1) => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:34:16
+ --> $DIR/feature-gate.rs:28:16
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^^^^^^^
@@ -98,7 +98,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:34:31
+ --> $DIR/feature-gate.rs:28:31
|
LL | () if (let 0 = 1) && (let 0 = 1) => {}
| ^^^^^^^^^
@@ -108,7 +108,7 @@ LL | () if (let 0 = 1) && (let 0 = 1) => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:40:15
+ --> $DIR/feature-gate.rs:32:15
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
@@ -118,7 +118,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:40:28
+ --> $DIR/feature-gate.rs:32:28
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
@@ -128,7 +128,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:40:42
+ --> $DIR/feature-gate.rs:32:42
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
@@ -138,7 +138,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:40:55
+ --> $DIR/feature-gate.rs:32:55
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
@@ -148,7 +148,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:40:68
+ --> $DIR/feature-gate.rs:32:68
|
LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
| ^^^^^^^^^
@@ -158,7 +158,7 @@ LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 =
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:52:15
+ --> $DIR/feature-gate.rs:39:15
|
LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -168,7 +168,7 @@ LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:68:16
+ --> $DIR/feature-gate.rs:54:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^^^^^^^
@@ -178,7 +178,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0));
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:71:16
+ --> $DIR/feature-gate.rs:56:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
@@ -187,134 +187,6 @@ LL | use_expr!((let 0 = 1));
= help: add `#![feature(let_chains)]` to the crate attributes to enable
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:10:16
- |
-LL | () if (let 0 = 1) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:14:18
- |
-LL | () if (((let 0 = 1))) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:18:23
- |
-LL | () if true && let 0 = 1 => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:22:15
- |
-LL | () if let 0 = 1 && true => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:26:16
- |
-LL | () if (let 0 = 1) && true => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:30:24
- |
-LL | () if true && (let 0 = 1) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:34:16
- |
-LL | () if (let 0 = 1) && (let 0 = 1) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:34:31
- |
-LL | () if (let 0 = 1) && (let 0 = 1) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:40:15
- |
-LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:40:28
- |
-LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:40:42
- |
-LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:40:55
- |
-LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:40:68
- |
-LL | () if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) => {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:52:15
- |
-LL | () if let Range { start: _, end: _ } = (true..true) && false => {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:68:16
- |
-LL | use_expr!((let 0 = 1 && 0 == 0));
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:71:16
- |
-LL | use_expr!((let 0 = 1));
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: aborting due to 35 previous errors
+error: aborting due to 19 previous errors
For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
index aeee27a151e82..b3c9246486ebb 100644
--- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.rs
@@ -201,7 +201,6 @@ fn outside_if_and_while_expr() {
(let true = let true = true);
//~^ ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
// Check function tail position.
&let 0 = 0
diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
index 996d0ea476d5c..b48915adc683b 100644
--- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr
@@ -1,5 +1,5 @@
error: expressions must be enclosed in braces to be used as const generic arguments
- --> $DIR/disallowed-positions.rs:236:9
+ --> $DIR/disallowed-positions.rs:235:9
|
LL | true && let 1 = 1
| ^^^^^^^^^^^^^^^^^
@@ -15,7 +15,8 @@ error: `let` expressions are not supported here
LL | if &let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:35:9
@@ -23,7 +24,8 @@ error: `let` expressions are not supported here
LL | if !let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:36:9
@@ -31,7 +33,8 @@ error: `let` expressions are not supported here
LL | if *let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:38:9
@@ -39,7 +42,8 @@ error: `let` expressions are not supported here
LL | if -let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:46:9
@@ -47,7 +51,8 @@ error: `let` expressions are not supported here
LL | if (let 0 = 0)? {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:50:16
@@ -55,7 +60,8 @@ error: `let` expressions are not supported here
LL | if true || let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:51:17
@@ -63,7 +69,8 @@ error: `let` expressions are not supported here
LL | if (true || let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:52:25
@@ -71,7 +78,8 @@ error: `let` expressions are not supported here
LL | if true && (true || let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:53:25
@@ -79,7 +87,8 @@ error: `let` expressions are not supported here
LL | if true || (true && let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:56:12
@@ -87,7 +96,8 @@ error: `let` expressions are not supported here
LL | if x = let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:59:15
@@ -95,7 +105,8 @@ error: `let` expressions are not supported here
LL | if true..(let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:61:11
@@ -103,7 +114,8 @@ error: `let` expressions are not supported here
LL | if ..(let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:63:9
@@ -111,7 +123,8 @@ error: `let` expressions are not supported here
LL | if (let 0 = 0).. {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:67:8
@@ -119,7 +132,8 @@ error: `let` expressions are not supported here
LL | if let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:71:8
@@ -127,7 +141,8 @@ error: `let` expressions are not supported here
LL | if let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:78:8
@@ -135,7 +150,8 @@ error: `let` expressions are not supported here
LL | if let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:86:8
@@ -143,7 +159,8 @@ error: `let` expressions are not supported here
LL | if let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:92:19
@@ -151,7 +168,8 @@ error: `let` expressions are not supported here
LL | if let true = let true = true {}
| ^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:96:12
@@ -159,7 +177,8 @@ error: `let` expressions are not supported here
LL | while &let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:99:12
@@ -167,7 +186,8 @@ error: `let` expressions are not supported here
LL | while !let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:100:12
@@ -175,7 +195,8 @@ error: `let` expressions are not supported here
LL | while *let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:102:12
@@ -183,7 +204,8 @@ error: `let` expressions are not supported here
LL | while -let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:110:12
@@ -191,7 +213,8 @@ error: `let` expressions are not supported here
LL | while (let 0 = 0)? {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:114:19
@@ -199,7 +222,8 @@ error: `let` expressions are not supported here
LL | while true || let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:115:20
@@ -207,7 +231,8 @@ error: `let` expressions are not supported here
LL | while (true || let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:116:28
@@ -215,7 +240,8 @@ error: `let` expressions are not supported here
LL | while true && (true || let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:117:28
@@ -223,7 +249,8 @@ error: `let` expressions are not supported here
LL | while true || (true && let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:120:15
@@ -231,7 +258,8 @@ error: `let` expressions are not supported here
LL | while x = let 0 = 0 {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:123:18
@@ -239,7 +267,8 @@ error: `let` expressions are not supported here
LL | while true..(let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:125:14
@@ -247,7 +276,8 @@ error: `let` expressions are not supported here
LL | while ..(let 0 = 0) {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:127:12
@@ -255,7 +285,8 @@ error: `let` expressions are not supported here
LL | while (let 0 = 0).. {}
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:131:11
@@ -263,7 +294,8 @@ error: `let` expressions are not supported here
LL | while let Range { start: _, end: _ } = true..true && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:135:11
@@ -271,7 +303,8 @@ error: `let` expressions are not supported here
LL | while let Range { start: _, end: _ } = true..true || false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:142:11
@@ -279,7 +312,8 @@ error: `let` expressions are not supported here
LL | while let Range { start: F, end } = F..|| true {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:150:11
@@ -287,7 +321,8 @@ error: `let` expressions are not supported here
LL | while let Range { start: true, end } = t..&&false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:156:22
@@ -295,7 +330,8 @@ error: `let` expressions are not supported here
LL | while let true = let true = true {}
| ^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:170:6
@@ -303,7 +339,8 @@ error: `let` expressions are not supported here
LL | &let 0 = 0;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:172:6
@@ -311,7 +348,8 @@ error: `let` expressions are not supported here
LL | !let 0 = 0;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:173:6
@@ -319,7 +357,8 @@ error: `let` expressions are not supported here
LL | *let 0 = 0;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:175:6
@@ -327,7 +366,8 @@ error: `let` expressions are not supported here
LL | -let 0 = 0;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:183:6
@@ -335,7 +375,8 @@ error: `let` expressions are not supported here
LL | (let 0 = 0)?;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:187:13
@@ -343,7 +384,8 @@ error: `let` expressions are not supported here
LL | true || let 0 = 0;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:188:14
@@ -351,7 +393,8 @@ error: `let` expressions are not supported here
LL | (true || let 0 = 0);
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:189:22
@@ -359,7 +402,8 @@ error: `let` expressions are not supported here
LL | true && (true || let 0 = 0);
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:192:9
@@ -367,7 +411,8 @@ error: `let` expressions are not supported here
LL | x = let 0 = 0;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:194:12
@@ -375,7 +420,8 @@ error: `let` expressions are not supported here
LL | true..(let 0 = 0);
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:195:8
@@ -383,7 +429,8 @@ error: `let` expressions are not supported here
LL | ..(let 0 = 0);
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:196:6
@@ -391,7 +438,8 @@ error: `let` expressions are not supported here
LL | (let 0 = 0)..;
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:198:6
@@ -399,7 +447,8 @@ error: `let` expressions are not supported here
LL | (let Range { start: _, end: _ } = true..true || false);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
--> $DIR/disallowed-positions.rs:202:6
@@ -407,55 +456,53 @@ error: `let` expressions are not supported here
LL | (let true = let true = true);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:202:17
- |
-LL | (let true = let true = true);
- | ^^^^^^^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:207:6
+ --> $DIR/disallowed-positions.rs:206:6
|
LL | &let 0 = 0
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:218:17
+ --> $DIR/disallowed-positions.rs:217:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:222:17
+ --> $DIR/disallowed-positions.rs:221:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:226:17
+ --> $DIR/disallowed-positions.rs:225:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
error: `let` expressions are not supported here
- --> $DIR/disallowed-positions.rs:236:17
+ --> $DIR/disallowed-positions.rs:235:17
|
LL | true && let 1 = 1
| ^^^^^^^^^
|
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
+ = note: only supported directly in conditions of `if`- and `while`-expressions
+ = note: as well as when nested within `&&` and parenthesis in those conditions
warning: the feature `const_generics` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/disallowed-positions.rs:20:12
@@ -939,7 +986,7 @@ LL | (let Range { start: _, end: _ } = true..true || false);
found struct `std::ops::Range<_>`
error[E0308]: mismatched types
- --> $DIR/disallowed-positions.rs:207:5
+ --> $DIR/disallowed-positions.rs:206:5
|
LL | fn outside_if_and_while_expr() {
| - help: try adding a return type: `-> &bool`
@@ -960,7 +1007,7 @@ note: required by `branch`
LL | fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 104 previous errors; 2 warnings emitted
+error: aborting due to 103 previous errors; 2 warnings emitted
Some errors have detailed explanations: E0277, E0308, E0600, E0614.
For more information about an error, try `rustc --explain E0277`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
index 0b38b5f47efef..2b4259e9dc150 100644
--- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.rs
@@ -13,33 +13,25 @@ fn _if() {
if (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR invalid parentheses around `let` expression in `if let`
if (((let 0 = 1))) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR invalid parentheses around `let` expression in `if let`
if true && let 0 = 1 {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
if let 0 = 1 && true {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
if (let 0 = 1) && true {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
if true && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
if (let 0 = 1) && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
@@ -47,15 +39,9 @@ fn _if() {
//~| ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
if let Range { start: _, end: _ } = (true..true) && false {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
}
fn _while() {
@@ -63,33 +49,25 @@ fn _while() {
while (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
while (((let 0 = 1))) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
while true && let 0 = 1 {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
while let 0 = 1 && true {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
while (let 0 = 1) && true {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
while true && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
while (let 0 = 1) && (let 0 = 1) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
@@ -97,15 +75,9 @@ fn _while() {
//~| ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions in this position are experimental [E0658]
//~| ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
while let Range { start: _, end: _ } = (true..true) && false {}
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
}
fn _macros() {
@@ -122,12 +94,8 @@ fn _macros() {
}
use_expr!((let 0 = 1 && 0 == 0));
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR `let` expressions are not supported here
- //~| ERROR `let` expressions are not supported here
use_expr!((let 0 = 1));
//~^ ERROR `let` expressions in this position are experimental [E0658]
- //~| ERROR invalid parentheses around `let` expression in `if let`
- //~| ERROR `let` expressions are not supported here
#[cfg(FALSE)] (let 0 = 1);
//~^ ERROR `let` expressions in this position are experimental [E0658]
use_expr!(let 0 = 1);
diff --git a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
index db464ad03f5f0..180eee0cadfe6 100644
--- a/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/feature-gate.stderr
@@ -1,5 +1,5 @@
error: no rules expected the token `let`
- --> $DIR/feature-gate.rs:133:15
+ --> $DIR/feature-gate.rs:101:15
|
LL | macro_rules! use_expr {
| --------------------- when calling this macro
@@ -18,7 +18,7 @@ LL | if (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:18:11
+ --> $DIR/feature-gate.rs:17:11
|
LL | if (((let 0 = 1))) {}
| ^^^^^^^^^
@@ -28,7 +28,7 @@ LL | if (((let 0 = 1))) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:22:16
+ --> $DIR/feature-gate.rs:20:16
|
LL | if true && let 0 = 1 {}
| ^^^^^^^^^
@@ -38,7 +38,7 @@ LL | if true && let 0 = 1 {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:26:8
+ --> $DIR/feature-gate.rs:23:8
|
LL | if let 0 = 1 && true {}
| ^^^^^^^^^
@@ -48,7 +48,7 @@ LL | if let 0 = 1 && true {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:30:9
+ --> $DIR/feature-gate.rs:26:9
|
LL | if (let 0 = 1) && true {}
| ^^^^^^^^^
@@ -58,7 +58,7 @@ LL | if (let 0 = 1) && true {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:34:17
+ --> $DIR/feature-gate.rs:29:17
|
LL | if true && (let 0 = 1) {}
| ^^^^^^^^^
@@ -68,7 +68,7 @@ LL | if true && (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:38:9
+ --> $DIR/feature-gate.rs:32:9
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
@@ -78,7 +78,7 @@ LL | if (let 0 = 1) && (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:38:24
+ --> $DIR/feature-gate.rs:32:24
|
LL | if (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
@@ -88,7 +88,7 @@ LL | if (let 0 = 1) && (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:44:8
+ --> $DIR/feature-gate.rs:36:8
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -98,7 +98,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:44:21
+ --> $DIR/feature-gate.rs:36:21
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -108,7 +108,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:44:35
+ --> $DIR/feature-gate.rs:36:35
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -118,7 +118,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:44:48
+ --> $DIR/feature-gate.rs:36:48
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -128,7 +128,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:44:61
+ --> $DIR/feature-gate.rs:36:61
|
LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -138,7 +138,7 @@ LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:56:8
+ --> $DIR/feature-gate.rs:43:8
|
LL | if let Range { start: _, end: _ } = (true..true) && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -148,7 +148,7 @@ LL | if let Range { start: _, end: _ } = (true..true) && false {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:64:12
+ --> $DIR/feature-gate.rs:50:12
|
LL | while (let 0 = 1) {}
| ^^^^^^^^^
@@ -158,7 +158,7 @@ LL | while (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:68:14
+ --> $DIR/feature-gate.rs:53:14
|
LL | while (((let 0 = 1))) {}
| ^^^^^^^^^
@@ -168,7 +168,7 @@ LL | while (((let 0 = 1))) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:72:19
+ --> $DIR/feature-gate.rs:56:19
|
LL | while true && let 0 = 1 {}
| ^^^^^^^^^
@@ -178,7 +178,7 @@ LL | while true && let 0 = 1 {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:76:11
+ --> $DIR/feature-gate.rs:59:11
|
LL | while let 0 = 1 && true {}
| ^^^^^^^^^
@@ -188,7 +188,7 @@ LL | while let 0 = 1 && true {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:80:12
+ --> $DIR/feature-gate.rs:62:12
|
LL | while (let 0 = 1) && true {}
| ^^^^^^^^^
@@ -198,7 +198,7 @@ LL | while (let 0 = 1) && true {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:84:20
+ --> $DIR/feature-gate.rs:65:20
|
LL | while true && (let 0 = 1) {}
| ^^^^^^^^^
@@ -208,7 +208,7 @@ LL | while true && (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:88:12
+ --> $DIR/feature-gate.rs:68:12
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
@@ -218,7 +218,7 @@ LL | while (let 0 = 1) && (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:88:27
+ --> $DIR/feature-gate.rs:68:27
|
LL | while (let 0 = 1) && (let 0 = 1) {}
| ^^^^^^^^^
@@ -228,7 +228,7 @@ LL | while (let 0 = 1) && (let 0 = 1) {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:94:11
+ --> $DIR/feature-gate.rs:72:11
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -238,7 +238,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:94:24
+ --> $DIR/feature-gate.rs:72:24
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -248,7 +248,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:94:38
+ --> $DIR/feature-gate.rs:72:38
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -258,7 +258,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:94:51
+ --> $DIR/feature-gate.rs:72:51
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -268,7 +268,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:94:64
+ --> $DIR/feature-gate.rs:72:64
|
LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
| ^^^^^^^^^
@@ -278,7 +278,7 @@ LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:106:11
+ --> $DIR/feature-gate.rs:79:11
|
LL | while let Range { start: _, end: _ } = (true..true) && false {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -288,7 +288,7 @@ LL | while let Range { start: _, end: _ } = (true..true) && false {}
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:131:20
+ --> $DIR/feature-gate.rs:99:20
|
LL | #[cfg(FALSE)] (let 0 = 1);
| ^^^^^^^^^
@@ -298,7 +298,7 @@ LL | #[cfg(FALSE)] (let 0 = 1);
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:114:17
+ --> $DIR/feature-gate.rs:86:17
|
LL | noop_expr!((let 0 = 1));
| ^^^^^^^^^
@@ -308,7 +308,7 @@ LL | noop_expr!((let 0 = 1));
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:123:16
+ --> $DIR/feature-gate.rs:95:16
|
LL | use_expr!((let 0 = 1 && 0 == 0));
| ^^^^^^^^^
@@ -318,7 +318,7 @@ LL | use_expr!((let 0 = 1 && 0 == 0));
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
error[E0658]: `let` expressions in this position are experimental
- --> $DIR/feature-gate.rs:127:16
+ --> $DIR/feature-gate.rs:97:16
|
LL | use_expr!((let 0 = 1));
| ^^^^^^^^^
@@ -327,274 +327,6 @@ LL | use_expr!((let 0 = 1));
= help: add `#![feature(let_chains)]` to the crate attributes to enable
= help: you can write `matches!(<expr>, <pattern>)` instead of `let <pattern> = <expr>`
-error: invalid parentheses around `let` expression in `if let`
- --> $DIR/feature-gate.rs:14:8
- |
-LL | if (let 0 = 1) {}
- | ^ ^
- |
-help: `if let` needs to be written without parentheses
- |
-LL - if (let 0 = 1) {}
-LL + if let 0 = 1 {}
- |
-
-error: invalid parentheses around `let` expression in `if let`
- --> $DIR/feature-gate.rs:18:8
- |
-LL | if (((let 0 = 1))) {}
- | ^^^ ^^^
- |
-help: `if let` needs to be written without parentheses
- |
-LL - if (((let 0 = 1))) {}
-LL + if let 0 = 1 {}
- |
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:22:16
- |
-LL | if true && let 0 = 1 {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:26:8
- |
-LL | if let 0 = 1 && true {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:30:9
- |
-LL | if (let 0 = 1) && true {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:34:17
- |
-LL | if true && (let 0 = 1) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:38:9
- |
-LL | if (let 0 = 1) && (let 0 = 1) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:38:24
- |
-LL | if (let 0 = 1) && (let 0 = 1) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:44:8
- |
-LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:44:21
- |
-LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:44:35
- |
-LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:44:48
- |
-LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:44:61
- |
-LL | if let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:56:8
- |
-LL | if let Range { start: _, end: _ } = (true..true) && false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:64:12
- |
-LL | while (let 0 = 1) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:68:14
- |
-LL | while (((let 0 = 1))) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:72:19
- |
-LL | while true && let 0 = 1 {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:76:11
- |
-LL | while let 0 = 1 && true {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:80:12
- |
-LL | while (let 0 = 1) && true {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:84:20
- |
-LL | while true && (let 0 = 1) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:88:12
- |
-LL | while (let 0 = 1) && (let 0 = 1) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:88:27
- |
-LL | while (let 0 = 1) && (let 0 = 1) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:94:11
- |
-LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:94:24
- |
-LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:94:38
- |
-LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:94:51
- |
-LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:94:64
- |
-LL | while let 0 = 1 && let 1 = 2 && (let 2 = 3 && let 3 = 4 && let 4 = 5) {}
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:106:11
- |
-LL | while let Range { start: _, end: _ } = (true..true) && false {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:123:16
- |
-LL | use_expr!((let 0 = 1 && 0 == 0));
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:123:16
- |
-LL | use_expr!((let 0 = 1 && 0 == 0));
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: invalid parentheses around `let` expression in `if let`
- --> $DIR/feature-gate.rs:127:15
- |
-LL | use_expr!((let 0 = 1));
- | ^ ^
- |
-help: `if let` needs to be written without parentheses
- |
-LL - use_expr!((let 0 = 1));
-LL + use_expr!(let 0 = 1);
- |
-
-error: `let` expressions are not supported here
- --> $DIR/feature-gate.rs:127:16
- |
-LL | use_expr!((let 0 = 1));
- | ^^^^^^^^^
- |
- = note: only supported directly without parentheses in conditions of `if`- and `while`-expressions, as well as in `let` chains within parentheses
-
-error: aborting due to 65 previous errors
+error: aborting due to 33 previous errors
For more information about this error, try `rustc --explain E0658`.
diff --git a/src/test/ui/rfc-2497-if-let-chains/protect-precedences.rs b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.rs
index 442983671884e..fcc09b159ec23 100644
--- a/src/test/ui/rfc-2497-if-let-chains/protect-precedences.rs
+++ b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.rs
@@ -10,7 +10,8 @@ fn main() {
fn _f1() -> bool {
// Should associate as `(let _ = (return (true && false)))`.
- if let _ = return true && false {}; //~ WARNING unreachable_code
+ if let _ = return true && false {};
+ //~^ WARNING unreachable block in `if`
}
assert!(!_f1());
}
diff --git a/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr
index 81cefdd29b3ea..24b35a2ab3167 100644
--- a/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr
+++ b/src/test/ui/rfc-2497-if-let-chains/protect-precedences.stderr
@@ -1,8 +1,8 @@
-warning: unreachable block in `if` expression
+warning: unreachable block in `if` or `while` expression
--> $DIR/protect-precedences.rs:13:41
|
LL | if let _ = return true && false {};
- | -------------------- ^^ unreachable block in `if` expression
+ | -------------------- ^^ unreachable block in `if` or `while` expression
| |
| any code following this expression is unreachable
|
diff --git a/src/test/ui/union/union-unsafe.thir.stderr b/src/test/ui/union/union-unsafe.thir.stderr
index e88642b0ff7ad..a8c3886657f43 100644
--- a/src/test/ui/union/union-unsafe.thir.stderr
+++ b/src/test/ui/union/union-unsafe.thir.stderr
@@ -55,10 +55,10 @@ LL | let U1 { a } = u1;
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
error[E0133]: access to union field is unsafe and requires unsafe function or block
- --> $DIR/union-unsafe.rs:65:20
+ --> $DIR/union-unsafe.rs:65:8
|
LL | if let U1 { a: 12 } = u1 {}
- | ^^ access to union field
+ | ^^^^^^^^^^^^^^^^^^^^^ access to union field
|
= note: the field may not be properly initialized: using uninitialized data will cause undefined behavior
diff --git a/src/test/ui/while-let.stderr b/src/test/ui/while-let.stderr
index d2f96f6642128..17e8830f63092 100644
--- a/src/test/ui/while-let.stderr
+++ b/src/test/ui/while-let.stderr
@@ -1,8 +1,8 @@
warning: irrefutable `while let` pattern
- --> $DIR/while-let.rs:7:13
+ --> $DIR/while-let.rs:7:19
|
LL | while let $p = $e $b
- | ^^^^^^^^^^^^^^^^^^^^
+ | ^^^
...
LL | / foo!(_a, 1, {
LL | | println!("irrefutable pattern");
@@ -15,10 +15,10 @@ LL | | });
= note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: irrefutable `while let` pattern
- --> $DIR/while-let.rs:7:13
+ --> $DIR/while-let.rs:7:19
|
LL | while let $p = $e $b
- | ^^^^^^^^^^^^^^^^^^^^
+ | ^^^
...
LL | / bar!(_a, 1, {
LL | | println!("irrefutable pattern");
@@ -30,13 +30,10 @@ LL | | });
= note: this warning originates in the macro `foo` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: irrefutable `while let` pattern
- --> $DIR/while-let.rs:27:5
+ --> $DIR/while-let.rs:27:11
|
-LL | / while let _a = 1 {
-LL | | println!("irrefutable pattern");
-LL | | break;
-LL | | }
- | |_____^
+LL | while let _a = 1 {
+ | ^^^^^^^^^^
|
= note: this pattern will always match, so the loop will never exit
= help: consider instead using a `loop { ... }` with a `let` inside it
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
index cb9347a923d87..891e865b245dd 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -1,5 +1,6 @@
use clippy_utils::consts::{constant, Constant};
use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::higher;
use clippy_utils::source::snippet_opt;
use clippy_utils::{is_direct_expn_of, is_expn_of, match_panic_call};
use if_chain::if_chain;
@@ -116,8 +117,8 @@ enum AssertKind {
/// where `message` is any expression and `c` is a constant bool.
fn match_assert_with_message<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<AssertKind> {
if_chain! {
- if let ExprKind::If(cond, then, _) = expr.kind;
- if let ExprKind::Unary(UnOp::Not, expr) = cond.kind;
+ if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
+ if let ExprKind::Unary(UnOp::Not, ref expr) = cond.kind;
// bind the first argument of the `assert!` macro
if let Some((Constant::Bool(is_true), _)) = constant(cx, cx.typeck_results(), expr);
// block
diff --git a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
index 9b2e4f8998e4e..51d95cc6f0b10 100644
--- a/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/src/tools/clippy/clippy_lints/src/blocks_in_if_conditions.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::higher;
use clippy_utils::source::snippet_block_with_applicability;
use clippy_utils::ty::implements_trait;
use clippy_utils::{differing_macro_contexts, get_parent_expr};
@@ -92,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
if in_external_macro(cx.sess(), expr.span) {
return;
}
- if let ExprKind::If(cond, _, _) = &expr.kind {
+ if let Some(higher::If { cond, .. }) = higher::If::hir(expr) {
if let ExprKind::Block(block, _) = &cond.kind {
if block.rules == BlockCheckMode::DefaultBlock {
if block.stmts.is_empty() {
diff --git a/src/tools/clippy/clippy_lints/src/collapsible_match.rs b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
index a403a9846babd..6b63c2cf157ac 100644
--- a/src/tools/clippy/clippy_lints/src/collapsible_match.rs
+++ b/src/tools/clippy/clippy_lints/src/collapsible_match.rs
@@ -1,9 +1,9 @@
use clippy_utils::diagnostics::span_lint_and_then;
use clippy_utils::visitors::LocalUsedVisitor;
-use clippy_utils::{is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
+use clippy_utils::{higher, is_lang_ctor, path_to_local, peel_ref_operators, SpanlessEq};
use if_chain::if_chain;
use rustc_hir::LangItem::OptionNone;
-use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
+use rustc_hir::{Expr, ExprKind, Guard, HirId, Pat, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{MultiSpan, Span};
@@ -49,22 +49,44 @@ declare_lint_pass!(CollapsibleMatch => [COLLAPSIBLE_MATCH]);
impl<'tcx> LateLintPass<'tcx> for CollapsibleMatch {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+ if let Some(higher::IfLet {
+ let_pat,
+ if_then,
+ if_else,
+ ..
+ }) = higher::IfLet::hir(expr)
+ {
+ check_arm(cx, if_then, None, let_pat, if_else);
+
+ check_if_let(cx, if_then, let_pat);
+ }
+
if let ExprKind::Match(_expr, arms, _source) = expr.kind {
- if let Some(wild_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
+ if let Some(wild_arm) = arms.iter().rfind(|arm| is_wild_like(cx, &arm.pat.kind, &arm.guard)) {
for arm in arms {
- check_arm(arm, wild_arm, cx);
+ check_arm(cx, arm.body, arm.guard.as_ref(), arm.pat, Some(wild_arm.body));
}
}
+
+ if let Some(first_arm) = arms.get(0) {
+ check_if_let(cx, &first_arm.body, &first_arm.pat);
+ }
}
}
}
-fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext<'tcx>) {
- let expr = strip_singleton_blocks(arm.body);
+fn check_arm<'tcx>(
+ cx: &LateContext<'tcx>,
+ outer_block: &'tcx Expr<'tcx>,
+ outer_guard: Option<&Guard<'tcx>>,
+ outer_pat: &'tcx Pat<'tcx>,
+ wild_outer_block: Option<&'tcx Expr<'tcx>>,
+) {
+ let expr = strip_singleton_blocks(outer_block);
if_chain! {
if let ExprKind::Match(expr_in, arms_inner, _) = expr.kind;
// the outer arm pattern and the inner match
- if expr_in.span.ctxt() == arm.pat.span.ctxt();
+ if expr_in.span.ctxt() == outer_pat.span.ctxt();
// there must be no more than two arms in the inner match for this lint
if arms_inner.len() == 2;
// no if guards on the inner match
@@ -73,18 +95,18 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
// match <local> { .. }
if let Some(binding_id) = path_to_local(peel_ref_operators(cx, expr_in));
// one of the branches must be "wild-like"
- if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| arm_is_wild_like(cx, arm_inner));
+ if let Some(wild_inner_arm_idx) = arms_inner.iter().rposition(|arm_inner| is_wild_like(cx, &arm_inner.pat.kind, &arm_inner.guard));
let (wild_inner_arm, non_wild_inner_arm) =
(&arms_inner[wild_inner_arm_idx], &arms_inner[1 - wild_inner_arm_idx]);
if !pat_contains_or(non_wild_inner_arm.pat);
// the binding must come from the pattern of the containing match arm
// ..<local>.. => match <local> { .. }
- if let Some(binding_span) = find_pat_binding(arm.pat, binding_id);
+ if let Some(binding_span) = find_pat_binding(outer_pat, binding_id);
// the "wild-like" branches must be equal
- if SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, wild_outer_arm.body);
+ if wild_outer_block.map(|el| SpanlessEq::new(cx).eq_expr(wild_inner_arm.body, el)).unwrap_or(true);
// the binding must not be used in the if guard
let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
- if match arm.guard {
+ if match outer_guard {
None => true,
Some(Guard::If(expr) | Guard::IfLet(_, expr)) => !used_visitor.check_expr(expr),
};
@@ -107,6 +129,31 @@ fn check_arm<'tcx>(arm: &Arm<'tcx>, wild_outer_arm: &Arm<'tcx>, cx: &LateContext
}
}
+fn check_if_let<'tcx>(cx: &LateContext<'tcx>, outer_expr: &'tcx Expr<'tcx>, outer_pat: &'tcx Pat<'tcx>) {
+ let block_inner = strip_singleton_blocks(outer_expr);
+ if_chain! {
+ if let Some(higher::IfLet { if_then: inner_if_then, let_expr: inner_let_expr, let_pat: inner_let_pat, .. }) = higher::IfLet::hir(block_inner);
+ if let Some(binding_id) = path_to_local(peel_ref_operators(cx, inner_let_expr));
+ if let Some(binding_span) = find_pat_binding(outer_pat, binding_id);
+ let mut used_visitor = LocalUsedVisitor::new(cx, binding_id);
+ if !used_visitor.check_expr(inner_if_then);
+ then {
+ span_lint_and_then(
+ cx,
+ COLLAPSIBLE_MATCH,
+ block_inner.span,
+ "unnecessary nested `if let` or `match`",
+ |diag| {
+ let mut help_span = MultiSpan::from_spans(vec![binding_span, inner_let_pat.span]);
+ help_span.push_span_label(binding_span, "replace this binding".into());
+ help_span.push_span_label(inner_let_pat.span, "with this pattern".into());
+ diag.span_help(help_span, "the outer pattern can be modified to include the inner pattern");
+ },
+ );
+ }
+ }
+}
+
fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
while let ExprKind::Block(block, _) = expr.kind {
match (block.stmts, block.expr) {
@@ -122,13 +169,13 @@ fn strip_singleton_blocks<'hir>(mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir>
}
/// A "wild-like" pattern is wild ("_") or `None`.
-/// For this lint to apply, both the outer and inner match expressions
+/// For this lint to apply, both the outer and inner patterns
/// must have "wild-like" branches that can be combined.
-fn arm_is_wild_like(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
- if arm.guard.is_some() {
+fn is_wild_like(cx: &LateContext<'_>, pat_kind: &PatKind<'_>, arm_guard: &Option<Guard<'_>>) -> bool {
+ if arm_guard.is_some() {
return false;
}
- match arm.pat.kind {
+ match pat_kind {
PatKind::Binding(..) | PatKind::Wild => true,
PatKind::Path(ref qpath) => is_lang_ctor(cx, qpath, OptionNone),
_ => false,
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index 2dcd55457993c..5eb99cfe24f49 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -316,9 +316,10 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
let mut start_eq = usize::MAX;
let mut end_eq = usize::MAX;
let mut expr_eq = true;
- for win in blocks.windows(2) {
- let l_stmts = win[0].stmts;
- let r_stmts = win[1].stmts;
+ let mut iter = blocks.windows(2);
+ while let Some(&[win0, win1]) = iter.next() {
+ let l_stmts = win0.stmts;
+ let r_stmts = win1.stmts;
// `SpanlessEq` now keeps track of the locals and is therefore context sensitive clippy#6752.
// The comparison therefore needs to be done in a way that builds the correct context.
@@ -335,22 +336,22 @@ fn scan_block_for_eq(cx: &LateContext<'tcx>, blocks: &[&Block<'tcx>]) -> Option<
it1.zip(it2)
.fold(0, |acc, (l, r)| if evaluator.eq_stmt(l, r) { acc + 1 } else { 0 })
};
- let block_expr_eq = both(&win[0].expr, &win[1].expr, |l, r| evaluator.eq_expr(l, r));
+ let block_expr_eq = both(&win0.expr, &win1.expr, |l, r| evaluator.eq_expr(l, r));
// IF_SAME_THEN_ELSE
if_chain! {
if block_expr_eq;
if l_stmts.len() == r_stmts.len();
if l_stmts.len() == current_start_eq;
- if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[0].hir_id);
- if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win[1].hir_id);
+ if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win0.hir_id);
+ if !is_lint_allowed(cx, IF_SAME_THEN_ELSE, win1.hir_id);
then {
span_lint_and_note(
cx,
IF_SAME_THEN_ELSE,
- win[0].span,
+ win0.span,
"this `if` has identical blocks",
- Some(win[1].span),
+ Some(win1.span),
"same as this",
);
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index ded7001ad8c86..7825e5f6ed52e 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -232,6 +232,7 @@ fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId,
| ExprKind::If(..)
| ExprKind::Loop(..)
| ExprKind::Match(..)
+ | ExprKind::Let(..)
| ExprKind::Closure(..)
| ExprKind::Block(..)
| ExprKind::Assign(..)
diff --git a/src/tools/clippy/clippy_lints/src/entry.rs b/src/tools/clippy/clippy_lints/src/entry.rs
index e1d0d65edb1b9..627f746ec9971 100644
--- a/src/tools/clippy/clippy_lints/src/entry.rs
+++ b/src/tools/clippy/clippy_lints/src/entry.rs
@@ -1,3 +1,4 @@
+use clippy_utils::higher;
use clippy_utils::{
can_move_expr_to_closure_no_visit,
diagnostics::span_lint_and_sugg,
@@ -5,6 +6,7 @@ use clippy_utils::{
source::{reindent_multiline, snippet_indent, snippet_with_applicability, snippet_with_context},
SpanlessEq,
};
+use core::fmt::Write;
use rustc_errors::Applicability;
use rustc_hir::{
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
@@ -13,7 +15,6 @@ use rustc_hir::{
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
-use std::fmt::Write;
declare_clippy_lint! {
/// ### What it does
@@ -62,10 +63,11 @@ declare_lint_pass!(HashMapPass => [MAP_ENTRY]);
impl<'tcx> LateLintPass<'tcx> for HashMapPass {
#[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- let (cond_expr, then_expr, else_expr) = match expr.kind {
- ExprKind::If(c, t, e) => (c, t, e),
+ let (cond_expr, then_expr, else_expr) = match higher::If::hir(expr) {
+ Some(higher::If { cond, then, r#else }) => (cond, then, r#else),
_ => return,
};
+
let (map_ty, contains_expr) = match try_parse_contains(cx, cond_expr) {
Some(x) => x,
None => return,
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 192b69e18f90f..f6a64a8ca6031 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -100,7 +100,7 @@ fn check_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if ex.span.ctxt() != expr.span.ctxt() {
if decl.inputs.is_empty() {
- if let Some(VecArgs::Vec(&[])) = higher::vec_macro(cx, ex) {
+ if let Some(VecArgs::Vec(&[])) = higher::VecArgs::hir(cx, ex) {
// replace `|| vec![]` with `Vec::new`
span_lint_and_sugg(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
index b01c0cdd84624..d12482e7b7bb9 100644
--- a/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/floating_point_arithmetic.rs
@@ -3,6 +3,7 @@ use clippy_utils::consts::{
Constant::{Int, F32, F64},
};
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::{eq_expr_value, get_parent_expr, numeric_literal, sugg};
use if_chain::if_chain;
use rustc_errors::Applicability;
@@ -545,11 +546,11 @@ fn are_negated<'a>(cx: &LateContext<'_>, expr1: &'a Expr<'a>, expr2: &'a Expr<'a
fn check_custom_abs(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
- if let ExprKind::If(cond, body, else_body) = expr.kind;
- if let ExprKind::Block(block, _) = body.kind;
+ if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
+ if let ExprKind::Block(block, _) = then.kind;
if block.stmts.is_empty();
if let Some(if_body_expr) = block.expr;
- if let Some(ExprKind::Block(else_block, _)) = else_body.map(|el| &el.kind);
+ if let Some(ExprKind::Block(else_block, _)) = r#else.map(|el| &el.kind);
if else_block.stmts.is_empty();
if let Some(else_body_expr) = else_block.expr;
if let Some((if_expr_positive, body)) = are_negated(cx, if_body_expr, else_body_expr);
diff --git a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
index d3ddeda9fd1b9..e2d3905eacb50 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_mutex.rs
@@ -1,9 +1,10 @@
use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::SpanlessEq;
use if_chain::if_chain;
use rustc_hir::intravisit::{self as visit, NestedVisitorMap, Visitor};
-use rustc_hir::{Expr, ExprKind, MatchSource};
+use rustc_hir::{Expr, ExprKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::hir::map::Map;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -42,7 +43,7 @@ declare_clippy_lint! {
declare_lint_pass!(IfLetMutex => [IF_LET_MUTEX]);
impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
- fn check_expr(&mut self, cx: &LateContext<'tcx>, ex: &'tcx Expr<'tcx>) {
+ fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let mut arm_visit = ArmVisitor {
mutex_lock_called: false,
found_mutex: None,
@@ -53,25 +54,23 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
found_mutex: None,
cx,
};
- if let ExprKind::Match(
- op,
- arms,
- MatchSource::IfLetDesugar {
- contains_else_clause: true,
- },
- ) = ex.kind
+ if let Some(higher::IfLet {
+ let_expr,
+ if_then,
+ if_else: Some(if_else),
+ ..
+ }) = higher::IfLet::hir(expr)
{
- op_visit.visit_expr(op);
+ op_visit.visit_expr(let_expr);
if op_visit.mutex_lock_called {
- for arm in arms {
- arm_visit.visit_arm(arm);
- }
+ arm_visit.visit_expr(if_then);
+ arm_visit.visit_expr(if_else);
if arm_visit.mutex_lock_called && arm_visit.same_mutex(cx, op_visit.found_mutex.unwrap()) {
span_lint_and_help(
cx,
IF_LET_MUTEX,
- ex.span,
+ expr.span,
"calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
None,
"move the lock call outside of the `if let ...` expression",
diff --git a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs
index 587307811a113..cd813c639dbbf 100644
--- a/src/tools/clippy/clippy_lints/src/if_let_some_result.rs
+++ b/src/tools/clippy/clippy_lints/src/if_let_some_result.rs
@@ -1,10 +1,11 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::method_chain_args;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::ty::is_type_diagnostic_item;
use if_chain::if_chain;
use rustc_errors::Applicability;
-use rustc_hir::{Expr, ExprKind, MatchSource, PatKind, QPath};
+use rustc_hir::{Expr, ExprKind, PatKind, QPath};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@@ -44,17 +45,17 @@ declare_lint_pass!(OkIfLet => [IF_LET_SOME_RESULT]);
impl<'tcx> LateLintPass<'tcx> for OkIfLet {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! { //begin checking variables
- if let ExprKind::Match(op, body, MatchSource::IfLetDesugar { .. }) = expr.kind; //test if expr is if let
- if let ExprKind::MethodCall(_, ok_span, result_types, _) = op.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
- if let PatKind::TupleStruct(QPath::Resolved(_, x), y, _) = body[0].pat.kind; //get operation
- if method_chain_args(op, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
+ if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr);
+ if let ExprKind::MethodCall(_, ok_span, ref result_types, _) = let_expr.kind; //check is expr.ok() has type Result<T,E>.ok(, _)
+ if let PatKind::TupleStruct(QPath::Resolved(_, ref x), ref y, _) = let_pat.kind; //get operation
+ if method_chain_args(let_expr, &["ok"]).is_some(); //test to see if using ok() methoduse std::marker::Sized;
if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&result_types[0]), sym::result_type);
if rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(x, false)) == "Some";
then {
let mut applicability = Applicability::MachineApplicable;
let some_expr_string = snippet_with_applicability(cx, y[0].span, "", &mut applicability);
- let trimmed_ok = snippet_with_applicability(cx, op.span.until(ok_span), "", &mut applicability);
+ let trimmed_ok = snippet_with_applicability(cx, let_expr.span.until(ok_span), "", &mut applicability);
let sugg = format!(
"if let Ok({}) = {}",
some_expr_string,
@@ -63,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for OkIfLet {
span_lint_and_sugg(
cx,
IF_LET_SOME_RESULT,
- expr.span.with_hi(op.span.hi()),
+ expr.span.with_hi(let_expr.span.hi()),
"matching on `Some` with `ok()` is redundant",
&format!("consider matching on `Ok({})` and removing the call to `ok` instead", some_expr_string),
sugg,
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index 17b9a2f888e0d..a2dac57454f2d 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -1,6 +1,6 @@
use clippy_utils::diagnostics::span_lint_and_help;
use clippy_utils::source::snippet_with_macro_callsite;
-use clippy_utils::{is_else_clause, is_lang_ctor, meets_msrv, msrvs};
+use clippy_utils::{higher, is_else_clause, is_lang_ctor, meets_msrv, msrvs};
use if_chain::if_chain;
use rustc_hir::LangItem::{OptionNone, OptionSome};
use rustc_hir::{Expr, ExprKind};
@@ -70,7 +70,7 @@ impl LateLintPass<'_> for IfThenSomeElseNone {
}
if_chain! {
- if let ExprKind::If(cond, then, Some(els)) = expr.kind;
+ if let Some(higher::If { cond, then, r#else: Some(els) }) = higher::If::hir(expr);
if let ExprKind::Block(then_block, _) = then.kind;
if let Some(then_expr) = then_block.expr;
if let ExprKind::Call(then_call, [then_arg]) = then_expr.kind;
diff --git a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
index 0a7d31dce2fd5..79d4d7ddcbced 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_saturating_sub.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::{in_macro, SpanlessEq};
use if_chain::if_chain;
use rustc_ast::ast::LitKind;
@@ -42,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitSaturatingSub {
return;
}
if_chain! {
- if let ExprKind::If(cond, then, None) = &expr.kind;
+ if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
// Check if the conditional expression is a binary operation
if let ExprKind::Binary(ref cond_op, cond_left, cond_right) = cond.kind;
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index 8c1f107330955..f52f090d3872e 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if let ExprKind::Index(array, index) = &expr.kind {
let ty = cx.typeck_results().expr_ty(array).peel_refs();
- if let Some(range) = higher::range(index) {
+ if let Some(range) = higher::Range::hir(index) {
// Ranged indexes, i.e., &x[n..m], &x[n..], &x[..n] and &x[..]
if let ty::Array(_, s) = ty.kind() {
let size: u128 = if let Some(size) = s.try_eval_usize(cx.tcx, cx.param_env) {
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index 2411a3175b919..58646385def52 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -172,7 +172,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
Finite
}
},
- ExprKind::Struct(..) => higher::range(expr).map_or(false, |r| r.end.is_none()).into(),
+ ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
_ => Finite,
}
}
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index 13f0d43cf8dd1..0594b73dd3837 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -64,7 +64,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
if let hir::StmtKind::Local(local) = stmt.kind;
if let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind;
if let hir::StmtKind::Expr(if_) = expr.kind;
- if let hir::ExprKind::If(cond, then, ref else_) = if_.kind;
+ if let hir::ExprKind::If(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
let mut used_visitor = LocalUsedVisitor::new(cx, canonical_id);
if !used_visitor.check_expr(cond);
if let hir::ExprKind::Block(then, _) = then.kind;
@@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
);
if has_interior_mutability { return; }
- let (default_multi_stmts, default) = if let Some(else_) = *else_ {
+ let (default_multi_stmts, default) = if let Some(else_) = else_ {
if let hir::ExprKind::Block(else_, _) = else_.kind {
if let Some(default) = check_assign(cx, canonical_id, else_) {
(else_.stmts.len() > 1, default)
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
index 64ff7574f86b7..9f2bc3c7ebae7 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_flatten.rs
@@ -1,11 +1,12 @@
use super::utils::make_iterator_snippet;
use super::MANUAL_FLATTEN;
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher;
use clippy_utils::{is_lang_ctor, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionSome, ResultOk};
-use rustc_hir::{Expr, ExprKind, MatchSource, Pat, PatKind, StmtKind};
+use rustc_hir::{Expr, ExprKind, Pat, PatKind, StmtKind};
use rustc_lint::LateContext;
use rustc_middle::ty;
use rustc_span::source_map::Span;
@@ -36,14 +37,12 @@ pub(super) fn check<'tcx>(
if_chain! {
if let Some(inner_expr) = inner_expr;
- if let ExprKind::Match(
- match_expr, match_arms, MatchSource::IfLetDesugar{ contains_else_clause: false }
- ) = inner_expr.kind;
+ if let Some(higher::IfLet { let_pat, let_expr, if_else: None, .. }) = higher::IfLet::hir(inner_expr);
// Ensure match_expr in `if let` statement is the same as the pat from the for-loop
if let PatKind::Binding(_, pat_hir_id, _, _) = pat.kind;
- if path_to_local_id(match_expr, pat_hir_id);
+ if path_to_local_id(let_expr, pat_hir_id);
// Ensure the `if let` statement is for the `Some` variant of `Option` or the `Ok` variant of `Result`
- if let PatKind::TupleStruct(ref qpath, _, _) = match_arms[0].pat.kind;
+ if let PatKind::TupleStruct(ref qpath, _, _) = let_pat.kind;
let some_ctor = is_lang_ctor(cx, qpath, OptionSome);
let ok_ctor = is_lang_ctor(cx, qpath, ResultOk);
if some_ctor || ok_ctor;
@@ -55,7 +54,7 @@ pub(super) fn check<'tcx>(
// Prepare the help message
let mut applicability = Applicability::MaybeIncorrect;
let arg_snippet = make_iterator_snippet(cx, arg, &mut applicability);
- let copied = match cx.typeck_results().expr_ty(match_expr).kind() {
+ let copied = match cx.typeck_results().expr_ty(let_expr).kind() {
ty::Ref(_, inner, _) => match inner.kind() {
ty::Ref(..) => ".copied()",
_ => ""
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
index a98e2dc1372db..2525b14e1c5c3 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
@@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
start: Some(start),
end: Some(end),
limits,
- }) = higher::range(arg)
+ }) = higher::Range::hir(arg)
{
// the var must be a single name
if let PatKind::Binding(_, canonical_id, _, _) = pat.kind {
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 7ca54d5397205..bd9de5e08d736 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -551,7 +551,7 @@ declare_lint_pass!(Loops => [
impl<'tcx> LateLintPass<'tcx> for Loops {
#[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let Some((pat, arg, body, span)) = higher::for_loop(expr) {
+ if let Some(higher::ForLoop { pat, arg, body, span }) = higher::ForLoop::hir(expr) {
// we don't want to check expanded macros
// this check is not at the top of the function
// since higher::for_loop expressions are marked as expansions
@@ -580,8 +580,8 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
while_let_on_iterator::check(cx, expr);
- if let Some((cond, body)) = higher::while_loop(expr) {
- while_immutable_condition::check(cx, cond, body);
+ if let Some(higher::While { if_cond, if_then, .. }) = higher::While::hir(&expr) {
+ while_immutable_condition::check(cx, if_cond, if_then);
}
needless_collect::check(expr, cx);
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index 1e54a1e2de165..344dc5074d369 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, arg: &Expr<'_>, body: &Expr<'_>) {
start: Some(start),
end: Some(end),
..
- }) = higher::range(arg)
+ }) = higher::Range::hir(arg)
{
let mut_ids = vec![check_for_mutability(cx, start), check_for_mutability(cx, end)];
if mut_ids[0].is_some() || mut_ids[1].is_some() {
diff --git a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
index 3810d0dcc051a..3f77e7af927ad 100644
--- a/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/needless_range_loop.rs
@@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
start: Some(start),
ref end,
limits,
- }) = higher::range(arg)
+ }) = higher::Range::hir(arg)
{
// the var must be a single name
if let PatKind::Binding(_, canonical_id, ident, _) = pat.kind {
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 6d9f6215ed418..2c46971d5f741 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -1,7 +1,7 @@
use super::utils::make_iterator_snippet;
use super::NEVER_LOOP;
use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::higher;
+use clippy_utils::higher::ForLoop;
use clippy_utils::source::snippet;
use rustc_errors::Applicability;
use rustc_hir::{Block, Expr, ExprKind, HirId, InlineAsmOperand, LoopSource, Node, Pat, Stmt, StmtKind};
@@ -16,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
if_chain! {
if let LoopSource::ForLoop = source;
if let Some((_, Node::Expr(parent_match))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
- if let Some((pat, iterator, _, for_span)) = higher::for_loop(parent_match);
+ if let Some(ForLoop { arg: iterator, pat, span: for_span, .. }) = ForLoop::hir(parent_match);
then {
// Suggests using an `if let` instead. This is `Unspecified` because the
// loop may (probably) contain `break` statements which would be invalid
@@ -111,6 +111,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
| ExprKind::Unary(_, e)
| ExprKind::Cast(e, _)
| ExprKind::Type(e, _)
+ | ExprKind::Let(_, e, _)
| ExprKind::Field(e, _)
| ExprKind::AddrOf(_, _, e)
| ExprKind::Struct(_, _, Some(e))
@@ -128,7 +129,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
// Break can come from the inner loop so remove them.
absorb_break(&never_loop_block(b, main_loop_id))
},
- ExprKind::If(e, e2, ref e3) => {
+ ExprKind::If(e, e2, e3) => {
let e1 = never_loop_expr(e, main_loop_id);
let e2 = never_loop_expr(e2, main_loop_id);
let e3 = e3
@@ -156,7 +157,7 @@ fn never_loop_expr(expr: &Expr<'_>, main_loop_id: HirId) -> NeverLoopResult {
NeverLoopResult::AlwaysBreak
}
},
- ExprKind::Break(_, ref e) | ExprKind::Ret(ref e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
+ ExprKind::Break(_, e) | ExprKind::Ret(e) => e.as_ref().map_or(NeverLoopResult::AlwaysBreak, |e| {
combine_seq(never_loop_expr(e, main_loop_id), NeverLoopResult::AlwaysBreak)
}),
ExprKind::InlineAsm(asm) => asm
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
index 9c1720798529d..6be410ca8e3cb 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
@@ -1,8 +1,9 @@
use super::WHILE_LET_LOOP;
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use rustc_errors::Applicability;
-use rustc_hir::{Block, Expr, ExprKind, MatchSource, StmtKind};
+use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
use rustc_lint::{LateContext, LintContext};
use rustc_middle::lint::in_external_macro;
@@ -11,41 +12,25 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'
let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
// or extract the first expression (if any) from the block
if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) {
- if let ExprKind::Match(matchexpr, arms, ref source) = inner.kind {
- // ensure "if let" compatible match structure
- match *source {
- MatchSource::Normal | MatchSource::IfLetDesugar { .. } => {
- if arms.len() == 2
- && arms[0].guard.is_none()
- && arms[1].guard.is_none()
- && is_simple_break_expr(arms[1].body)
- {
- if in_external_macro(cx.sess(), expr.span) {
- return;
- }
+ if let Some(higher::IfLet {
+ let_pat,
+ let_expr,
+ if_else: Some(if_else),
+ ..
+ }) = higher::IfLet::hir(inner)
+ {
+ if is_simple_break_expr(if_else) {
+ could_be_while_let(cx, expr, let_pat, let_expr);
+ }
+ }
- // NOTE: we used to build a body here instead of using
- // ellipsis, this was removed because:
- // 1) it was ugly with big bodies;
- // 2) it was not indented properly;
- // 3) it wasn’t very smart (see #675).
- let mut applicability = Applicability::HasPlaceholders;
- span_lint_and_sugg(
- cx,
- WHILE_LET_LOOP,
- expr.span,
- "this loop could be written as a `while let` loop",
- "try",
- format!(
- "while let {} = {} {{ .. }}",
- snippet_with_applicability(cx, arms[0].pat.span, "..", &mut applicability),
- snippet_with_applicability(cx, matchexpr.span, "..", &mut applicability),
- ),
- applicability,
- );
- }
- },
- _ => (),
+ if let ExprKind::Match(ref matchexpr, ref arms, MatchSource::Normal) = inner.kind {
+ if arms.len() == 2
+ && arms[0].guard.is_none()
+ && arms[1].guard.is_none()
+ && is_simple_break_expr(&arms[1].body)
+ {
+ could_be_while_let(cx, expr, &arms[0].pat, matchexpr);
}
}
}
@@ -54,14 +39,12 @@ pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'
/// If a block begins with a statement (possibly a `let` binding) and has an
/// expression, return it.
fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
- if block.stmts.is_empty() {
- return None;
- }
- if let StmtKind::Local(local) = block.stmts[0].kind {
- local.init //.map(|expr| expr)
- } else {
- None
+ if let Some(first_stmt) = block.stmts.get(0) {
+ if let StmtKind::Local(local) = first_stmt.kind {
+ return local.init;
+ }
}
+ None
}
/// If a block begins with an expression (with or without semicolon), return it.
@@ -86,3 +69,34 @@ fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
_ => false,
}
}
+
+fn could_be_while_let<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ let_pat: &'tcx Pat<'_>,
+ let_expr: &'tcx Expr<'_>,
+) {
+ if in_external_macro(cx.sess(), expr.span) {
+ return;
+ }
+
+ // NOTE: we used to build a body here instead of using
+ // ellipsis, this was removed because:
+ // 1) it was ugly with big bodies;
+ // 2) it was not indented properly;
+ // 3) it wasn’t very smart (see #675).
+ let mut applicability = Applicability::HasPlaceholders;
+ span_lint_and_sugg(
+ cx,
+ WHILE_LET_LOOP,
+ expr.span,
+ "this loop could be written as a `while let` loop",
+ "try",
+ format!(
+ "while let {} = {} {{ .. }}",
+ snippet_with_applicability(cx, let_pat.span, "..", &mut applicability),
+ snippet_with_applicability(cx, let_expr.span, "..", &mut applicability),
+ ),
+ applicability,
+ );
+}
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
index ef822e0cbe540..0757d329125cb 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -1,5 +1,6 @@
use super::WHILE_LET_ON_ITERATOR;
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::{
get_enclosing_loop_or_closure, is_refutable, is_trait_method, match_def_path, paths, visitors::is_res_used,
@@ -7,27 +8,31 @@ use clippy_utils::{
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor};
-use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, MatchSource, Mutability, Node, PatKind, QPath, UnOp};
+use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
use rustc_lint::LateContext;
use rustc_span::{symbol::sym, Span, Symbol};
pub(super) fn check(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
let (scrutinee_expr, iter_expr, some_pat, loop_expr) = if_chain! {
- if let ExprKind::Match(scrutinee_expr, [arm, _], MatchSource::WhileLetDesugar) = expr.kind;
+ if let Some(higher::WhileLet {
+ if_then,
+ let_pat,
+ let_expr,
+ ..
+ }) = higher::WhileLet::hir(expr);
// check for `Some(..)` pattern
- if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = arm.pat.kind;
+ if let PatKind::TupleStruct(QPath::Resolved(None, pat_path), some_pat, _) = let_pat.kind;
if let Res::Def(_, pat_did) = pat_path.res;
if match_def_path(cx, pat_did, &paths::OPTION_SOME);
// check for call to `Iterator::next`
- if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = scrutinee_expr.kind;
+ if let ExprKind::MethodCall(method_name, _, [iter_expr], _) = let_expr.kind;
if method_name.ident.name == sym::next;
- if is_trait_method(cx, scrutinee_expr, sym::Iterator);
- if let Some(iter_expr) = try_parse_iter_expr(cx, iter_expr);
+ if is_trait_method(cx, let_expr, sym::Iterator);
+ if let Some(iter_expr_struct) = try_parse_iter_expr(cx, iter_expr);
// get the loop containing the match expression
- if let Some((_, Node::Expr(loop_expr))) = cx.tcx.hir().parent_iter(expr.hir_id).nth(1);
- if !uses_iter(cx, &iter_expr, arm.body);
+ if !uses_iter(cx, &iter_expr_struct, if_then);
then {
- (scrutinee_expr, iter_expr, some_pat, loop_expr)
+ (let_expr, iter_expr_struct, some_pat, expr)
} else {
return;
}
@@ -81,6 +86,7 @@ struct IterExpr {
/// The path being used.
path: Res,
}
+
/// Parses any expression to find out which field of which variable is used. Will return `None` if
/// the expression might have side effects.
fn try_parse_iter_expr(cx: &LateContext<'_>, mut e: &Expr<'_>) -> Option<IterExpr> {
@@ -285,6 +291,7 @@ fn needs_mutable_borrow(cx: &LateContext<'tcx>, iter_expr: &IterExpr, loop_expr:
}
impl Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
type Map = ErasedMap<'tcx>;
+
fn nested_visit_map(&mut self) -> NestedVisitorMap<Self::Map> {
NestedVisitorMap::None
}
diff --git a/src/tools/clippy/clippy_lints/src/manual_map.rs b/src/tools/clippy/clippy_lints/src/manual_map.rs
index 7dec1595e0d10..53d97f775435a 100644
--- a/src/tools/clippy/clippy_lints/src/manual_map.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_map.rs
@@ -1,5 +1,6 @@
use crate::{map_unit_fn::OPTION_MAP_UNIT_FN, matches::MATCH_AS_REF};
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
use clippy_utils::ty::{is_type_diagnostic_item, peel_mid_ty_refs_is_mutable};
use clippy_utils::{
@@ -9,7 +10,7 @@ use clippy_utils::{
use rustc_ast::util::parser::PREC_POSTFIX;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, MatchSource, Mutability, Pat, PatKind};
+use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, HirId, Mutability, Pat, PatKind};
use rustc_lint::{LateContext, LateLintPass, LintContext};
use rustc_middle::lint::in_external_macro;
use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -43,155 +44,168 @@ declare_lint_pass!(ManualMap => [MANUAL_MAP]);
impl LateLintPass<'_> for ManualMap {
#[allow(clippy::too_many_lines)]
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Match(
- scrutinee,
- [arm1 @ Arm { guard: None, .. }, arm2 @ Arm { guard: None, .. }],
- match_kind,
- ) = expr.kind
+ if let Some(higher::IfLet {
+ let_pat,
+ let_expr,
+ if_then,
+ if_else: Some(if_else),
+ }) = higher::IfLet::hir(expr)
{
- if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
- return;
- }
+ manage_lint(cx, expr, (&let_pat.kind, if_then), (&PatKind::Wild, if_else), let_expr);
+ }
- let (scrutinee_ty, ty_ref_count, ty_mutability) =
- peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrutinee));
- if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
- && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
- {
- return;
- }
+ if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) =
+ expr.kind
+ {
+ manage_lint(
+ cx,
+ expr,
+ (&then.pat.kind, then.body),
+ (&r#else.pat.kind, r#else.body),
+ scrutinee,
+ );
+ }
+ }
+}
- let expr_ctxt = expr.span.ctxt();
- let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
- try_parse_pattern(cx, arm1.pat, expr_ctxt),
- try_parse_pattern(cx, arm2.pat, expr_ctxt),
- ) {
- (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count }))
- if is_none_expr(cx, arm1.body) =>
- {
- (arm2.body, pattern, ref_count, true)
- },
- (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count }))
- if is_none_expr(cx, arm1.body) =>
- {
- (arm2.body, pattern, ref_count, false)
- },
- (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild))
- if is_none_expr(cx, arm2.body) =>
- {
- (arm1.body, pattern, ref_count, true)
- },
- (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None))
- if is_none_expr(cx, arm2.body) =>
- {
- (arm1.body, pattern, ref_count, false)
- },
- _ => return,
- };
+fn manage_lint<'tcx>(
+ cx: &LateContext<'tcx>,
+ expr: &'tcx Expr<'_>,
+ then: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
+ r#else: (&'tcx PatKind<'_>, &'tcx Expr<'_>),
+ scrut: &'tcx Expr<'_>,
+) {
+ if in_external_macro(cx.sess(), expr.span) || in_constant(cx, expr.hir_id) {
+ return;
+ }
- // Top level or patterns aren't allowed in closures.
- if matches!(some_pat.kind, PatKind::Or(_)) {
- return;
- }
+ let (scrutinee_ty, ty_ref_count, ty_mutability) = peel_mid_ty_refs_is_mutable(cx.typeck_results().expr_ty(scrut));
+ if !(is_type_diagnostic_item(cx, scrutinee_ty, sym::option_type)
+ && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(expr), sym::option_type))
+ {
+ return;
+ }
- let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
- Some(expr) => expr,
- None => return,
- };
+ let (then_pat, then_expr) = then;
+ let (else_pat, else_expr) = r#else;
- // These two lints will go back and forth with each other.
- if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit
- && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id)
- {
- return;
- }
+ let expr_ctxt = expr.span.ctxt();
+ let (some_expr, some_pat, pat_ref_count, is_wild_none) = match (
+ try_parse_pattern(cx, then_pat, expr_ctxt),
+ try_parse_pattern(cx, else_pat, expr_ctxt),
+ ) {
+ (Some(OptionPat::Wild), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
+ (else_expr, pattern, ref_count, true)
+ },
+ (Some(OptionPat::None), Some(OptionPat::Some { pattern, ref_count })) if is_none_expr(cx, then_expr) => {
+ (else_expr, pattern, ref_count, false)
+ },
+ (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::Wild)) if is_none_expr(cx, else_expr) => {
+ (then_expr, pattern, ref_count, true)
+ },
+ (Some(OptionPat::Some { pattern, ref_count }), Some(OptionPat::None)) if is_none_expr(cx, else_expr) => {
+ (then_expr, pattern, ref_count, false)
+ },
+ _ => return,
+ };
- // `map` won't perform any adjustments.
- if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
- return;
- }
+ // Top level or patterns aren't allowed in closures.
+ if matches!(some_pat.kind, PatKind::Or(_)) {
+ return;
+ }
- if !can_move_expr_to_closure(cx, some_expr) {
- return;
- }
+ let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
+ Some(expr) => expr,
+ None => return,
+ };
- // Determine which binding mode to use.
- let explicit_ref = some_pat.contains_explicit_ref_binding();
- let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
+ if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) {
+ return;
+ }
- let as_ref_str = match binding_ref {
- Some(Mutability::Mut) => ".as_mut()",
- Some(Mutability::Not) => ".as_ref()",
- None => "",
- };
+ // `map` won't perform any adjustments.
+ if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
+ return;
+ }
- let mut app = Applicability::MachineApplicable;
+ if !can_move_expr_to_closure(cx, some_expr) {
+ return;
+ }
- // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
- // it's being passed by value.
- let scrutinee = peel_hir_expr_refs(scrutinee).0;
- let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
- let scrutinee_str =
- if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
- format!("({})", scrutinee_str)
- } else {
- scrutinee_str.into()
- };
+ // Determine which binding mode to use.
+ let explicit_ref = some_pat.contains_explicit_ref_binding();
+ let binding_ref = explicit_ref.or_else(|| (ty_ref_count != pat_ref_count).then(|| ty_mutability));
+
+ let as_ref_str = match binding_ref {
+ Some(Mutability::Mut) => ".as_mut()",
+ Some(Mutability::Not) => ".as_ref()",
+ None => "",
+ };
+
+ let mut app = Applicability::MachineApplicable;
- let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
- match can_pass_as_func(cx, id, some_expr) {
- Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
- snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
- },
- _ => {
- if path_to_local_id(some_expr, id)
- && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
- && binding_ref.is_some()
- {
- return;
- }
+ // Remove address-of expressions from the scrutinee. Either `as_ref` will be called, or
+ // it's being passed by value.
+ let scrutinee = peel_hir_expr_refs(scrut).0;
+ let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
+ let scrutinee_str = if scrutinee.span.ctxt() == expr.span.ctxt() && scrutinee.precedence().order() < PREC_POSTFIX {
+ format!("({})", scrutinee_str)
+ } else {
+ scrutinee_str.into()
+ };
- // `ref` and `ref mut` annotations were handled earlier.
- let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
- "mut "
- } else {
- ""
- };
- format!(
- "|{}{}| {}",
- annotation,
- some_binding,
- snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
- )
- },
+ let body_str = if let PatKind::Binding(annotation, id, some_binding, None) = some_pat.kind {
+ match can_pass_as_func(cx, id, some_expr) {
+ Some(func) if func.span.ctxt() == some_expr.span.ctxt() => {
+ snippet_with_applicability(cx, func.span, "..", &mut app).into_owned()
+ },
+ _ => {
+ if path_to_local_id(some_expr, id)
+ && !is_lint_allowed(cx, MATCH_AS_REF, expr.hir_id)
+ && binding_ref.is_some()
+ {
+ return;
}
- } else if !is_wild_none && explicit_ref.is_none() {
- // TODO: handle explicit reference annotations.
+
+ // `ref` and `ref mut` annotations were handled earlier.
+ let annotation = if matches!(annotation, BindingAnnotation::Mutable) {
+ "mut "
+ } else {
+ ""
+ };
format!(
- "|{}| {}",
- snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
+ "|{}{}| {}",
+ annotation,
+ some_binding,
snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
)
- } else {
- // Refutable bindings and mixed reference annotations can't be handled by `map`.
- return;
- };
-
- span_lint_and_sugg(
- cx,
- MANUAL_MAP,
- expr.span,
- "manual implementation of `Option::map`",
- "try this",
- if matches!(match_kind, MatchSource::IfLetDesugar { .. }) && is_else_clause(cx.tcx, expr) {
- format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
- } else {
- format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
- },
- app,
- );
+ },
}
- }
+ } else if !is_wild_none && explicit_ref.is_none() {
+ // TODO: handle explicit reference annotations.
+ format!(
+ "|{}| {}",
+ snippet_with_context(cx, some_pat.span, expr_ctxt, "..", &mut app).0,
+ snippet_with_context(cx, some_expr.span, expr_ctxt, "..", &mut app).0
+ )
+ } else {
+ // Refutable bindings and mixed reference annotations can't be handled by `map`.
+ return;
+ };
+
+ span_lint_and_sugg(
+ cx,
+ MANUAL_MAP,
+ expr.span,
+ "manual implementation of `Option::map`",
+ "try this",
+ if is_else_clause(cx.tcx, expr) {
+ format!("{{ {}{}.map({}) }}", scrutinee_str, as_ref_str, body_str)
+ } else {
+ format!("{}{}.map({})", scrutinee_str, as_ref_str, body_str)
+ },
+ app,
+ );
}
// Checks whether the expression could be passed as a function, or whether a closure is needed.
@@ -199,7 +213,7 @@ impl LateLintPass<'_> for ManualMap {
fn can_pass_as_func(cx: &LateContext<'tcx>, binding: HirId, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
match expr.kind {
ExprKind::Call(func, [arg])
- if path_to_local_id(arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
+ if path_to_local_id (arg, binding) && cx.typeck_results().expr_adjustments(arg).is_empty() =>
{
Some(func)
},
@@ -221,21 +235,28 @@ enum OptionPat<'a> {
// Try to parse into a recognized `Option` pattern.
// i.e. `_`, `None`, `Some(..)`, or a reference to any of those.
-fn try_parse_pattern(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
- fn f(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, ref_count: usize, ctxt: SyntaxContext) -> Option<OptionPat<'tcx>> {
- match pat.kind {
+fn try_parse_pattern(
+ cx: &LateContext<'tcx>,
+ pat_kind: &'tcx PatKind<'_>,
+ ctxt: SyntaxContext,
+) -> Option<OptionPat<'tcx>> {
+ fn f(
+ cx: &LateContext<'tcx>,
+ pat_kind: &'tcx PatKind<'_>,
+ ref_count: usize,
+ ctxt: SyntaxContext,
+ ) -> Option<OptionPat<'tcx>> {
+ match pat_kind {
PatKind::Wild => Some(OptionPat::Wild),
- PatKind::Ref(pat, _) => f(cx, pat, ref_count + 1, ctxt),
+ PatKind::Ref(ref_pat, _) => f(cx, &ref_pat.kind, ref_count + 1, ctxt),
PatKind::Path(ref qpath) if is_lang_ctor(cx, qpath, OptionNone) => Some(OptionPat::None),
- PatKind::TupleStruct(ref qpath, [pattern], _)
- if is_lang_ctor(cx, qpath, OptionSome) && pat.span.ctxt() == ctxt =>
- {
+ PatKind::TupleStruct(ref qpath, [pattern], _) if is_lang_ctor(cx, qpath, OptionSome) => {
Some(OptionPat::Some { pattern, ref_count })
},
_ => None,
}
}
- f(cx, pat, 0, ctxt)
+ f(cx, pat_kind, 0, ctxt)
}
// Checks for an expression wrapped by the `Some` constructor. Returns the contained expression.
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index db12c377488b0..4e040508b6bfb 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
}
if_chain! {
- if let ExprKind::If(cond, then, _) = &expr.kind;
+ if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr);
if let ExprKind::MethodCall(_, _, [target_arg, pattern], _) = cond.kind;
if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id);
if let ExprKind::Path(target_path) = &target_arg.kind;
@@ -212,7 +212,7 @@ fn find_stripping<'tcx>(
if is_ref_str(self.cx, ex);
let unref = peel_ref(ex);
if let ExprKind::Index(indexed, index) = &unref.kind;
- if let Some(higher::Range { start, end, .. }) = higher::range(index);
+ if let Some(higher::Range { start, end, .. }) = higher::Range::hir(index);
if let ExprKind::Path(path) = &indexed.kind;
if self.cx.qpath_res(path, ex.hir_id) == self.target;
then {
diff --git a/src/tools/clippy/clippy_lints/src/matches.rs b/src/tools/clippy/clippy_lints/src/matches.rs
index 5360c02f90539..a183d0c66e8ce 100644
--- a/src/tools/clippy/clippy_lints/src/matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches.rs
@@ -2,6 +2,7 @@ use clippy_utils::consts::{constant, miri_to_const, Constant};
use clippy_utils::diagnostics::{
multispan_sugg, span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then,
};
+use clippy_utils::higher;
use clippy_utils::source::{expr_block, indent_of, snippet, snippet_block, snippet_opt, snippet_with_applicability};
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, match_type, peel_mid_ty_refs};
@@ -12,8 +13,10 @@ use clippy_utils::{
strip_pat_refs,
};
use clippy_utils::{paths, search_same, SpanlessEq, SpanlessHash};
+use core::array;
+use core::iter::{once, ExactSizeIterator};
use if_chain::if_chain;
-use rustc_ast::ast::LitKind;
+use rustc_ast::ast::{Attribute, LitKind};
use rustc_errors::Applicability;
use rustc_hir::def::{CtorKind, DefKind, Res};
use rustc_hir::LangItem::{OptionNone, OptionSome};
@@ -628,8 +631,11 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
check_match_single_binding(cx, ex, arms, expr);
}
}
- if let ExprKind::Match(ex, arms, _) = expr.kind {
- check_match_ref_pats(cx, ex, arms, expr);
+ if let ExprKind::Match(ref ex, ref arms, _) = expr.kind {
+ check_match_ref_pats(cx, ex, arms.iter().map(|el| el.pat), expr);
+ }
+ if let Some(higher::IfLet { let_pat, let_expr, .. }) = higher::IfLet::hir(expr) {
+ check_match_ref_pats(cx, let_expr, once(let_pat), expr);
}
}
@@ -1179,39 +1185,40 @@ fn is_panic_block(block: &Block<'_>) -> bool {
}
}
-fn check_match_ref_pats(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
- if has_only_ref_pats(arms) {
- let mut suggs = Vec::with_capacity(arms.len() + 1);
- let (title, msg) = if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = ex.kind {
- let span = ex.span.source_callsite();
- suggs.push((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
- (
- "you don't need to add `&` to both the expression and the patterns",
- "try",
- )
- } else {
- let span = ex.span.source_callsite();
- suggs.push((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
- (
- "you don't need to add `&` to all patterns",
- "instead of prefixing all patterns with `&`, you can dereference the expression",
- )
- };
-
- suggs.extend(arms.iter().filter_map(|a| {
- if let PatKind::Ref(refp, _) = a.pat.kind {
- Some((a.pat.span, snippet(cx, refp.span, "..").to_string()))
- } else {
- None
- }
- }));
+fn check_match_ref_pats<'a, 'b, I>(cx: &LateContext<'_>, ex: &Expr<'_>, pats: I, expr: &Expr<'_>)
+where
+ 'b: 'a,
+ I: Clone + Iterator<Item = &'a Pat<'b>>,
+{
+ if !has_only_ref_pats(pats.clone()) {
+ return;
+ }
- span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
- if !expr.span.from_expansion() {
- multispan_sugg(diag, msg, suggs);
- }
- });
+ let (first_sugg, msg, title);
+ let span = ex.span.source_callsite();
+ if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, ref inner) = ex.kind {
+ first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, inner, "..").to_string()));
+ msg = "try";
+ title = "you don't need to add `&` to both the expression and the patterns";
+ } else {
+ first_sugg = once((span, Sugg::hir_with_macro_callsite(cx, ex, "..").deref().to_string()));
+ msg = "instead of prefixing all patterns with `&`, you can dereference the expression";
+ title = "you don't need to add `&` to all patterns";
}
+
+ let remaining_suggs = pats.filter_map(|pat| {
+ if let PatKind::Ref(ref refp, _) = pat.kind {
+ Some((pat.span, snippet(cx, refp.span, "..").to_string()))
+ } else {
+ None
+ }
+ });
+
+ span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
+ if !expr.span.from_expansion() {
+ multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
+ }
+ });
}
fn check_match_as_ref(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
@@ -1286,46 +1293,99 @@ fn check_wild_in_or_pats(cx: &LateContext<'_>, arms: &[Arm<'_>]) {
/// Lint a `match` or `if let .. { .. } else { .. }` expr that could be replaced by `matches!`
fn check_match_like_matches<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
- if let ExprKind::Match(ex, arms, ref match_source) = &expr.kind {
- match match_source {
- MatchSource::Normal => find_matches_sugg(cx, ex, arms, expr, false),
- MatchSource::IfLetDesugar { .. } => find_matches_sugg(cx, ex, arms, expr, true),
- _ => false,
- }
- } else {
- false
+ if let Some(higher::IfLet {
+ let_pat,
+ let_expr,
+ if_then,
+ if_else: Some(if_else),
+ }) = higher::IfLet::hir(expr)
+ {
+ return find_matches_sugg(
+ cx,
+ let_expr,
+ array::IntoIter::new([(&[][..], Some(let_pat), if_then, None), (&[][..], None, if_else, None)]),
+ expr,
+ true,
+ );
}
+
+ if let ExprKind::Match(scrut, arms, MatchSource::Normal) = expr.kind {
+ return find_matches_sugg(
+ cx,
+ scrut,
+ arms.iter().map(|arm| {
+ (
+ cx.tcx.hir().attrs(arm.hir_id),
+ Some(arm.pat),
+ arm.body,
+ arm.guard.as_ref(),
+ )
+ }),
+ expr,
+ false,
+ );
+ }
+
+ false
}
-/// Lint a `match` or desugared `if let` for replacement by `matches!`
-fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>, desugared: bool) -> bool {
+/// Lint a `match` or `if let` for replacement by `matches!`
+fn find_matches_sugg<'a, 'b, I>(
+ cx: &LateContext<'_>,
+ ex: &Expr<'_>,
+ mut iter: I,
+ expr: &Expr<'_>,
+ is_if_let: bool,
+) -> bool
+where
+ 'b: 'a,
+ I: Clone
+ + DoubleEndedIterator
+ + ExactSizeIterator
+ + Iterator<
+ Item = (
+ &'a [Attribute],
+ Option<&'a Pat<'b>>,
+ &'a Expr<'b>,
+ Option<&'a Guard<'b>>,
+ ),
+ >,
+{
if_chain! {
- if arms.len() >= 2;
+ if iter.len() >= 2;
if cx.typeck_results().expr_ty(expr).is_bool();
- if let Some((b1_arm, b0_arms)) = arms.split_last();
- if let Some(b0) = find_bool_lit(&b0_arms[0].body.kind, desugared);
- if let Some(b1) = find_bool_lit(&b1_arm.body.kind, desugared);
- if is_wild(b1_arm.pat);
+ if let Some((_, last_pat_opt, last_expr, _)) = iter.next_back();
+ let iter_without_last = iter.clone();
+ if let Some((first_attrs, _, first_expr, first_guard)) = iter.next();
+ if let Some(b0) = find_bool_lit(&first_expr.kind, is_if_let);
+ if let Some(b1) = find_bool_lit(&last_expr.kind, is_if_let);
if b0 != b1;
- let if_guard = &b0_arms[0].guard;
- if if_guard.is_none() || b0_arms.len() == 1;
- if cx.tcx.hir().attrs(b0_arms[0].hir_id).is_empty();
- if b0_arms[1..].iter()
+ if first_guard.is_none() || iter.len() == 0;
+ if first_attrs.is_empty();
+ if iter
.all(|arm| {
- find_bool_lit(&arm.body.kind, desugared).map_or(false, |b| b == b0) &&
- arm.guard.is_none() && cx.tcx.hir().attrs(arm.hir_id).is_empty()
+ find_bool_lit(&arm.2.kind, is_if_let).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty()
});
then {
+ if let Some(ref last_pat) = last_pat_opt {
+ if !is_wild(last_pat) {
+ return false;
+ }
+ }
+
// The suggestion may be incorrect, because some arms can have `cfg` attributes
// evaluated into `false` and so such arms will be stripped before.
let mut applicability = Applicability::MaybeIncorrect;
let pat = {
use itertools::Itertools as _;
- b0_arms.iter()
- .map(|arm| snippet_with_applicability(cx, arm.pat.span, "..", &mut applicability))
+ iter_without_last
+ .filter_map(|arm| {
+ let pat_span = arm.1?.span;
+ Some(snippet_with_applicability(cx, pat_span, "..", &mut applicability))
+ })
.join(" | ")
};
- let pat_and_guard = if let Some(Guard::If(g)) = if_guard {
+ let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
format!("{} if {}", pat, snippet_with_applicability(cx, g.span, "..", &mut applicability))
} else {
pat
@@ -1342,7 +1402,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
cx,
MATCH_LIKE_MATCHES_MACRO,
expr.span,
- &format!("{} expression looks like `matches!` macro", if desugared { "if let .. else" } else { "match" }),
+ &format!("{} expression looks like `matches!` macro", if is_if_let { "if let .. else" } else { "match" }),
"try this",
format!(
"{}matches!({}, {})",
@@ -1360,7 +1420,7 @@ fn find_matches_sugg(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr
}
/// Extract a `bool` or `{ bool }`
-fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
+fn find_bool_lit(ex: &ExprKind<'_>, is_if_let: bool) -> Option<bool> {
match ex {
ExprKind::Lit(Spanned {
node: LitKind::Bool(b), ..
@@ -1372,7 +1432,7 @@ fn find_bool_lit(ex: &ExprKind<'_>, desugared: bool) -> Option<bool> {
..
},
_,
- ) if desugared => {
+ ) if is_if_let => {
if let ExprKind::Lit(Spanned {
node: LitKind::Bool(b), ..
}) = exp.kind
@@ -1644,19 +1704,26 @@ fn is_ref_some_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> Option<BindingAnnotat
None
}
-fn has_only_ref_pats(arms: &[Arm<'_>]) -> bool {
- let mapped = arms
- .iter()
- .map(|a| {
- match a.pat.kind {
- PatKind::Ref(..) => Some(true), // &-patterns
- PatKind::Wild => Some(false), // an "anything" wildcard is also fine
- _ => None, // any other pattern is not fine
+fn has_only_ref_pats<'a, 'b, I>(pats: I) -> bool
+where
+ 'b: 'a,
+ I: Iterator<Item = &'a Pat<'b>>,
+{
+ let mut at_least_one_is_true = false;
+ for opt in pats.map(|pat| match pat.kind {
+ PatKind::Ref(..) => Some(true), // &-patterns
+ PatKind::Wild => Some(false), // an "anything" wildcard is also fine
+ _ => None, // any other pattern is not fine
+ }) {
+ if let Some(inner) = opt {
+ if inner {
+ at_least_one_is_true = true;
}
- })
- .collect::<Option<Vec<bool>>>();
- // look for Some(v) where there's at least one true element
- mapped.map_or(false, |v| v.iter().any(|el| *el))
+ } else {
+ return false;
+ }
+ }
+ at_least_one_is_true
}
pub fn overlapping<T>(ranges: &[SpannedRange<T>]) -> Option<(&SpannedRange<T>, &SpannedRange<T>)>
@@ -1745,6 +1812,7 @@ where
mod redundant_pattern_match {
use super::REDUNDANT_PATTERN_MATCHING;
use clippy_utils::diagnostics::span_lint_and_then;
+ use clippy_utils::higher;
use clippy_utils::source::{snippet, snippet_with_applicability};
use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
@@ -1755,22 +1823,27 @@ mod redundant_pattern_match {
use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
use rustc_hir::{
intravisit::{walk_expr, ErasedMap, NestedVisitorMap, Visitor},
- Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, PatKind, QPath,
+ Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath,
};
use rustc_lint::LateContext;
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
use rustc_span::sym;
pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Match(op, arms, ref match_source) = &expr.kind {
- match match_source {
- MatchSource::Normal => find_sugg_for_match(cx, expr, op, arms),
- MatchSource::IfLetDesugar { contains_else_clause } => {
- find_sugg_for_if_let(cx, expr, op, &arms[0], "if", *contains_else_clause);
- },
- MatchSource::WhileLetDesugar => find_sugg_for_if_let(cx, expr, op, &arms[0], "while", false),
- _ => {},
- }
+ if let Some(higher::IfLet {
+ if_else,
+ let_pat,
+ let_expr,
+ ..
+ }) = higher::IfLet::ast(cx, expr)
+ {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "if", if_else.is_some())
+ }
+ if let ExprKind::Match(op, arms, MatchSource::Normal) = &expr.kind {
+ find_sugg_for_match(cx, expr, op, arms)
+ }
+ if let Some(higher::WhileLet { let_pat, let_expr, .. }) = higher::WhileLet::hir(expr) {
+ find_sugg_for_if_let(cx, expr, let_pat, let_expr, "while", false)
}
}
@@ -1924,18 +1997,18 @@ mod redundant_pattern_match {
fn find_sugg_for_if_let<'tcx>(
cx: &LateContext<'tcx>,
expr: &'tcx Expr<'_>,
- op: &'tcx Expr<'tcx>,
- arm: &Arm<'_>,
+ let_pat: &Pat<'_>,
+ let_expr: &'tcx Expr<'_>,
keyword: &'static str,
has_else: bool,
) {
// also look inside refs
- let mut kind = &arm.pat.kind;
+ let mut kind = &let_pat.kind;
// if we have &None for example, peel it so we can detect "if let None = x"
if let PatKind::Ref(inner, _mutability) = kind {
kind = &inner.kind;
}
- let op_ty = cx.typeck_results().expr_ty(op);
+ let op_ty = cx.typeck_results().expr_ty(let_expr);
// Determine which function should be used, and the type contained by the corresponding
// variant.
let (good_method, inner_ty) = match kind {
@@ -1989,38 +2062,38 @@ mod redundant_pattern_match {
// scrutinee would be, so they have to be considered as well.
// e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
// for the duration if body.
- let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, op);
+ let needs_drop = type_needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
// check that `while_let_on_iterator` lint does not trigger
if_chain! {
if keyword == "while";
- if let ExprKind::MethodCall(method_path, _, _, _) = op.kind;
+ if let ExprKind::MethodCall(method_path, _, _, _) = let_expr.kind;
if method_path.ident.name == sym::next;
- if is_trait_method(cx, op, sym::Iterator);
+ if is_trait_method(cx, let_expr, sym::Iterator);
then {
return;
}
}
- let result_expr = match &op.kind {
+ let result_expr = match &let_expr.kind {
ExprKind::AddrOf(_, _, borrowed) => borrowed,
- _ => op,
+ _ => let_expr,
};
span_lint_and_then(
cx,
REDUNDANT_PATTERN_MATCHING,
- arm.pat.span,
+ let_pat.span,
&format!("redundant pattern matching, consider using `{}`", good_method),
|diag| {
- // while let ... = ... { ... }
+ // if/while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^
let expr_span = expr.span;
- // while let ... = ... { ... }
+ // if/while let ... = ... { ... }
// ^^^
let op_span = result_expr.span.source_callsite();
- // while let ... = ... { ... }
+ // if/while let ... = ... { ... }
// ^^^^^^^^^^^^^^^^^^^
let span = expr_span.until(op_span.shrink_to_hi());
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
index a49851de38e1e..6954da67e32c0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_next_slice.rs
@@ -18,7 +18,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
// since it is already covered by `&loops::ITER_NEXT_LOOP`
let mut parent_expr_opt = get_parent_expr(cx, expr);
while let Some(parent_expr) = parent_expr_opt {
- if higher::for_loop(parent_expr).is_some() {
+ if higher::ForLoop::hir(parent_expr).is_some() {
return;
}
parent_expr_opt = get_parent_expr(cx, parent_expr);
@@ -29,7 +29,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, cal
if_chain! {
if let hir::ExprKind::Index(caller_var, index_expr) = &caller_expr.kind;
if let Some(higher::Range { start: Some(start_expr), end: None, limits: ast::RangeLimits::HalfOpen })
- = higher::range(index_expr);
+ = higher::Range::hir(index_expr);
if let hir::ExprKind::Lit(ref start_lit) = &start_expr.kind;
if let ast::LitKind::Int(start_idx, _) = start_lit.node;
then {
diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs
index d5032c5ba7f29..610152a217f1e 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs
@@ -53,7 +53,7 @@ impl<'a, 'tcx> intravisit::Visitor<'tcx> for MutVisitor<'a, 'tcx> {
return;
}
- if let Some((_, arg, body, _)) = higher::for_loop(expr) {
+ if let Some(higher::ForLoop { arg, body, .. }) = higher::ForLoop::hir(expr) {
// A `for` loop lowers to:
// ```rust
// match ::std::iter::Iterator::next(&mut iter) {
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index 36f2829a5b94e..c9dd94400efb9 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -3,6 +3,7 @@
//! This lint is **warn** by default
use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::higher;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
use clippy_utils::{is_else_clause, is_expn_of};
@@ -77,10 +78,15 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
if e.span.from_expansion() {
return;
}
- if let ExprKind::If(pred, then_block, Some(else_expr)) = e.kind {
+ if let Some(higher::If {
+ cond,
+ then,
+ r#else: Some(r#else),
+ }) = higher::If::hir(e)
+ {
let reduce = |ret, not| {
let mut applicability = Applicability::MachineApplicable;
- let snip = Sugg::hir_with_applicability(cx, pred, "<predicate>", &mut applicability);
+ let snip = Sugg::hir_with_applicability(cx, cond, "<predicate>", &mut applicability);
let mut snip = if not { !snip } else { snip };
if ret {
@@ -101,8 +107,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBool {
applicability,
);
};
- if let ExprKind::Block(then_block, _) = then_block.kind {
- match (fetch_bool_block(then_block), fetch_bool_expr(else_expr)) {
+ if let ExprKind::Block(then, _) = then.kind {
+ match (fetch_bool_block(then), fetch_bool_expr(r#else)) {
(RetBool(true), RetBool(true)) | (Bool(true), Bool(true)) => {
span_lint(
cx,
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index f6254aa715a45..ac21eb5275f0f 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -1,7 +1,6 @@
use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
use rustc_ast::ast::{
- Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat,
- PatKind,
+ Arm, AssocItem, AssocItemKind, Attribute, Block, FnDecl, FnKind, Item, ItemKind, Local, Pat, PatKind,
};
use rustc_ast::visit::{walk_block, walk_expr, walk_pat, Visitor};
use rustc_lint::{EarlyContext, EarlyLintPass};
diff --git a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
index 7aef3a5f34cf2..d0b0bad5eb1cc 100644
--- a/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/option_if_let_else.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::sugg::Sugg;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::usage::contains_return_break_continue_macro;
@@ -6,7 +7,7 @@ use clippy_utils::{eager_or_lazy, in_macro, is_else_clause, is_lang_ctor};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::OptionSome;
-use rustc_hir::{Arm, BindingAnnotation, Block, Expr, ExprKind, MatchSource, Mutability, PatKind, UnOp};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, Mutability, PatKind, UnOp};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@@ -84,20 +85,20 @@ struct OptionIfLetElseOccurence {
/// Extracts the body of a given arm. If the arm contains only an expression,
/// then it returns the expression. Otherwise, it returns the entire block
-fn extract_body_from_arm<'a>(arm: &'a Arm<'a>) -> Option<&'a Expr<'a>> {
+fn extract_body_from_expr<'a>(expr: &'a Expr<'a>) -> Option<&'a Expr<'a>> {
if let ExprKind::Block(
Block {
- stmts: statements,
- expr: Some(expr),
+ stmts: block_stmts,
+ expr: Some(block_expr),
..
},
_,
- ) = &arm.body.kind
+ ) = expr.kind
{
- if let [] = statements {
- Some(expr)
+ if let [] = block_stmts {
+ Some(block_expr)
} else {
- Some(arm.body)
+ Some(expr)
}
} else {
None
@@ -121,37 +122,33 @@ fn format_option_in_sugg(cx: &LateContext<'_>, cond_expr: &Expr<'_>, as_ref: boo
/// If this expression is the option if let/else construct we're detecting, then
/// this function returns an `OptionIfLetElseOccurence` struct with details if
/// this construct is found, or None if this construct is not found.
-fn detect_option_if_let_else<'tcx>(
- cx: &'_ LateContext<'tcx>,
- expr: &'_ Expr<'tcx>,
-) -> Option<OptionIfLetElseOccurence> {
+fn detect_option_if_let_else<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<OptionIfLetElseOccurence> {
if_chain! {
if !in_macro(expr.span); // Don't lint macros, because it behaves weirdly
- if let ExprKind::Match(cond_expr, arms, MatchSource::IfLetDesugar{contains_else_clause: true}) = &expr.kind;
+ if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr);
if !is_else_clause(cx.tcx, expr);
- if arms.len() == 2;
- if !is_result_ok(cx, cond_expr); // Don't lint on Result::ok because a different lint does it already
- if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &arms[0].pat.kind;
+ if !is_result_ok(cx, let_expr); // Don't lint on Result::ok because a different lint does it already
+ if let PatKind::TupleStruct(struct_qpath, [inner_pat], _) = &let_pat.kind;
if is_lang_ctor(cx, struct_qpath, OptionSome);
if let PatKind::Binding(bind_annotation, _, id, _) = &inner_pat.kind;
- if !contains_return_break_continue_macro(arms[0].body);
- if !contains_return_break_continue_macro(arms[1].body);
+ if !contains_return_break_continue_macro(if_then);
+ if !contains_return_break_continue_macro(if_else);
then {
let capture_mut = if bind_annotation == &BindingAnnotation::Mutable { "mut " } else { "" };
- let some_body = extract_body_from_arm(&arms[0])?;
- let none_body = extract_body_from_arm(&arms[1])?;
+ let some_body = extract_body_from_expr(if_then)?;
+ let none_body = extract_body_from_expr(if_else)?;
let method_sugg = if eager_or_lazy::is_eagerness_candidate(cx, none_body) { "map_or" } else { "map_or_else" };
let capture_name = id.name.to_ident_string();
- let (as_ref, as_mut) = match &cond_expr.kind {
+ let (as_ref, as_mut) = match &let_expr.kind {
ExprKind::AddrOf(_, Mutability::Not, _) => (true, false),
ExprKind::AddrOf(_, Mutability::Mut, _) => (false, true),
_ => (bind_annotation == &BindingAnnotation::Ref, bind_annotation == &BindingAnnotation::RefMut),
};
- let cond_expr = match &cond_expr.kind {
+ let cond_expr = match let_expr.kind {
// Pointer dereferencing happens automatically, so we can omit it in the suggestion
ExprKind::Unary(UnOp::Deref, expr) | ExprKind::AddrOf(_, _, expr) => expr,
- _ => cond_expr,
+ _ => let_expr,
};
Some(OptionIfLetElseOccurence {
option: format_option_in_sugg(cx, cond_expr, as_ref, as_mut),
diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
index 4534f6e251659..35cff4141a903 100644
--- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
@@ -104,22 +104,25 @@ impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
}
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
- if let ExprKind::Match(expr, arms, source) = expr.kind {
- match source {
- MatchSource::Normal | MatchSource::IfLetDesugar { .. } | MatchSource::WhileLetDesugar => {
- if let Some(expr_ty) = cx.typeck_results().node_type_opt(expr.hir_id) {
- 'pattern_checks: for arm in arms {
- let pat = &arm.pat;
- if in_external_macro(cx.sess(), pat.span) {
- continue 'pattern_checks;
- }
- if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
- break 'pattern_checks;
- }
- }
+ if let ExprKind::Match(scrutinee, arms, MatchSource::Normal) = expr.kind {
+ if let Some(expr_ty) = cx.typeck_results().node_type_opt(scrutinee.hir_id) {
+ 'pattern_checks: for arm in arms {
+ let pat = &arm.pat;
+ if in_external_macro(cx.sess(), pat.span) {
+ continue 'pattern_checks;
}
- },
- _ => (),
+ if apply_lint(cx, pat, expr_ty, DerefPossible::Possible) {
+ break 'pattern_checks;
+ }
+ }
+ }
+ }
+ if let ExprKind::Let(let_pat, let_expr, _) = expr.kind {
+ if let Some(ref expr_ty) = cx.typeck_results().node_type_opt(let_expr.hir_id) {
+ if in_external_macro(cx.sess(), let_pat.span) {
+ return;
+ }
+ apply_lint(cx, let_pat, expr_ty, DerefPossible::Possible);
}
}
}
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 0e682c692c7a6..91085c13ac4a4 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::higher;
use clippy_utils::is_lang_ctor;
use clippy_utils::source::snippet_with_applicability;
use clippy_utils::sugg::Sugg;
@@ -7,7 +8,7 @@ use clippy_utils::{eq_expr_value, path_to_local_id};
use if_chain::if_chain;
use rustc_errors::Applicability;
use rustc_hir::LangItem::{OptionNone, OptionSome};
-use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, MatchSource, PatKind, StmtKind};
+use rustc_hir::{BindingAnnotation, Block, Expr, ExprKind, PatKind, StmtKind};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::sym;
@@ -50,10 +51,10 @@ impl QuestionMark {
/// If it matches, it will suggest to use the question mark operator instead
fn check_is_none_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
- if let ExprKind::If(if_expr, body, else_) = &expr.kind;
- if let ExprKind::MethodCall(segment, _, args, _) = &if_expr.kind;
+ if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr);
+ if let ExprKind::MethodCall(segment, _, args, _) = &cond.kind;
if segment.ident.name == sym!(is_none);
- if Self::expression_returns_none(cx, body);
+ if Self::expression_returns_none(cx, then);
if let Some(subject) = args.get(0);
if Self::is_option(cx, subject);
@@ -61,9 +62,9 @@ impl QuestionMark {
let mut applicability = Applicability::MachineApplicable;
let receiver_str = &Sugg::hir_with_applicability(cx, subject, "..", &mut applicability);
let mut replacement: Option<String> = None;
- if let Some(else_) = else_ {
+ if let Some(else_inner) = r#else {
if_chain! {
- if let ExprKind::Block(block, None) = &else_.kind;
+ if let ExprKind::Block(block, None) = &else_inner.kind;
if block.stmts.is_empty();
if let Some(block_expr) = &block.expr;
if eq_expr_value(cx, subject, block_expr);
@@ -96,25 +97,23 @@ impl QuestionMark {
fn check_if_let_some_and_early_return_none(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
- if let ExprKind::Match(subject, arms, source) = &expr.kind;
- if *source == MatchSource::IfLetDesugar { contains_else_clause: true };
- if Self::is_option(cx, subject);
+ if let Some(higher::IfLet { let_pat, let_expr, if_then, if_else: Some(if_else) }) = higher::IfLet::hir(expr);
+ if Self::is_option(cx, let_expr);
- if let PatKind::TupleStruct(path1, fields, None) = &arms[0].pat.kind;
+ if let PatKind::TupleStruct(ref path1, fields, None) = let_pat.kind;
if is_lang_ctor(cx, path1, OptionSome);
if let PatKind::Binding(annot, bind_id, _, _) = fields[0].kind;
let by_ref = matches!(annot, BindingAnnotation::Ref | BindingAnnotation::RefMut);
- if let ExprKind::Block(block, None) = &arms[0].body.kind;
+ if let ExprKind::Block(ref block, None) = if_then.kind;
if block.stmts.is_empty();
if let Some(trailing_expr) = &block.expr;
if path_to_local_id(trailing_expr, bind_id);
- if let PatKind::Wild = arms[1].pat.kind;
- if Self::expression_returns_none(cx, arms[1].body);
+ if Self::expression_returns_none(cx, if_else);
then {
let mut applicability = Applicability::MachineApplicable;
- let receiver_str = snippet_with_applicability(cx, subject.span, "..", &mut applicability);
+ let receiver_str = snippet_with_applicability(cx, let_expr.span, "..", &mut applicability);
let replacement = format!(
"{}{}?",
receiver_str,
diff --git a/src/tools/clippy/clippy_lints/src/ranges.rs b/src/tools/clippy/clippy_lints/src/ranges.rs
index 0179bd48ee3cb..0114a2f97a228 100644
--- a/src/tools/clippy/clippy_lints/src/ranges.rs
+++ b/src/tools/clippy/clippy_lints/src/ranges.rs
@@ -329,7 +329,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
if let ExprKind::MethodCall(iter_path, _, iter_args, _) = iter.kind;
if iter_path.ident.name == sym::iter;
// range expression in `.zip()` call: `0..x.len()`
- if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::range(zip_arg);
+ if let Some(higher::Range { start: Some(start), end: Some(end), .. }) = higher::Range::hir(zip_arg);
if is_integer_const(cx, start, 0);
// `.len()` call
if let ExprKind::MethodCall(len_path, _, len_args, _) = end.kind;
@@ -337,7 +337,7 @@ fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args:
// `.iter()` and `.len()` called on same `Path`
if let ExprKind::Path(QPath::Resolved(_, iter_path)) = iter_args[0].kind;
if let ExprKind::Path(QPath::Resolved(_, len_path)) = len_args[0].kind;
- if SpanlessEq::new(cx).eq_path_segments(iter_path.segments, len_path.segments);
+ if SpanlessEq::new(cx).eq_path_segments(&iter_path.segments, &len_path.segments);
then {
span_lint(cx,
RANGE_ZIP_WITH_LEN,
@@ -356,7 +356,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
start,
end: Some(end),
limits: RangeLimits::HalfOpen
- }) = higher::range(expr);
+ }) = higher::Range::hir(expr);
if let Some(y) = y_plus_one(cx, end);
then {
let span = if expr.span.from_expansion() {
@@ -401,7 +401,7 @@ fn check_exclusive_range_plus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
// inclusive range minus one: `x..=(y-1)`
fn check_inclusive_range_minus_one(cx: &LateContext<'_>, expr: &Expr<'_>) {
if_chain! {
- if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::range(expr);
+ if let Some(higher::Range { start, end: Some(end), limits: RangeLimits::Closed }) = higher::Range::hir(expr);
if let Some(y) = y_minus_one(cx, end);
then {
span_lint_and_then(
@@ -438,8 +438,8 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
fn is_for_loop_arg(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
let mut cur_expr = expr;
while let Some(parent_expr) = get_parent_expr(cx, cur_expr) {
- match higher::for_loop(parent_expr) {
- Some((_, args, _, _)) if args.hir_id == expr.hir_id => return true,
+ match higher::ForLoop::hir(parent_expr) {
+ Some(higher::ForLoop { arg, .. }) if arg.hir_id == expr.hir_id => return true,
_ => cur_expr = parent_expr,
}
}
@@ -455,7 +455,7 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
}
if_chain! {
- if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::range(expr);
+ if let Some(higher::Range { start: Some(start), end: Some(end), limits }) = higher::Range::hir(expr);
let ty = cx.typeck_results().expr_ty(start);
if let ty::Int(_) | ty::Uint(_) = ty.kind();
if let Some((start_idx, _)) = constant(cx, cx.typeck_results(), start);
diff --git a/src/tools/clippy/clippy_lints/src/redundant_clone.rs b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
index eeeac35a6d511..530b3396abef6 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_clone.rs
@@ -725,7 +725,7 @@ fn rvalue_locals(rvalue: &mir::Rvalue<'_>, mut visit: impl FnMut(mir::Local)) {
BinaryOp(_, box (lhs, rhs)) | CheckedBinaryOp(_, box (lhs, rhs)) => {
visit_op(lhs);
visit_op(rhs);
- }
+ },
_ => (),
}
}
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index db4b1002ce129..e153288aa58df 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -212,14 +212,6 @@ fn check_final_expr<'tcx>(
check_final_expr(cx, arm.body, Some(arm.body.span), RetReplacement::Block);
}
},
- MatchSource::IfLetDesugar {
- contains_else_clause: true,
- } => {
- if let ExprKind::Block(ifblock, _) = arms[0].body.kind {
- check_block_return(cx, ifblock);
- }
- check_final_expr(cx, arms[1].body, None, RetReplacement::Empty);
- },
_ => (),
},
ExprKind::DropTemps(expr) => check_final_expr(cx, expr, None, RetReplacement::Empty),
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index a8e962d1af3fa..44d5ff0b63ad5 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -588,7 +588,7 @@ fn ident_difference_expr_with_base_location(
| (ForLoop(_, _, _, _), ForLoop(_, _, _, _))
| (While(_, _, _), While(_, _, _))
| (If(_, _, _), If(_, _, _))
- | (Let(_, _), Let(_, _))
+ | (Let(_, _, _), Let(_, _, _))
| (Type(_, _), Type(_, _))
| (Cast(_, _), Cast(_, _))
| (Lit(_), Lit(_))
diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
index c8a231341b7e1..d6cf7190abb04 100644
--- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs
@@ -67,7 +67,7 @@ impl EarlyLintPass for UnnestedOrPatterns {
fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &ast::Expr) {
if meets_msrv(self.msrv.as_ref(), &msrvs::OR_PATTERNS) {
- if let ast::ExprKind::Let(pat, _) = &e.kind {
+ if let ast::ExprKind::Let(pat, _, _) = &e.kind {
lint_unnested_or_patterns(cx, pat);
}
}
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index c5b8acb9982d8..bffd9f3612b0a 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -1,4 +1,5 @@
use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::higher;
use clippy_utils::ty::is_type_diagnostic_item;
use clippy_utils::{differing_macro_contexts, usage::is_potentially_mutated};
use if_chain::if_chain;
@@ -160,11 +161,11 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
if in_external_macro(self.cx.tcx.sess, expr.span) {
return;
}
- if let ExprKind::If(cond, then, els) = &expr.kind {
+ if let Some(higher::If { cond, then, r#else }) = higher::If::hir(expr) {
walk_expr(self, cond);
self.visit_branch(cond, then, false);
- if let Some(els) = els {
- self.visit_branch(cond, els, true);
+ if let Some(else_inner) = r#else {
+ self.visit_branch(cond, else_inner, true);
}
} else {
// find `unwrap[_err]()` calls:
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 61fd375a9892c..f93d7782e2511 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -208,6 +208,15 @@ impl<'tcx> Visitor<'tcx> for PrintVisitor {
print!(" if let ExprKind::");
let current = format!("{}.kind", self.current);
match expr.kind {
+ ExprKind::Let(pat, expr, _) => {
+ let let_pat = self.next("pat");
+ let let_expr = self.next("expr");
+ println!(" Let(ref {}, ref {}, _) = {};", let_pat, let_expr, current);
+ self.current = let_expr;
+ self.visit_expr(expr);
+ self.current = let_pat;
+ self.visit_pat(pat);
+ },
ExprKind::Box(inner) => {
let inner_pat = self.next("inner");
println!("Box(ref {}) = {};", inner_pat, current);
diff --git a/src/tools/clippy/clippy_lints/src/utils/inspector.rs b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
index f7ddee12dcf64..6bf216cec1670 100644
--- a/src/tools/clippy/clippy_lints/src/utils/inspector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/inspector.rs
@@ -66,28 +66,6 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
hir::ImplItemKind::TyAlias(_) => println!("associated type"),
}
}
- // fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx
- // hir::TraitItem) {
- // if !has_attr(&item.attrs) {
- // return;
- // }
- // }
- //
- // fn check_variant(&mut self, cx: &LateContext<'tcx>, var: &'tcx
- // hir::Variant, _:
- // &hir::Generics) {
- // if !has_attr(&var.node.attrs) {
- // return;
- // }
- // }
- //
- // fn check_field_def(&mut self, cx: &LateContext<'tcx>, field: &'tcx
- // hir::FieldDef) {
- // if !has_attr(&field.attrs) {
- // return;
- // }
- // }
- //
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
if !has_attr(cx.sess(), cx.tcx.hir().attrs(expr.hir_id)) {
@@ -127,13 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for DeepCodeInspector {
hir::StmtKind::Expr(e) | hir::StmtKind::Semi(e) => print_expr(cx, e, 0),
}
}
- // fn check_foreign_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx
- // hir::ForeignItem) {
- // if !has_attr(&item.attrs) {
- // return;
- // }
- // }
- //
}
fn has_attr(sess: &Session, attrs: &[Attribute]) -> bool {
@@ -171,6 +142,10 @@ fn print_expr(cx: &LateContext<'_>, expr: &hir::Expr<'_>, indent: usize) {
print_expr(cx, arg, indent + 1);
}
},
+ hir::ExprKind::Let(ref pat, ref expr, _) => {
+ print_pat(cx, pat, indent + 1);
+ print_expr(cx, expr, indent + 1);
+ },
hir::ExprKind::MethodCall(path, _, args, _) => {
println!("{}MethodCall", ind);
println!("{}method name: {}", ind, path.ident.name);
diff --git a/src/tools/clippy/clippy_lints/src/vec.rs b/src/tools/clippy/clippy_lints/src/vec.rs
index 32fa46f042ce1..95a45fa937f11 100644
--- a/src/tools/clippy/clippy_lints/src/vec.rs
+++ b/src/tools/clippy/clippy_lints/src/vec.rs
@@ -49,8 +49,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
if_chain! {
if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty_adjusted(expr).kind();
if let ty::Slice(..) = ty.kind();
- if let ExprKind::AddrOf(BorrowKind::Ref, mutability, addressee) = expr.kind;
- if let Some(vec_args) = higher::vec_macro(cx, addressee);
+ if let ExprKind::AddrOf(BorrowKind::Ref, mutability, ref addressee) = expr.kind;
+ if let Some(vec_args) = higher::VecArgs::hir(cx, addressee);
then {
self.check_vec_macro(cx, &vec_args, mutability, expr.span);
}
@@ -58,8 +58,8 @@ impl<'tcx> LateLintPass<'tcx> for UselessVec {
// search for `for _ in vec![…]`
if_chain! {
- if let Some((_, arg, _, _)) = higher::for_loop(expr);
- if let Some(vec_args) = higher::vec_macro(cx, arg);
+ if let Some(higher::ForLoop { arg, .. }) = higher::ForLoop::hir(expr);
+ if let Some(vec_args) = higher::VecArgs::hir(cx, arg);
if is_copy(cx, vec_type(cx.typeck_results().expr_ty_adjusted(arg)));
then {
// report the error around the `vec!` not inside `<std macros>:`
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 30c2260d15cac..7ea07a15aea51 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -158,7 +158,7 @@ pub fn eq_expr(l: &Expr, r: &Expr) -> bool {
(Unary(lo, l), Unary(ro, r)) => mem::discriminant(lo) == mem::discriminant(ro) && eq_expr(l, r),
(Lit(l), Lit(r)) => l.kind == r.kind,
(Cast(l, lt), Cast(r, rt)) | (Type(l, lt), Type(r, rt)) => eq_expr(l, r) && eq_ty(lt, rt),
- (Let(lp, le), Let(rp, re)) => eq_pat(lp, rp) && eq_expr(le, re),
+ (Let(lp, le, _), Let(rp, re, _)) => eq_pat(lp, rp) && eq_expr(le, re),
(If(lc, lt, le), If(rc, rt, re)) => eq_expr(lc, rc) && eq_block(lt, rt) && eq_expr_opt(le, re),
(While(lc, lt, ll), While(rc, rt, rl)) => eq_label(ll, rl) && eq_expr(lc, rc) && eq_block(lt, rt),
(ForLoop(lp, li, lt, ll), ForLoop(rp, ri, rt, rl)) => {
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index 88b115a63d788..29e2559fc6d60 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -60,6 +60,7 @@ fn identify_some_pure_patterns(expr: &Expr<'_>) -> bool {
| ExprKind::MethodCall(..)
| ExprKind::Binary(..)
| ExprKind::Unary(..)
+ | ExprKind::Let(..)
| ExprKind::Cast(..)
| ExprKind::Type(..)
| ExprKind::DropTemps(..)
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 884180f0586e3..29b698e56e3c0 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -1,5 +1,4 @@
-//! This module contains functions for retrieve the original AST from lowered
-//! `hir`.
+//! This module contains functions that retrieves specifiec elements.
#![deny(clippy::missing_docs_in_private_items)]
@@ -7,142 +6,214 @@ use crate::{is_expn_of, match_def_path, paths};
use if_chain::if_chain;
use rustc_ast::ast::{self, LitKind};
use rustc_hir as hir;
-use rustc_hir::{BorrowKind, Expr, ExprKind, StmtKind, UnOp};
+use rustc_hir::{Block, BorrowKind, Expr, ExprKind, LoopSource, Node, Pat, StmtKind, UnOp};
use rustc_lint::LateContext;
use rustc_span::{sym, ExpnKind, Span, Symbol};
-/// Represent a range akin to `ast::ExprKind::Range`.
-#[derive(Debug, Copy, Clone)]
-pub struct Range<'a> {
- /// The lower bound of the range, or `None` for ranges such as `..X`.
- pub start: Option<&'a hir::Expr<'a>>,
- /// The upper bound of the range, or `None` for ranges such as `X..`.
- pub end: Option<&'a hir::Expr<'a>>,
- /// Whether the interval is open or closed.
- pub limits: ast::RangeLimits,
+/// The essential nodes of a desugared for loop as well as the entire span:
+/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
+pub struct ForLoop<'tcx> {
+ pub pat: &'tcx hir::Pat<'tcx>,
+ pub arg: &'tcx hir::Expr<'tcx>,
+ pub body: &'tcx hir::Expr<'tcx>,
+ pub span: Span,
}
-/// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
-pub fn range<'a>(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
- /// Finds the field named `name` in the field. Always return `Some` for
- /// convenience.
- fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
- let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
-
- Some(expr)
+impl<'tcx> ForLoop<'tcx> {
+ #[inline]
+ pub fn hir(expr: &Expr<'tcx>) -> Option<Self> {
+ if_chain! {
+ if let hir::ExprKind::Match(ref iterexpr, ref arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
+ if let Some(first_arm) = arms.get(0);
+ if let hir::ExprKind::Call(_, ref iterargs) = iterexpr.kind;
+ if let Some(first_arg) = iterargs.get(0);
+ if iterargs.len() == 1 && arms.len() == 1 && first_arm.guard.is_none();
+ if let hir::ExprKind::Loop(ref block, ..) = first_arm.body.kind;
+ if block.expr.is_none();
+ if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
+ if let hir::StmtKind::Local(ref local) = let_stmt.kind;
+ if let hir::StmtKind::Expr(ref body_expr) = body.kind;
+ then {
+ return Some(Self {
+ pat: &*local.pat,
+ arg: first_arg,
+ body: body_expr,
+ span: first_arm.span
+ });
+ }
+ }
+ None
}
+}
+
+pub struct If<'hir> {
+ pub cond: &'hir Expr<'hir>,
+ pub r#else: Option<&'hir Expr<'hir>>,
+ pub then: &'hir Expr<'hir>,
+}
- match expr.kind {
- hir::ExprKind::Call(path, args)
- if matches!(
- path.kind,
- hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
- ) =>
+impl<'hir> If<'hir> {
+ #[inline]
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::If(
+ Expr {
+ kind: ExprKind::DropTemps(cond),
+ ..
+ },
+ then,
+ r#else,
+ ) = expr.kind
{
- Some(Range {
- start: Some(&args[0]),
- end: Some(&args[1]),
- limits: ast::RangeLimits::Closed,
- })
- },
- hir::ExprKind::Struct(path, fields, None) => match path {
- hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
- start: None,
- end: None,
- limits: ast::RangeLimits::HalfOpen,
- }),
- hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
- start: Some(get_field("start", fields)?),
- end: None,
- limits: ast::RangeLimits::HalfOpen,
- }),
- hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
- start: Some(get_field("start", fields)?),
- end: Some(get_field("end", fields)?),
- limits: ast::RangeLimits::HalfOpen,
- }),
- hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
- start: None,
- end: Some(get_field("end", fields)?),
- limits: ast::RangeLimits::Closed,
- }),
- hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
- start: None,
- end: Some(get_field("end", fields)?),
- limits: ast::RangeLimits::HalfOpen,
- }),
- _ => None,
- },
- _ => None,
+ Some(Self { cond, r#else, then })
+ } else {
+ None
+ }
}
}
-/// Checks if a `let` statement is from a `for` loop desugaring.
-pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
- // This will detect plain for-loops without an actual variable binding:
- //
- // ```
- // for x in some_vec {
- // // do stuff
- // }
- // ```
- if_chain! {
- if let Some(expr) = local.init;
- if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
- then {
- return true;
+pub struct IfLet<'hir> {
+ pub let_pat: &'hir Pat<'hir>,
+ pub let_expr: &'hir Expr<'hir>,
+ pub if_then: &'hir Expr<'hir>,
+ pub if_else: Option<&'hir Expr<'hir>>,
+}
+
+impl<'hir> IfLet<'hir> {
+ #[inline]
+ pub fn ast(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<Self> {
+ let rslt = Self::hir(expr)?;
+ Self::is_not_within_while_context(cx, expr)?;
+ Some(rslt)
+ }
+
+ #[inline]
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::If(
+ Expr {
+ kind: ExprKind::Let(let_pat, let_expr, _),
+ ..
+ },
+ if_then,
+ if_else,
+ ) = expr.kind
+ {
+ return Some(Self {
+ let_pat,
+ let_expr,
+ if_then,
+ if_else,
+ });
}
+ None
}
- // This detects a variable binding in for loop to avoid `let_unit_value`
- // lint (see issue #1964).
- //
- // ```
- // for _ in vec![()] {
- // // anything
- // }
- // ```
- if let hir::LocalSource::ForLoopDesugar = local.source {
- return true;
+ #[inline]
+ fn is_not_within_while_context(cx: &LateContext<'tcx>, expr: &Expr<'hir>) -> Option<()> {
+ let hir = cx.tcx.hir();
+ let parent = hir.get_parent_node(expr.hir_id);
+ let parent_parent = hir.get_parent_node(parent);
+ let parent_parent_node = hir.get(parent_parent);
+ if let Node::Expr(Expr {
+ kind: ExprKind::Loop(_, _, LoopSource::While, _),
+ ..
+ }) = parent_parent_node
+ {
+ return None;
+ }
+ Some(())
}
+}
- false
+pub struct IfOrIfLet<'hir> {
+ pub cond: &'hir Expr<'hir>,
+ pub r#else: Option<&'hir Expr<'hir>>,
+ pub then: &'hir Expr<'hir>,
}
-/// Recover the essential nodes of a desugared for loop as well as the entire span:
-/// `for pat in arg { body }` becomes `(pat, arg, body)`. Return `(pat, arg, body, span)`.
-pub fn for_loop<'tcx>(
- expr: &'tcx hir::Expr<'tcx>,
-) -> Option<(&hir::Pat<'_>, &'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>, Span)> {
- if_chain! {
- if let hir::ExprKind::Match(iterexpr, arms, hir::MatchSource::ForLoopDesugar) = expr.kind;
- if let hir::ExprKind::Call(_, iterargs) = iterexpr.kind;
- if iterargs.len() == 1 && arms.len() == 1 && arms[0].guard.is_none();
- if let hir::ExprKind::Loop(block, ..) = arms[0].body.kind;
- if block.expr.is_none();
- if let [ _, _, ref let_stmt, ref body ] = *block.stmts;
- if let hir::StmtKind::Local(local) = let_stmt.kind;
- if let hir::StmtKind::Expr(expr) = body.kind;
- then {
- return Some((&*local.pat, &iterargs[0], expr, arms[0].span));
+impl<'hir> IfOrIfLet<'hir> {
+ #[inline]
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::If(cond, then, r#else) = expr.kind {
+ if let ExprKind::DropTemps(new_cond) = cond.kind {
+ return Some(Self {
+ cond: new_cond,
+ r#else,
+ then,
+ });
+ }
+ if let ExprKind::Let(..) = cond.kind {
+ return Some(Self { cond, r#else, then });
+ }
}
+ None
}
- None
}
-/// Recover the essential nodes of a desugared while loop:
-/// `while cond { body }` becomes `(cond, body)`.
-pub fn while_loop<'tcx>(expr: &'tcx hir::Expr<'tcx>) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>)> {
- if_chain! {
- if let hir::ExprKind::Loop(hir::Block { expr: Some(expr), .. }, _, hir::LoopSource::While, _) = &expr.kind;
- if let hir::ExprKind::Match(cond, arms, hir::MatchSource::WhileDesugar) = &expr.kind;
- if let hir::ExprKind::DropTemps(cond) = &cond.kind;
- if let [hir::Arm { body, .. }, ..] = &arms[..];
- then {
- return Some((cond, body));
+/// Represent a range akin to `ast::ExprKind::Range`.
+#[derive(Debug, Copy, Clone)]
+pub struct Range<'a> {
+ /// The lower bound of the range, or `None` for ranges such as `..X`.
+ pub start: Option<&'a hir::Expr<'a>>,
+ /// The upper bound of the range, or `None` for ranges such as `X..`.
+ pub end: Option<&'a hir::Expr<'a>>,
+ /// Whether the interval is open or closed.
+ pub limits: ast::RangeLimits,
+}
+
+impl<'a> Range<'a> {
+ /// Higher a `hir` range to something similar to `ast::ExprKind::Range`.
+ pub fn hir(expr: &'a hir::Expr<'_>) -> Option<Range<'a>> {
+ /// Finds the field named `name` in the field. Always return `Some` for
+ /// convenience.
+ fn get_field<'c>(name: &str, fields: &'c [hir::ExprField<'_>]) -> Option<&'c hir::Expr<'c>> {
+ let expr = &fields.iter().find(|field| field.ident.name.as_str() == name)?.expr;
+ Some(expr)
+ }
+
+ match expr.kind {
+ hir::ExprKind::Call(ref path, ref args)
+ if matches!(
+ path.kind,
+ hir::ExprKind::Path(hir::QPath::LangItem(hir::LangItem::RangeInclusiveNew, _))
+ ) =>
+ {
+ Some(Range {
+ start: Some(&args[0]),
+ end: Some(&args[1]),
+ limits: ast::RangeLimits::Closed,
+ })
+ },
+ hir::ExprKind::Struct(ref path, ref fields, None) => match path {
+ hir::QPath::LangItem(hir::LangItem::RangeFull, _) => Some(Range {
+ start: None,
+ end: None,
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ hir::QPath::LangItem(hir::LangItem::RangeFrom, _) => Some(Range {
+ start: Some(get_field("start", fields)?),
+ end: None,
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ hir::QPath::LangItem(hir::LangItem::Range, _) => Some(Range {
+ start: Some(get_field("start", fields)?),
+ end: Some(get_field("end", fields)?),
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ hir::QPath::LangItem(hir::LangItem::RangeToInclusive, _) => Some(Range {
+ start: None,
+ end: Some(get_field("end", fields)?),
+ limits: ast::RangeLimits::Closed,
+ }),
+ hir::QPath::LangItem(hir::LangItem::RangeTo, _) => Some(Range {
+ start: None,
+ end: Some(get_field("end", fields)?),
+ limits: ast::RangeLimits::HalfOpen,
+ }),
+ _ => None,
+ },
+ _ => None,
}
}
- None
}
/// Represent the pre-expansion arguments of a `vec!` invocation.
@@ -153,41 +224,157 @@ pub enum VecArgs<'a> {
Vec(&'a [hir::Expr<'a>]),
}
-/// Returns the arguments of the `vec!` macro if this expression was expanded
-/// from `vec!`.
-pub fn vec_macro<'e>(cx: &LateContext<'_>, expr: &'e hir::Expr<'_>) -> Option<VecArgs<'e>> {
- if_chain! {
- if let hir::ExprKind::Call(fun, args) = expr.kind;
- if let hir::ExprKind::Path(ref qpath) = fun.kind;
- if is_expn_of(fun.span, "vec").is_some();
- if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
- then {
- return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
- // `vec![elem; size]` case
- Some(VecArgs::Repeat(&args[0], &args[1]))
- }
- else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
- // `vec![a, b, c]` case
- if_chain! {
- if let hir::ExprKind::Box(boxed) = args[0].kind;
- if let hir::ExprKind::Array(args) = boxed.kind;
- then {
- return Some(VecArgs::Vec(&*args));
- }
+impl<'a> VecArgs<'a> {
+ /// Returns the arguments of the `vec!` macro if this expression was expanded
+ /// from `vec!`.
+ pub fn hir(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) -> Option<VecArgs<'a>> {
+ if_chain! {
+ if let hir::ExprKind::Call(ref fun, ref args) = expr.kind;
+ if let hir::ExprKind::Path(ref qpath) = fun.kind;
+ if is_expn_of(fun.span, "vec").is_some();
+ if let Some(fun_def_id) = cx.qpath_res(qpath, fun.hir_id).opt_def_id();
+ then {
+ return if match_def_path(cx, fun_def_id, &paths::VEC_FROM_ELEM) && args.len() == 2 {
+ // `vec![elem; size]` case
+ Some(VecArgs::Repeat(&args[0], &args[1]))
}
+ else if match_def_path(cx, fun_def_id, &paths::SLICE_INTO_VEC) && args.len() == 1 {
+ // `vec![a, b, c]` case
+ if_chain! {
+ if let hir::ExprKind::Box(ref boxed) = args[0].kind;
+ if let hir::ExprKind::Array(ref args) = boxed.kind;
+ then {
+ return Some(VecArgs::Vec(&*args));
+ }
+ }
- None
+ None
+ }
+ else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
+ Some(VecArgs::Vec(&[]))
+ }
+ else {
+ None
+ };
}
- else if match_def_path(cx, fun_def_id, &paths::VEC_NEW) && args.is_empty() {
- Some(VecArgs::Vec(&[]))
+ }
+
+ None
+ }
+}
+
+pub struct While<'hir> {
+ pub if_cond: &'hir Expr<'hir>,
+ pub if_then: &'hir Expr<'hir>,
+ pub if_else: Option<&'hir Expr<'hir>>,
+}
+
+impl<'hir> While<'hir> {
+ #[inline]
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::Loop(
+ Block {
+ expr:
+ Some(Expr {
+ kind:
+ ExprKind::If(
+ Expr {
+ kind: ExprKind::DropTemps(if_cond),
+ ..
+ },
+ if_then,
+ if_else_ref,
+ ),
+ ..
+ }),
+ ..
+ },
+ _,
+ LoopSource::While,
+ _,
+ ) = expr.kind
+ {
+ let if_else = *if_else_ref;
+ return Some(Self {
+ if_cond,
+ if_then,
+ if_else,
+ });
+ }
+ None
+ }
+}
+
+pub struct WhileLet<'hir> {
+ pub if_expr: &'hir Expr<'hir>,
+ pub let_pat: &'hir Pat<'hir>,
+ pub let_expr: &'hir Expr<'hir>,
+ pub if_then: &'hir Expr<'hir>,
+ pub if_else: Option<&'hir Expr<'hir>>,
+}
+
+impl<'hir> WhileLet<'hir> {
+ #[inline]
+ pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
+ if let ExprKind::Loop(
+ Block {
+ expr: Some(if_expr), ..
+ },
+ _,
+ LoopSource::While,
+ _,
+ ) = expr.kind
+ {
+ if let Expr {
+ kind:
+ ExprKind::If(
+ Expr {
+ kind: ExprKind::Let(let_pat, let_expr, _),
+ ..
+ },
+ if_then,
+ if_else_ref,
+ ),
+ ..
+ } = if_expr
+ {
+ let if_else = *if_else_ref;
+ return Some(Self {
+ if_expr,
+ let_pat,
+ let_expr,
+ if_then,
+ if_else,
+ });
}
- else {
- None
- };
}
+ None
}
+}
- None
+/// Converts a hir binary operator to the corresponding `ast` type.
+#[must_use]
+pub fn binop(op: hir::BinOpKind) -> ast::BinOpKind {
+ match op {
+ hir::BinOpKind::Eq => ast::BinOpKind::Eq,
+ hir::BinOpKind::Ge => ast::BinOpKind::Ge,
+ hir::BinOpKind::Gt => ast::BinOpKind::Gt,
+ hir::BinOpKind::Le => ast::BinOpKind::Le,
+ hir::BinOpKind::Lt => ast::BinOpKind::Lt,
+ hir::BinOpKind::Ne => ast::BinOpKind::Ne,
+ hir::BinOpKind::Or => ast::BinOpKind::Or,
+ hir::BinOpKind::Add => ast::BinOpKind::Add,
+ hir::BinOpKind::And => ast::BinOpKind::And,
+ hir::BinOpKind::BitAnd => ast::BinOpKind::BitAnd,
+ hir::BinOpKind::BitOr => ast::BinOpKind::BitOr,
+ hir::BinOpKind::BitXor => ast::BinOpKind::BitXor,
+ hir::BinOpKind::Div => ast::BinOpKind::Div,
+ hir::BinOpKind::Mul => ast::BinOpKind::Mul,
+ hir::BinOpKind::Rem => ast::BinOpKind::Rem,
+ hir::BinOpKind::Shl => ast::BinOpKind::Shl,
+ hir::BinOpKind::Shr => ast::BinOpKind::Shr,
+ hir::BinOpKind::Sub => ast::BinOpKind::Sub,
+ }
}
/// Extract args from an assert-like macro.
@@ -218,8 +405,8 @@ pub fn extract_assert_macro_args<'tcx>(e: &'tcx Expr<'tcx>) -> Option<Vec<&'tcx
if let StmtKind::Semi(matchexpr) = block.stmts.get(0)?.kind {
// macros with unique arg: `{debug_}assert!` (e.g., `debug_assert!(some_condition)`)
if_chain! {
- if let ExprKind::If(clause, _, _) = matchexpr.kind;
- if let ExprKind::Unary(UnOp::Not, condition) = clause.kind;
+ if let Some(If { cond, .. }) = If::hir(matchexpr);
+ if let ExprKind::Unary(UnOp::Not, condition) = cond.kind;
then {
return Some(vec![condition]);
}
@@ -345,3 +532,35 @@ impl FormatArgsExpn<'tcx> {
}
}
}
+
+/// Checks if a `let` statement is from a `for` loop desugaring.
+pub fn is_from_for_desugar(local: &hir::Local<'_>) -> bool {
+ // This will detect plain for-loops without an actual variable binding:
+ //
+ // ```
+ // for x in some_vec {
+ // // do stuff
+ // }
+ // ```
+ if_chain! {
+ if let Some(ref expr) = local.init;
+ if let hir::ExprKind::Match(_, _, hir::MatchSource::ForLoopDesugar) = expr.kind;
+ then {
+ return true;
+ }
+ }
+
+ // This detects a variable binding in for loop to avoid `let_unit_value`
+ // lint (see issue #1964).
+ //
+ // ```
+ // for _ in vec![()] {
+ // // anything
+ // }
+ // ```
+ if let hir::LocalSource::ForLoopDesugar = local.source {
+ return true;
+ }
+
+ false
+}
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index fd70553e064b6..a44f2df2fd631 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -232,6 +232,9 @@ impl HirEqInterExpr<'_, '_, '_> {
(&ExprKind::If(lc, lt, ref le), &ExprKind::If(rc, rt, ref re)) => {
self.eq_expr(lc, rc) && self.eq_expr(&**lt, &**rt) && both(le, re, |l, r| self.eq_expr(l, r))
},
+ (&ExprKind::Let(ref lp, ref le, _), &ExprKind::Let(ref rp, ref re, _)) => {
+ self.eq_pat(lp, rp) && self.eq_expr(le, re)
+ },
(&ExprKind::Lit(ref l), &ExprKind::Lit(ref r)) => l.node == r.node,
(&ExprKind::Loop(lb, ref ll, ref lls, _), &ExprKind::Loop(rb, ref rl, ref rls, _)) => {
lls == rls && self.eq_block(lb, rb) && both(ll, rl, |l, r| l.ident.name == r.ident.name)
@@ -665,6 +668,10 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
}
}
},
+ ExprKind::Let(ref pat, ref expr, _) => {
+ self.hash_expr(expr);
+ self.hash_pat(pat);
+ },
ExprKind::LlvmInlineAsm(..) | ExprKind::Err => {},
ExprKind::Lit(ref l) => {
l.node.hash(&mut self.s);
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 1d59d6bfea1b9..82bfce8fe789e 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -961,17 +961,6 @@ pub fn is_else_clause(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
let map = tcx.hir();
let mut iter = map.parent_iter(expr.hir_id);
match iter.next() {
- Some((arm_id, Node::Arm(..))) => matches!(
- iter.next(),
- Some((
- _,
- Node::Expr(Expr {
- kind: ExprKind::Match(_, [_, else_arm], MatchSource::IfLetDesugar { .. }),
- ..
- })
- ))
- if else_arm.hir_id == arm_id
- ),
Some((
_,
Node::Expr(Expr {
@@ -1370,15 +1359,15 @@ pub fn if_sequence<'tcx>(mut expr: &'tcx Expr<'tcx>) -> (Vec<&'tcx Expr<'tcx>>,
let mut conds = Vec::new();
let mut blocks: Vec<&Block<'_>> = Vec::new();
- while let ExprKind::If(cond, then_expr, ref else_expr) = expr.kind {
- conds.push(cond);
- if let ExprKind::Block(block, _) = then_expr.kind {
+ while let Some(higher::IfOrIfLet { cond, then, r#else }) = higher::IfOrIfLet::hir(expr) {
+ conds.push(&*cond);
+ if let ExprKind::Block(ref block, _) = then.kind {
blocks.push(block);
} else {
panic!("ExprKind::If node is not an ExprKind::Block");
}
- if let Some(else_expr) = *else_expr {
+ if let Some(ref else_expr) = r#else {
expr = else_expr;
} else {
break;
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 3bd75b10e9058..3b494e1fc8535 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -116,7 +116,7 @@ impl<'a> Sugg<'a> {
/// Generate a suggestion for an expression with the given snippet. This is used by the `hir_*`
/// function variants of `Sugg`, since these use different snippet functions.
fn hir_from_snippet(expr: &hir::Expr<'_>, snippet: Cow<'a, str>) -> Self {
- if let Some(range) = higher::range(expr) {
+ if let Some(range) = higher::Range::hir(expr) {
let op = match range.limits {
ast::RangeLimits::HalfOpen => AssocOp::DotDot,
ast::RangeLimits::Closed => AssocOp::DotDotEq,
@@ -128,6 +128,7 @@ impl<'a> Sugg<'a> {
hir::ExprKind::AddrOf(..)
| hir::ExprKind::Box(..)
| hir::ExprKind::If(..)
+ | hir::ExprKind::Let(..)
| hir::ExprKind::Closure(..)
| hir::ExprKind::Unary(..)
| hir::ExprKind::Match(..) => Sugg::MaybeParen(snippet),
diff --git a/src/tools/clippy/tests/ui/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout
index 502b38545b8ef..1653de9a6f26d 100644
--- a/src/tools/clippy/tests/ui/author/if.stdout
+++ b/src/tools/clippy/tests/ui/author/if.stdout
@@ -12,7 +12,8 @@ if_chain! {
if let ExprKind::Lit(ref lit1) = right.kind;
if let LitKind::Int(2, _) = lit1.node;
if block.expr.is_none();
- if let ExprKind::Lit(ref lit2) = cond.kind;
+ if let ExprKind::DropTemps(ref expr) = cond.kind;
+ if let ExprKind::Lit(ref lit2) = expr.kind;
if let LitKind::Bool(true) = lit2.node;
if let ExprKind::Block(ref block1) = then.kind;
if block1.stmts.len() == 1;
diff --git a/src/tools/clippy/tests/ui/collapsible_match.stderr b/src/tools/clippy/tests/ui/collapsible_match.stderr
index 7797888490089..f96917f583344 100644
--- a/src/tools/clippy/tests/ui/collapsible_match.stderr
+++ b/src/tools/clippy/tests/ui/collapsible_match.stderr
@@ -35,7 +35,7 @@ LL | Ok(val) => match val {
LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern
-error: unnecessary nested match
+error: unnecessary nested `if let` or `match`
--> $DIR/collapsible_match.rs:25:9
|
LL | / if let Some(n) = val {
@@ -51,7 +51,7 @@ LL | if let Ok(val) = res_opt {
LL | if let Some(n) = val {
| ^^^^^^^ with this pattern
-error: unnecessary nested match
+error: unnecessary nested `if let` or `match`
--> $DIR/collapsible_match.rs:32:9
|
LL | / if let Some(n) = val {
@@ -87,7 +87,7 @@ LL | match val {
LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern
-error: unnecessary nested match
+error: unnecessary nested `if let` or `match`
--> $DIR/collapsible_match.rs:52:13
|
LL | / if let Some(n) = val {
@@ -121,7 +121,7 @@ LL | match val {
LL | Some(n) => foo(n),
| ^^^^^^^ with this pattern
-error: unnecessary nested match
+error: unnecessary nested `if let` or `match`
--> $DIR/collapsible_match.rs:72:13
|
LL | / if let Some(n) = val {
diff --git a/src/tools/clippy/tests/ui/crashes/ice-7410.rs b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
index aaa422d88c3e0..85fa421032191 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-7410.rs
+++ b/src/tools/clippy/tests/ui/crashes/ice-7410.rs
@@ -4,6 +4,7 @@
#![feature(lang_items, start, libc)]
#![no_std]
+#![allow(clippy::if_same_then_else)]
#![allow(clippy::redundant_pattern_matching)]
use core::panic::PanicInfo;
diff --git a/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs
index bb238c81ebc05..553c840f9b081 100644
--- a/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs
+++ b/src/tools/clippy/tests/ui/crashes/issues_loop_mut_cond.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::blocks_in_if_conditions)]
#![allow(dead_code)]
/// Issue: https://github.com/rust-lang/rust-clippy/issues/2596
diff --git a/src/tools/clippy/tests/ui/infinite_loop.rs b/src/tools/clippy/tests/ui/infinite_loop.rs
index 3d8fb8507e515..e518b2677b7bf 100644
--- a/src/tools/clippy/tests/ui/infinite_loop.rs
+++ b/src/tools/clippy/tests/ui/infinite_loop.rs
@@ -1,3 +1,5 @@
+#![allow(clippy::blocks_in_if_conditions)]
+
fn fn_val(i: i32) -> i32 {
unimplemented!()
}
diff --git a/src/tools/clippy/tests/ui/infinite_loop.stderr b/src/tools/clippy/tests/ui/infinite_loop.stderr
index 1fcb29eff18e4..2736753c14b6e 100644
--- a/src/tools/clippy/tests/ui/infinite_loop.stderr
+++ b/src/tools/clippy/tests/ui/infinite_loop.stderr
@@ -1,5 +1,5 @@
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:21:11
+ --> $DIR/infinite_loop.rs:23:11
|
LL | while y < 10 {
| ^^^^^^
@@ -8,7 +8,7 @@ LL | while y < 10 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:26:11
+ --> $DIR/infinite_loop.rs:28:11
|
LL | while y < 10 && x < 3 {
| ^^^^^^^^^^^^^^^
@@ -16,7 +16,7 @@ LL | while y < 10 && x < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:33:11
+ --> $DIR/infinite_loop.rs:35:11
|
LL | while !cond {
| ^^^^^
@@ -24,7 +24,7 @@ LL | while !cond {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:77:11
+ --> $DIR/infinite_loop.rs:79:11
|
LL | while i < 3 {
| ^^^^^
@@ -32,7 +32,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:82:11
+ --> $DIR/infinite_loop.rs:84:11
|
LL | while i < 3 && j > 0 {
| ^^^^^^^^^^^^^^
@@ -40,7 +40,7 @@ LL | while i < 3 && j > 0 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:86:11
+ --> $DIR/infinite_loop.rs:88:11
|
LL | while i < 3 {
| ^^^^^
@@ -48,7 +48,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:101:11
+ --> $DIR/infinite_loop.rs:103:11
|
LL | while i < 3 {
| ^^^^^
@@ -56,7 +56,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:106:11
+ --> $DIR/infinite_loop.rs:108:11
|
LL | while i < 3 {
| ^^^^^
@@ -64,7 +64,7 @@ LL | while i < 3 {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:172:15
+ --> $DIR/infinite_loop.rs:174:15
|
LL | while self.count < n {
| ^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL | while self.count < n {
= note: this may lead to an infinite or to a never running loop
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:180:11
+ --> $DIR/infinite_loop.rs:182:11
|
LL | while y < 10 {
| ^^^^^^
@@ -82,7 +82,7 @@ LL | while y < 10 {
= help: rewrite it as `if cond { loop { } }`
error: variables in the condition are not mutated in the loop body
- --> $DIR/infinite_loop.rs:187:11
+ --> $DIR/infinite_loop.rs:189:11
|
LL | while y < 10 {
| ^^^^^^
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.rs b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
index 44c51e8112a7d..c84e31ea482a4 100644
--- a/src/tools/clippy/tests/ui/match_overlapping_arm.rs
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.rs
@@ -2,6 +2,7 @@
#![feature(half_open_range_patterns)]
#![warn(clippy::match_overlapping_arm)]
#![allow(clippy::redundant_pattern_matching)]
+#![allow(clippy::if_same_then_else)]
/// Tests for match_overlapping_arm
diff --git a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
index f25a66d634e88..359fa49f51be7 100644
--- a/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
+++ b/src/tools/clippy/tests/ui/match_overlapping_arm.stderr
@@ -1,60 +1,60 @@
error: some ranges overlap
- --> $DIR/match_overlapping_arm.rs:12:9
+ --> $DIR/match_overlapping_arm.rs:13:9
|
LL | 0..=10 => println!("0 ... 10"),
| ^^^^^^
|
= note: `-D clippy::match-overlapping-arm` implied by `-D warnings`
note: overlaps with this
- --> $DIR/match_overlapping_arm.rs:13:9
+ --> $DIR/match_overlapping_arm.rs:14:9
|
LL | 0..=11 => println!("0 ... 11"),
| ^^^^^^
error: some ranges overlap
- --> $DIR/match_overlapping_arm.rs:18:9
+ --> $DIR/match_overlapping_arm.rs:19:9
|
LL | 0..=5 => println!("0 ... 5"),
| ^^^^^
|
note: overlaps with this
- --> $DIR/match_overlapping_arm.rs:20:9
+ --> $DIR/match_overlapping_arm.rs:21:9
|
LL | FOO..=11 => println!("0 ... 11"),
| ^^^^^^^^
error: some ranges overlap
- --> $DIR/match_overlapping_arm.rs:55:9
+ --> $DIR/match_overlapping_arm.rs:56:9
|
LL | 0..11 => println!("0 .. 11"),
| ^^^^^
|
note: overlaps with this
- --> $DIR/match_overlapping_arm.rs:56:9
+ --> $DIR/match_overlapping_arm.rs:57:9
|
LL | 0..=11 => println!("0 ... 11"),
| ^^^^^^
error: some ranges overlap
- --> $DIR/match_overlapping_arm.rs:80:9
+ --> $DIR/match_overlapping_arm.rs:81:9
|
LL | 0..=10 => println!("0 ... 10"),
| ^^^^^^
|
note: overlaps with this
- --> $DIR/match_overlapping_arm.rs:79:9
+ --> $DIR/match_overlapping_arm.rs:80:9
|
LL | 5..14 => println!("5 .. 14"),
| ^^^^^
error: some ranges overlap
- --> $DIR/match_overlapping_arm.rs:85:9
+ --> $DIR/match_overlapping_arm.rs:86:9
|
LL | 0..7 => println!("0 .. 7"),
| ^^^^
|
note: overlaps with this
- --> $DIR/match_overlapping_arm.rs:86:9
+ --> $DIR/match_overlapping_arm.rs:87:9
|
LL | 0..=10 => println!("0 ... 10"),
| ^^^^^^
diff --git a/src/tools/rustfmt/src/expr.rs b/src/tools/rustfmt/src/expr.rs
index 6cfeb9977a966..975af6c02947a 100644
--- a/src/tools/rustfmt/src/expr.rs
+++ b/src/tools/rustfmt/src/expr.rs
@@ -616,7 +616,7 @@ struct ControlFlow<'a> {
fn extract_pats_and_cond(expr: &ast::Expr) -> (Option<&ast::Pat>, &ast::Expr) {
match expr.kind {
- ast::ExprKind::Let(ref pat, ref cond) => (Some(pat), cond),
+ ast::ExprKind::Let(ref pat, ref cond, _) => (Some(pat), cond),
_ => (None, expr),
}
}