Auto merge of #80357 - c410-f3r:new-hir-let, r=matthewjasper

Introduce `hir::ExprKind::Let` - Take 2

Builds on #68577 and depends on #79328.

cc #53667
This commit is contained in:
bors 2021-08-16 00:31:42 +00:00
commit 2a6fb9a4c0
129 changed files with 2136 additions and 2184 deletions

View File

@ -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 }`

View File

@ -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);
}

View File

@ -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);

View File

@ -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));
let desugar = hir::MatchSource::IfLetDesugar { contains_else_clause };
hir::ExprKind::Match(scrutinee, arena_vec![self; then_arm, else_arm], desugar)
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)
}
}
// 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> {

View File

@ -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(),

View File

@ -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);

View File

@ -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) => {

View File

@ -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

View File

@ -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",
}
}

View File

@ -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);

View File

@ -1092,53 +1092,30 @@ impl<'a> State<'a> {
}
fn print_else(&mut self, els: Option<&hir::Expr<'_>>) {
match els {
Some(else_) => {
match else_.kind {
// "another else-if"
hir::ExprKind::If(ref i, ref then, ref e) => {
self.cbox(INDENT_UNIT - 1);
self.ibox(0);
self.s.word(" else if ");
self.print_expr_as_cond(&i);
self.s.space();
self.print_expr(&then);
self.print_else(e.as_ref().map(|e| &**e))
}
// "final else"
hir::ExprKind::Block(ref b, _) => {
self.cbox(INDENT_UNIT - 1);
self.ibox(0);
self.s.word(" else ");
self.print_block(&b)
}
hir::ExprKind::Match(ref expr, arms, _) => {
// else if let desugared to match
assert!(arms.len() == 2, "if let desugars to match with two arms");
self.s.word(" else ");
self.s.word("{");
self.cbox(INDENT_UNIT);
self.ibox(INDENT_UNIT);
self.word_nbsp("match");
self.print_expr_as_cond(&expr);
self.s.space();
self.bopen();
for arm in arms {
self.print_arm(arm);
}
self.bclose(expr.span);
self.s.word("}");
}
// BLEAH, constraints would be great here
_ => {
panic!("print_if saw if with weird alternative");
}
if let Some(els_inner) = els {
match els_inner.kind {
// Another `else if` block.
hir::ExprKind::If(ref i, ref then, ref e) => {
self.cbox(INDENT_UNIT - 1);
self.ibox(0);
self.s.word(" else if ");
self.print_expr_as_cond(&i);
self.s.space();
self.print_expr(&then);
self.print_else(e.as_ref().map(|e| &**e))
}
// Final `else` block.
hir::ExprKind::Block(ref b, _) => {
self.cbox(INDENT_UNIT - 1);
self.ibox(0);
self.s.word(" else ");
self.print_block(&b)
}
// Constraints would be great here!
_ => {
panic!("print_if saw if with weird alternative");
}
}
_ => {}
}
}
@ -1165,34 +1142,49 @@ impl<'a> State<'a> {
self.pclose()
}
pub fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) {
let needs_par = expr.precedence().order() < prec;
fn print_expr_maybe_paren(&mut self, expr: &hir::Expr<'_>, prec: i8) {
self.print_expr_cond_paren(expr, expr.precedence().order() < prec)
}
/// Prints an expr using syntax that's acceptable in a condition position, such as the `cond` in
/// `if cond { ... }`.
pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr))
}
/// Prints `expr` or `(expr)` when `needs_par` holds.
fn print_expr_cond_paren(&mut self, expr: &hir::Expr<'_>, needs_par: bool) {
if needs_par {
self.popen();
}
self.print_expr(expr);
if let hir::ExprKind::DropTemps(ref actual_expr) = expr.kind {
self.print_expr(actual_expr);
} else {
self.print_expr(expr);
}
if needs_par {
self.pclose();
}
}
/// Print an expr using syntax that's acceptable in a condition position, such as the `cond` in
/// `if cond { ... }`.
pub fn print_expr_as_cond(&mut self, expr: &hir::Expr<'_>) {
let needs_par = match expr.kind {
// These cases need parens due to the parse error observed in #26461: `if return {}`
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) | hir::ExprKind::Break(..) => true,
/// Print a `let pat = expr` expression.
fn print_let(&mut self, pat: &hir::Pat<'_>, expr: &hir::Expr<'_>) {
self.s.word("let ");
self.print_pat(pat);
self.s.space();
self.word_space("=");
let npals = || parser::needs_par_as_let_scrutinee(expr.precedence().order());
self.print_expr_cond_paren(expr, Self::cond_needs_par(expr) || npals())
}
// Does `expr` need parenthesis when printed in a condition position?
//
// These cases need parens due to the parse error observed in #26461: `if return {}`
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
fn cond_needs_par(expr: &hir::Expr<'_>) -> bool {
match expr.kind {
hir::ExprKind::Break(..) | hir::ExprKind::Closure(..) | hir::ExprKind::Ret(..) => true,
_ => contains_exterior_struct_lit(expr),
};
if needs_par {
self.popen();
}
self.print_expr(expr);
if needs_par {
self.pclose();
}
}
@ -1314,6 +1306,9 @@ impl<'a> State<'a> {
(&hir::ExprKind::Cast { .. }, hir::BinOpKind::Lt | hir::BinOpKind::Shl) => {
parser::PREC_FORCE_PAREN
}
(&hir::ExprKind::Let { .. }, _) if !parser::needs_par_as_let_scrutinee(prec) => {
parser::PREC_FORCE_PAREN
}
_ => left_prec,
};
@ -1531,6 +1526,9 @@ impl<'a> State<'a> {
// Print `}`:
self.bclose_maybe_open(expr.span, true);
}
hir::ExprKind::Let(ref pat, ref scrutinee, _) => {
self.print_let(pat, scrutinee);
}
hir::ExprKind::If(ref test, ref blk, ref elseopt) => {
self.print_if(&test, &blk, elseopt.as_ref().map(|e| &**e));
}

View File

@ -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",

View File

@ -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,

View File

@ -292,6 +292,10 @@ pub enum ExprKind<'tcx> {
Loop {
body: ExprId,
},
Let {
expr: ExprId,
pat: Pat<'tcx>,
},
/// A `match` expression.
Match {
scrutinee: ExprId,

View File

@ -565,6 +565,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::If { .. }
| ExprKind::Loop { .. }
| ExprKind::Block { .. }
| ExprKind::Let { .. }
| ExprKind::Assign { .. }
| ExprKind::AssignOp { .. }
| ExprKind::Break { .. }

View File

@ -284,6 +284,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
| ExprKind::LogicalOp { .. }
| ExprKind::Call { .. }
| ExprKind::Field { .. }
| ExprKind::Let { .. }
| ExprKind::Deref { .. }
| ExprKind::Index { .. }
| ExprKind::VarRef { .. }

View File

@ -46,6 +46,7 @@ impl Category {
ExprKind::LogicalOp { .. }
| ExprKind::Match { .. }
| ExprKind::If { .. }
| ExprKind::Let { .. }
| ExprKind::NeverToAny { .. }
| ExprKind::Use { .. }
| ExprKind::Adt { .. }

View File

@ -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 =

View File

@ -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);

View File

@ -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);

View File

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

View File

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

View File

@ -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]);

View File

@ -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).

View File

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

View File

@ -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> {

View File

@ -221,6 +221,7 @@ impl<'tcx> CheckInlineAssembly<'tcx> {
| ExprKind::Index(..)
| ExprKind::Path(..)
| ExprKind::AddrOf(..)
| ExprKind::Let(..)
| ExprKind::Break(..)
| ExprKind::Continue(..)
| ExprKind::Ret(..)

View File

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

View File

@ -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);
}

View File

@ -24,27 +24,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
let tcx = self.tcx;
use hir::MatchSource::*;
let (source_if, if_no_else, force_scrutinee_bool) = match match_src {
IfLetDesugar { contains_else_clause, .. } => (true, !contains_else_clause, false),
WhileDesugar => (false, false, true),
_ => (false, false, false),
};
// Type check the discriminant and get its type.
let scrutinee_ty = if force_scrutinee_bool {
// Here we want to ensure:
//
// 1. That default match bindings are *not* accepted in the condition of an
// `if` expression. E.g. given `fn foo() -> &bool;` we reject `if foo() { .. }`.
//
// 2. By expecting `bool` for `expr` we get nice diagnostics for e.g. `if x = y { .. }`.
//
// FIXME(60707): Consider removing hack with principled solution.
self.check_expr_has_type_or_error(scrut, self.tcx.types.bool, |_| {})
} else {
self.demand_scrutinee_type(scrut, arms_contain_ref_bindings(arms), arms.is_empty())
};
let acrb = arms_contain_ref_bindings(arms);
let scrutinee_ty = self.demand_scrutinee_type(scrut, acrb, arms.is_empty());
// If there are no arms, that is a diverging match; a special case.
if arms.is_empty() {
@ -52,7 +33,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
return tcx.types.never;
}
self.warn_arms_when_scrutinee_diverges(arms, match_src);
self.warn_arms_when_scrutinee_diverges(arms);
// Otherwise, we have to union together the types that the arms produce and so forth.
let scrut_diverges = self.diverges.replace(Diverges::Maybe);
@ -112,128 +93,95 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
self.diverges.set(Diverges::Maybe);
let arm_ty = if source_if
&& if_no_else
&& i != 0
&& self.if_fallback_coercion(
expr.span,
&arms[0].body,
&mut coercion,
|hir_id, span| self.coercion_reason_match(hir_id, span),
) {
tcx.ty_error()
} else {
// Only call this if this is not an `if` expr with an expected type and no `else`
// clause to avoid duplicated type errors. (#60254)
self.check_expr_with_expectation(&arm.body, expected)
};
let arm_ty = self.check_expr_with_expectation(&arm.body, expected);
all_arms_diverge &= self.diverges.get();
let opt_suggest_box_span =
self.opt_suggest_box_span(arm.body.span, arm_ty, orig_expected);
if source_if {
let then_expr = &arms[0].body;
match (i, if_no_else) {
(0, _) => coercion.coerce(self, &self.misc(expr.span), &arm.body, arm_ty),
(_, true) => {} // Handled above to avoid duplicated type errors (#60254).
(_, _) => {
let then_ty = prior_arm_ty.unwrap();
let cause = self.if_cause(
expr.span,
then_expr,
&arm.body,
then_ty,
arm_ty,
opt_suggest_box_span,
);
coercion.coerce(self, &cause, &arm.body, arm_ty);
}
}
} else {
let (arm_span, semi_span) =
self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
let (span, code) = match i {
// The reason for the first arm to fail is not that the match arms diverge,
// but rather that there's a prior obligation that doesn't hold.
0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
_ => (
expr.span,
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
arm_span,
scrut_span: scrut.span,
semi_span,
source: match_src,
prior_arms: other_arms.clone(),
last_ty: prior_arm_ty.unwrap(),
scrut_hir_id: scrut.hir_id,
opt_suggest_box_span,
}),
),
};
let cause = self.cause(span, code);
// This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`.
// We use it this way to be able to expand on the potential error and detect when a
// `match` tail statement could be a tail expression instead. If so, we suggest
// removing the stray semicolon.
coercion.coerce_inner(
self,
&cause,
Some(&arm.body),
arm_ty,
Some(&mut |err: &mut DiagnosticBuilder<'_>| {
let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
Some(ret_coercion) if self.in_tail_expr => {
let ret_ty = ret_coercion.borrow().expected_ty();
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
self.can_coerce(arm_ty, ret_ty)
&& prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty))
// The match arms need to unify for the case of `impl Trait`.
&& !matches!(ret_ty.kind(), ty::Opaque(..))
}
_ => false,
};
if let (Expectation::IsLast(stmt), Some(ret), true) =
(orig_expected, self.ret_type_span, can_coerce_to_return_ty)
{
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
let mut ret_span: MultiSpan = semi_span.into();
ret_span.push_span_label(
expr.span,
"this could be implicitly returned but it is a statement, not a \
tail expression"
.to_owned(),
);
ret_span.push_span_label(
ret,
"the `match` arms can conform to this return type".to_owned(),
);
ret_span.push_span_label(
semi_span,
"the `match` is a statement because of this semicolon, consider \
removing it"
.to_owned(),
);
err.span_note(
ret_span,
"you might have meant to return the `match` expression",
);
err.tool_only_span_suggestion(
semi_span,
"remove this semicolon",
String::new(),
Applicability::MaybeIncorrect,
);
}
let (arm_span, semi_span) =
self.get_appropriate_arm_semicolon_removal_span(&arms, i, prior_arm_ty, arm_ty);
let (span, code) = match i {
// The reason for the first arm to fail is not that the match arms diverge,
// but rather that there's a prior obligation that doesn't hold.
0 => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)),
_ => (
expr.span,
ObligationCauseCode::MatchExpressionArm(box MatchExpressionArmCause {
arm_span,
scrut_span: scrut.span,
semi_span,
source: match_src,
prior_arms: other_arms.clone(),
last_ty: prior_arm_ty.unwrap(),
scrut_hir_id: scrut.hir_id,
opt_suggest_box_span,
}),
false,
);
),
};
let cause = self.cause(span, code);
other_arms.push(arm_span);
if other_arms.len() > 5 {
other_arms.remove(0);
}
// This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`.
// We use it this way to be able to expand on the potential error and detect when a
// `match` tail statement could be a tail expression instead. If so, we suggest
// removing the stray semicolon.
coercion.coerce_inner(
self,
&cause,
Some(&arm.body),
arm_ty,
Some(&mut |err: &mut DiagnosticBuilder<'_>| {
let can_coerce_to_return_ty = match self.ret_coercion.as_ref() {
Some(ret_coercion) if self.in_tail_expr => {
let ret_ty = ret_coercion.borrow().expected_ty();
let ret_ty = self.inh.infcx.shallow_resolve(ret_ty);
self.can_coerce(arm_ty, ret_ty)
&& prior_arm_ty.map_or(true, |t| self.can_coerce(t, ret_ty))
// The match arms need to unify for the case of `impl Trait`.
&& !matches!(ret_ty.kind(), ty::Opaque(..))
}
_ => false,
};
if let (Expectation::IsLast(stmt), Some(ret), true) =
(orig_expected, self.ret_type_span, can_coerce_to_return_ty)
{
let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi());
let mut ret_span: MultiSpan = semi_span.into();
ret_span.push_span_label(
expr.span,
"this could be implicitly returned but it is a statement, not a \
tail expression"
.to_owned(),
);
ret_span.push_span_label(
ret,
"the `match` arms can conform to this return type".to_owned(),
);
ret_span.push_span_label(
semi_span,
"the `match` is a statement because of this semicolon, consider \
removing it"
.to_owned(),
);
err.span_note(
ret_span,
"you might have meant to return the `match` expression",
);
err.tool_only_span_suggestion(
semi_span,
"remove this semicolon",
String::new(),
Applicability::MaybeIncorrect,
);
}
}),
false,
);
other_arms.push(arm_span);
if other_arms.len() > 5 {
other_arms.remove(0);
}
prior_arm_ty = Some(arm_ty);
}
@ -283,39 +231,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
/// When the previously checked expression (the scrutinee) diverges,
/// warn the user about the match arms being unreachable.
fn warn_arms_when_scrutinee_diverges(
&self,
arms: &'tcx [hir::Arm<'tcx>],
source: hir::MatchSource,
) {
use hir::MatchSource::*;
let msg = match source {
IfLetDesugar { .. } => "block in `if` expression",
WhileDesugar { .. } | WhileLetDesugar { .. } => "block in `while` expression",
_ => "arm",
};
fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) {
for arm in arms {
self.warn_if_unreachable(arm.body.hir_id, arm.body.span, msg);
self.warn_if_unreachable(arm.body.hir_id, arm.body.span, "arm");
}
}
/// Handle the fallback arm of a desugared if(-let) like a missing else.
///
/// Returns `true` if there was an error forcing the coercion to the `()` type.
pub(crate) fn if_fallback_coercion<F, T>(
pub(super) fn if_fallback_coercion<T>(
&self,
span: Span,
then_expr: &'tcx hir::Expr<'tcx>,
coercion: &mut CoerceMany<'tcx, '_, T>,
ret_reason: F,
) -> bool
where
F: Fn(hir::HirId, Span) -> Option<(Span, String)>,
T: AsCoercionSite,
{
// If this `if` expr is the parent's function return expr,
// the cause of the type coercion is the return type, point at it. (#25228)
let ret_reason = ret_reason(then_expr.hir_id, span);
let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span);
let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse);
let mut error = false;
coercion.coerce_forced_unit(
@ -338,55 +274,34 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
error
}
pub(crate) fn coercion_reason_if(
&self,
hir_id: hir::HirId,
span: Span,
) -> Option<(Span, String)> {
self.coercion_reason_inner(hir_id, span, 1)
}
pub(crate) fn coercion_reason_match(
&self,
hir_id: hir::HirId,
span: Span,
) -> Option<(Span, String)> {
self.coercion_reason_inner(hir_id, span, 2)
}
fn coercion_reason_inner(
&self,
hir_id: hir::HirId,
span: Span,
parent_index: usize,
) -> Option<(Span, String)> {
let hir = self.tcx.hir();
let mut parent_iter = hir.parent_iter(hir_id);
let (_, node) = parent_iter.nth(parent_index)?;
match node {
hir::Node::Block(block) => {
let expr = block.expr?;
// check that the body's parent is an fn
let (_, parent) = parent_iter.nth(1)?;
if let hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. }) = parent {
// check that the `if` expr without `else` is the fn body's expr
if expr.span == span {
let (fn_decl, _) = self.get_fn_decl(hir_id)?;
fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> {
let node = {
let rslt = self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(hir_id));
self.tcx.hir().get(rslt)
};
if let hir::Node::Block(block) = node {
// check that the body's parent is an fn
let parent = self
.tcx
.hir()
.get(self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(block.hir_id)));
if let (Some(expr), hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) =
(&block.expr, parent)
{
// check that the `if` expr without `else` is the fn body's expr
if expr.span == sp {
return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| {
let span = fn_decl.output.span();
let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?;
return Some((
span,
format!("expected `{}` because of this return type", snippet),
));
}
Some((span, format!("expected `{}` because of this return type", snippet)))
});
}
None
}
hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) => {
Some((pat.span, "expected because of this assignment".to_string()))
}
_ => None,
}
if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node {
return Some((pat.span, "expected because of this assignment".to_string()));
}
None
}
pub(crate) fn if_cause(
@ -492,7 +407,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
)
}
fn demand_scrutinee_type(
pub(super) fn demand_scrutinee_type(
&self,
scrut: &'tcx hir::Expr<'tcx>,
contains_ref_bindings: Option<hir::Mutability>,

View File

@ -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 {

View File

@ -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

View File

@ -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(..)

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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

View File

@ -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: {
_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
bb19 (cleanup): {
drop(_1) -> bb13; // 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
_10 = discriminant(_1); // 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
}
bb21 (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
bb21: {
switchInt(_7) -> [false: bb16, otherwise: bb20]; // 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
_11 = discriminant(_1); // 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
}
bb23 (cleanup): {
switchInt(_7) -> [false: bb13, otherwise: bb22]; // scope 0 at $DIR/issue-41888.rs:15:1: 15:2
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}
}

View File

@ -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: {

View File

@ -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: {

View File

@ -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
}

View File

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

View File

@ -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

View File

@ -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`

View File

@ -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`

View File

@ -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(); }

View File

@ -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
|

View File

@ -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

View File

@ -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 ())

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -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!");
}
}

View File

@ -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

View File

@ -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.");

View File

@ -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

View File

@ -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 => {}

View File

@ -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`.

View File

@ -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

View File

@ -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`.

View File

@ -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);

