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), } }