View File

@ -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`.

View File

@ -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());
}

View File

@ -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
|

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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() {

View File

@ -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,

View File

@ -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",
);

View File

@ -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(..)

View File

@ -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,

View File

@ -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,

View File

@ -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);

View File

@ -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",

View File

@ -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,

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

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

View File

@ -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)

View File

@ -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()",
_ => ""

View File

@ -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 {

View File

@ -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);

View File

@ -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() {

View File

@ -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 {

View File

@ -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

View File

@ -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 wasnt 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 wasnt 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,
);
}

View File

@ -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
}

View File

@ -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,163 +44,176 @@ 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;
}
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,
};
// Top level or patterns aren't allowed in closures.
if matches!(some_pat.kind, PatKind::Or(_)) {
return;
}
let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
Some(expr) => expr,
None => return,
};
// 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;
}
// `map` won't perform any adjustments.
if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
return;
}
if !can_move_expr_to_closure(cx, some_expr) {
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));
let as_ref_str = match binding_ref {
Some(Mutability::Mut) => ".as_mut()",
Some(Mutability::Not) => ".as_ref()",
None => "",
};
let mut app = Applicability::MachineApplicable;
// 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()
};
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;
}
// `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
)
},
}
} 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(
if let ExprKind::Match(scrutinee, [then @ Arm { guard: None, .. }, r#else @ Arm { guard: None, .. }], _) =
expr.kind
{
manage_lint(
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,
expr,
(&then.pat.kind, then.body),
(&r#else.pat.kind, r#else.body),
scrutinee,
);
}
}
}
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;
}
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 (then_pat, then_expr) = then;
let (else_pat, else_expr) = r#else;
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,
};
// Top level or patterns aren't allowed in closures.
if matches!(some_pat.kind, PatKind::Or(_)) {
return;
}
let some_expr = match get_some_expr(cx, some_expr, expr_ctxt) {
Some(expr) => expr,
None => return,
};
if cx.typeck_results().expr_ty(some_expr) == cx.tcx.types.unit && !is_lint_allowed(cx, OPTION_MAP_UNIT_FN, expr.hir_id) {
return;
}
// `map` won't perform any adjustments.
if !cx.typeck_results().expr_adjustments(some_expr).is_empty() {
return;
}
if !can_move_expr_to_closure(cx, some_expr) {
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));
let as_ref_str = match binding_ref {
Some(Mutability::Mut) => ".as_mut()",
Some(Mutability::Not) => ".as_ref()",
None => "",
};
let mut app = Applicability::MachineApplicable;
// 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()
};
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;
}
// `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
)
},
}
} 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.
// Returns the function to be passed to `map` if it exists.
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.

View File

@ -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 {

View File

@ -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
}
}));
span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
if !expr.span.from_expansion() {
multispan_sugg(diag, msg, suggs);
}
});
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;
}
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());

View File

@ -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 {

View File

@ -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) {

Some files were not shown because too many files have changed in this diff Show More