Rollup merge of #119554 - matthewjasper:remove-guard-distinction, r=compiler-errors
Fix scoping for let chains in match guards If let guards were previously represented as a different type of guard in HIR and THIR. This meant that let chains in match guards were not handled correctly because they were treated exactly like normal guards. - Remove `hir::Guard` and `thir::Guard`. - Make the scoping different between normal guards and if let guards also check for let chains. closes #118593
This commit is contained in:
commit
958417fba1
34 changed files with 272 additions and 292 deletions
|
@ -546,20 +546,7 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||||
|
|
||||||
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm<'hir> {
|
||||||
let pat = self.lower_pat(&arm.pat);
|
let pat = self.lower_pat(&arm.pat);
|
||||||
let guard = arm.guard.as_ref().map(|cond| {
|
let guard = arm.guard.as_ref().map(|cond| self.lower_expr(cond));
|
||||||
if let ExprKind::Let(pat, scrutinee, span, is_recovered) = &cond.kind {
|
|
||||||
hir::Guard::IfLet(self.arena.alloc(hir::Let {
|
|
||||||
hir_id: self.next_id(),
|
|
||||||
span: self.lower_span(*span),
|
|
||||||
pat: self.lower_pat(pat),
|
|
||||||
ty: None,
|
|
||||||
init: self.lower_expr(scrutinee),
|
|
||||||
is_recovered: *is_recovered,
|
|
||||||
}))
|
|
||||||
} else {
|
|
||||||
hir::Guard::If(self.lower_expr(cond))
|
|
||||||
}
|
|
||||||
});
|
|
||||||
let hir_id = self.next_id();
|
let hir_id = self.next_id();
|
||||||
let span = self.lower_span(arm.span);
|
let span = self.lower_span(arm.span);
|
||||||
self.lower_attrs(hir_id, &arm.attrs);
|
self.lower_attrs(hir_id, &arm.attrs);
|
||||||
|
|
|
@ -3590,7 +3590,7 @@ impl<'b, 'v> Visitor<'v> for ConditionVisitor<'b> {
|
||||||
));
|
));
|
||||||
} else if let Some(guard) = &arm.guard {
|
} else if let Some(guard) = &arm.guard {
|
||||||
self.errors.push((
|
self.errors.push((
|
||||||
arm.pat.span.to(guard.body().span),
|
arm.pat.span.to(guard.span),
|
||||||
format!(
|
format!(
|
||||||
"if this pattern and condition are matched, {} is not \
|
"if this pattern and condition are matched, {} is not \
|
||||||
initialized",
|
initialized",
|
||||||
|
|
|
@ -1258,7 +1258,7 @@ pub struct Arm<'hir> {
|
||||||
/// If this pattern and the optional guard matches, then `body` is evaluated.
|
/// If this pattern and the optional guard matches, then `body` is evaluated.
|
||||||
pub pat: &'hir Pat<'hir>,
|
pub pat: &'hir Pat<'hir>,
|
||||||
/// Optional guard clause.
|
/// Optional guard clause.
|
||||||
pub guard: Option<Guard<'hir>>,
|
pub guard: Option<&'hir Expr<'hir>>,
|
||||||
/// The expression the arm evaluates to if this arm matches.
|
/// The expression the arm evaluates to if this arm matches.
|
||||||
pub body: &'hir Expr<'hir>,
|
pub body: &'hir Expr<'hir>,
|
||||||
}
|
}
|
||||||
|
@ -1280,26 +1280,6 @@ pub struct Let<'hir> {
|
||||||
pub is_recovered: Option<ErrorGuaranteed>,
|
pub is_recovered: Option<ErrorGuaranteed>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
|
||||||
pub enum Guard<'hir> {
|
|
||||||
If(&'hir Expr<'hir>),
|
|
||||||
IfLet(&'hir Let<'hir>),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'hir> Guard<'hir> {
|
|
||||||
/// Returns the body of the guard
|
|
||||||
///
|
|
||||||
/// In other words, returns the e in either of the following:
|
|
||||||
///
|
|
||||||
/// - `if e`
|
|
||||||
/// - `if let x = e`
|
|
||||||
pub fn body(&self) -> &'hir Expr<'hir> {
|
|
||||||
match self {
|
|
||||||
Guard::If(e) | Guard::IfLet(Let { init: e, .. }) => e,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
#[derive(Debug, Clone, Copy, HashStable_Generic)]
|
||||||
pub struct ExprField<'hir> {
|
pub struct ExprField<'hir> {
|
||||||
#[stable_hasher(ignore)]
|
#[stable_hasher(ignore)]
|
||||||
|
|
|
@ -619,13 +619,8 @@ pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) {
|
||||||
pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) {
|
pub fn walk_arm<'v, V: Visitor<'v>>(visitor: &mut V, arm: &'v Arm<'v>) {
|
||||||
visitor.visit_id(arm.hir_id);
|
visitor.visit_id(arm.hir_id);
|
||||||
visitor.visit_pat(arm.pat);
|
visitor.visit_pat(arm.pat);
|
||||||
if let Some(ref g) = arm.guard {
|
if let Some(ref e) = arm.guard {
|
||||||
match g {
|
visitor.visit_expr(e);
|
||||||
Guard::If(ref e) => visitor.visit_expr(e),
|
|
||||||
Guard::IfLet(ref l) => {
|
|
||||||
visitor.visit_let_expr(l);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
visitor.visit_expr(arm.body);
|
visitor.visit_expr(arm.body);
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,6 +177,14 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
|
fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) {
|
||||||
|
fn has_let_expr(expr: &Expr<'_>) -> bool {
|
||||||
|
match &expr.kind {
|
||||||
|
hir::ExprKind::Binary(_, lhs, rhs) => has_let_expr(lhs) || has_let_expr(rhs),
|
||||||
|
hir::ExprKind::Let(..) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let prev_cx = visitor.cx;
|
let prev_cx = visitor.cx;
|
||||||
|
|
||||||
visitor.terminating_scopes.insert(arm.hir_id.local_id);
|
visitor.terminating_scopes.insert(arm.hir_id.local_id);
|
||||||
|
@ -184,7 +192,9 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir
|
||||||
visitor.enter_node_scope_with_dtor(arm.hir_id.local_id);
|
visitor.enter_node_scope_with_dtor(arm.hir_id.local_id);
|
||||||
visitor.cx.var_parent = visitor.cx.parent;
|
visitor.cx.var_parent = visitor.cx.parent;
|
||||||
|
|
||||||
if let Some(hir::Guard::If(expr)) = arm.guard {
|
if let Some(expr) = arm.guard
|
||||||
|
&& !has_let_expr(expr)
|
||||||
|
{
|
||||||
visitor.terminating_scopes.insert(expr.hir_id.local_id);
|
visitor.terminating_scopes.insert(expr.hir_id.local_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1874,18 +1874,10 @@ impl<'a> State<'a> {
|
||||||
self.print_pat(arm.pat);
|
self.print_pat(arm.pat);
|
||||||
self.space();
|
self.space();
|
||||||
if let Some(ref g) = arm.guard {
|
if let Some(ref g) = arm.guard {
|
||||||
match *g {
|
|
||||||
hir::Guard::If(e) => {
|
|
||||||
self.word_space("if");
|
self.word_space("if");
|
||||||
self.print_expr(e);
|
self.print_expr(g);
|
||||||
self.space();
|
self.space();
|
||||||
}
|
}
|
||||||
hir::Guard::IfLet(&hir::Let { pat, ty, init, .. }) => {
|
|
||||||
self.word_nbsp("if");
|
|
||||||
self.print_let(pat, ty, init);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.word_space("=>");
|
self.word_space("=>");
|
||||||
|
|
||||||
match arm.body.kind {
|
match arm.body.kind {
|
||||||
|
|
|
@ -78,17 +78,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
let mut other_arms = vec![]; // Used only for diagnostics.
|
let mut other_arms = vec![]; // Used only for diagnostics.
|
||||||
let mut prior_arm = None;
|
let mut prior_arm = None;
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
if let Some(g) = &arm.guard {
|
if let Some(e) = &arm.guard {
|
||||||
self.diverges.set(Diverges::Maybe);
|
self.diverges.set(Diverges::Maybe);
|
||||||
match g {
|
|
||||||
hir::Guard::If(e) => {
|
|
||||||
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
|
self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {});
|
||||||
}
|
}
|
||||||
hir::Guard::IfLet(l) => {
|
|
||||||
self.check_expr_let(l);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
self.diverges.set(Diverges::Maybe);
|
self.diverges.set(Diverges::Maybe);
|
||||||
|
|
||||||
|
|
|
@ -669,12 +669,8 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
self.walk_pat(discr_place, arm.pat, arm.guard.is_some());
|
self.walk_pat(discr_place, arm.pat, arm.guard.is_some());
|
||||||
|
|
||||||
match arm.guard {
|
if let Some(ref e) = arm.guard {
|
||||||
Some(hir::Guard::If(e)) => self.consume_expr(e),
|
self.consume_expr(e)
|
||||||
Some(hir::Guard::IfLet(l)) => {
|
|
||||||
self.walk_local(l.init, l.pat, None, |t| t.borrow_expr(l.init, ty::ImmBorrow))
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.consume_expr(arm.body);
|
self.consume_expr(arm.body);
|
||||||
|
|
|
@ -519,20 +519,13 @@ pub struct FruInfo<'tcx> {
|
||||||
#[derive(Clone, Debug, HashStable)]
|
#[derive(Clone, Debug, HashStable)]
|
||||||
pub struct Arm<'tcx> {
|
pub struct Arm<'tcx> {
|
||||||
pub pattern: Box<Pat<'tcx>>,
|
pub pattern: Box<Pat<'tcx>>,
|
||||||
pub guard: Option<Guard<'tcx>>,
|
pub guard: Option<ExprId>,
|
||||||
pub body: ExprId,
|
pub body: ExprId,
|
||||||
pub lint_level: LintLevel,
|
pub lint_level: LintLevel,
|
||||||
pub scope: region::Scope,
|
pub scope: region::Scope,
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A `match` guard.
|
|
||||||
#[derive(Clone, Debug, HashStable)]
|
|
||||||
pub enum Guard<'tcx> {
|
|
||||||
If(ExprId),
|
|
||||||
IfLet(Box<Pat<'tcx>>, ExprId),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, HashStable)]
|
#[derive(Copy, Clone, Debug, HashStable)]
|
||||||
pub enum LogicalOp {
|
pub enum LogicalOp {
|
||||||
/// The `&&` operator.
|
/// The `&&` operator.
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{
|
use super::{
|
||||||
AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, Guard, InlineAsmExpr, InlineAsmOperand, Pat,
|
AdtExpr, Arm, Block, ClosureExpr, Expr, ExprKind, InlineAsmExpr, InlineAsmOperand, Pat,
|
||||||
PatKind, Stmt, StmtKind, Thir,
|
PatKind, Stmt, StmtKind, Thir,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -213,13 +213,8 @@ pub fn walk_arm<'thir, 'tcx: 'thir, V: Visitor<'thir, 'tcx>>(
|
||||||
visitor: &mut V,
|
visitor: &mut V,
|
||||||
arm: &'thir Arm<'tcx>,
|
arm: &'thir Arm<'tcx>,
|
||||||
) {
|
) {
|
||||||
match arm.guard {
|
if let Some(expr) = arm.guard {
|
||||||
Some(Guard::If(expr)) => visitor.visit_expr(&visitor.thir()[expr]),
|
visitor.visit_expr(&visitor.thir()[expr])
|
||||||
Some(Guard::IfLet(ref pat, expr)) => {
|
|
||||||
visitor.visit_pat(pat);
|
|
||||||
visitor.visit_expr(&visitor.thir()[expr]);
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
}
|
||||||
visitor.visit_pat(&arm.pattern);
|
visitor.visit_pat(&arm.pattern);
|
||||||
visitor.visit_expr(&visitor.thir()[arm.body]);
|
visitor.visit_expr(&visitor.thir()[arm.body]);
|
||||||
|
|
|
@ -82,7 +82,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
cond,
|
cond,
|
||||||
Some(condition_scope),
|
Some(condition_scope),
|
||||||
condition_scope,
|
condition_scope,
|
||||||
source_info
|
source_info,
|
||||||
|
true,
|
||||||
));
|
));
|
||||||
|
|
||||||
this.expr_into_dest(destination, then_blk, then)
|
this.expr_into_dest(destination, then_blk, then)
|
||||||
|
@ -173,6 +174,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
Some(condition_scope),
|
Some(condition_scope),
|
||||||
condition_scope,
|
condition_scope,
|
||||||
source_info,
|
source_info,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let (short_circuit, continuation, constant) = match op {
|
let (short_circuit, continuation, constant) = match op {
|
||||||
|
|
|
@ -33,6 +33,12 @@ use std::borrow::Borrow;
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
|
/// Lowers a condition in a way that ensures that variables bound in any let
|
||||||
|
/// expressions are definitely initialized in the if body.
|
||||||
|
///
|
||||||
|
/// If `declare_bindings` is false then variables created in `let`
|
||||||
|
/// expressions will not be declared. This is for if let guards on arms with
|
||||||
|
/// an or pattern, where the guard is lowered multiple times.
|
||||||
pub(crate) fn then_else_break(
|
pub(crate) fn then_else_break(
|
||||||
&mut self,
|
&mut self,
|
||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
|
@ -40,6 +46,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override: Option<region::Scope>,
|
temp_scope_override: Option<region::Scope>,
|
||||||
break_scope: region::Scope,
|
break_scope: region::Scope,
|
||||||
variable_source_info: SourceInfo,
|
variable_source_info: SourceInfo,
|
||||||
|
declare_bindings: bool,
|
||||||
) -> BlockAnd<()> {
|
) -> BlockAnd<()> {
|
||||||
let this = self;
|
let this = self;
|
||||||
let expr = &this.thir[expr_id];
|
let expr = &this.thir[expr_id];
|
||||||
|
@ -53,6 +60,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override,
|
temp_scope_override,
|
||||||
break_scope,
|
break_scope,
|
||||||
variable_source_info,
|
variable_source_info,
|
||||||
|
declare_bindings,
|
||||||
));
|
));
|
||||||
|
|
||||||
let rhs_then_block = unpack!(this.then_else_break(
|
let rhs_then_block = unpack!(this.then_else_break(
|
||||||
|
@ -61,6 +69,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override,
|
temp_scope_override,
|
||||||
break_scope,
|
break_scope,
|
||||||
variable_source_info,
|
variable_source_info,
|
||||||
|
declare_bindings,
|
||||||
));
|
));
|
||||||
|
|
||||||
rhs_then_block.unit()
|
rhs_then_block.unit()
|
||||||
|
@ -75,6 +84,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override,
|
temp_scope_override,
|
||||||
local_scope,
|
local_scope,
|
||||||
variable_source_info,
|
variable_source_info,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let rhs_success_block = unpack!(this.then_else_break(
|
let rhs_success_block = unpack!(this.then_else_break(
|
||||||
|
@ -83,6 +93,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override,
|
temp_scope_override,
|
||||||
break_scope,
|
break_scope,
|
||||||
variable_source_info,
|
variable_source_info,
|
||||||
|
true,
|
||||||
));
|
));
|
||||||
this.cfg.goto(lhs_success_block, variable_source_info, rhs_success_block);
|
this.cfg.goto(lhs_success_block, variable_source_info, rhs_success_block);
|
||||||
rhs_success_block.unit()
|
rhs_success_block.unit()
|
||||||
|
@ -102,6 +113,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override,
|
temp_scope_override,
|
||||||
local_scope,
|
local_scope,
|
||||||
variable_source_info,
|
variable_source_info,
|
||||||
|
true,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
this.break_for_else(success_block, break_scope, variable_source_info);
|
this.break_for_else(success_block, break_scope, variable_source_info);
|
||||||
|
@ -116,6 +128,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override,
|
temp_scope_override,
|
||||||
break_scope,
|
break_scope,
|
||||||
variable_source_info,
|
variable_source_info,
|
||||||
|
declare_bindings,
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -125,6 +138,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
temp_scope_override,
|
temp_scope_override,
|
||||||
break_scope,
|
break_scope,
|
||||||
variable_source_info,
|
variable_source_info,
|
||||||
|
declare_bindings,
|
||||||
),
|
),
|
||||||
ExprKind::Let { expr, ref pat } => this.lower_let_expr(
|
ExprKind::Let { expr, ref pat } => this.lower_let_expr(
|
||||||
block,
|
block,
|
||||||
|
@ -133,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
break_scope,
|
break_scope,
|
||||||
Some(variable_source_info.scope),
|
Some(variable_source_info.scope),
|
||||||
variable_source_info.span,
|
variable_source_info.span,
|
||||||
true,
|
declare_bindings,
|
||||||
),
|
),
|
||||||
_ => {
|
_ => {
|
||||||
let temp_scope = temp_scope_override.unwrap_or_else(|| this.local_scope());
|
let temp_scope = temp_scope_override.unwrap_or_else(|| this.local_scope());
|
||||||
|
@ -417,7 +431,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
None,
|
None,
|
||||||
arm.span,
|
arm.span,
|
||||||
&arm.pattern,
|
&arm.pattern,
|
||||||
arm.guard.as_ref(),
|
arm.guard,
|
||||||
opt_scrutinee_place,
|
opt_scrutinee_place,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -709,7 +723,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
mut visibility_scope: Option<SourceScope>,
|
mut visibility_scope: Option<SourceScope>,
|
||||||
scope_span: Span,
|
scope_span: Span,
|
||||||
pattern: &Pat<'tcx>,
|
pattern: &Pat<'tcx>,
|
||||||
guard: Option<&Guard<'tcx>>,
|
guard: Option<ExprId>,
|
||||||
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
|
opt_match_place: Option<(Option<&Place<'tcx>>, Span)>,
|
||||||
) -> Option<SourceScope> {
|
) -> Option<SourceScope> {
|
||||||
self.visit_primary_bindings(
|
self.visit_primary_bindings(
|
||||||
|
@ -737,11 +751,38 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if let Some(Guard::IfLet(guard_pat, _)) = guard {
|
if let Some(guard_expr) = guard {
|
||||||
|
self.declare_guard_bindings(guard_expr, scope_span, visibility_scope);
|
||||||
|
}
|
||||||
|
visibility_scope
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Declare bindings in a guard. This has to be done when declaring bindings
|
||||||
|
/// for an arm to ensure that or patterns only have one version of each
|
||||||
|
/// variable.
|
||||||
|
pub(crate) fn declare_guard_bindings(
|
||||||
|
&mut self,
|
||||||
|
guard_expr: ExprId,
|
||||||
|
scope_span: Span,
|
||||||
|
visibility_scope: Option<SourceScope>,
|
||||||
|
) {
|
||||||
|
match self.thir.exprs[guard_expr].kind {
|
||||||
|
ExprKind::Let { expr: _, pat: ref guard_pat } => {
|
||||||
// FIXME: pass a proper `opt_match_place`
|
// FIXME: pass a proper `opt_match_place`
|
||||||
self.declare_bindings(visibility_scope, scope_span, guard_pat, None, None);
|
self.declare_bindings(visibility_scope, scope_span, guard_pat, None, None);
|
||||||
}
|
}
|
||||||
visibility_scope
|
ExprKind::Scope { value, .. } => {
|
||||||
|
self.declare_guard_bindings(value, scope_span, visibility_scope);
|
||||||
|
}
|
||||||
|
ExprKind::Use { source } => {
|
||||||
|
self.declare_guard_bindings(source, scope_span, visibility_scope);
|
||||||
|
}
|
||||||
|
ExprKind::LogicalOp { op: LogicalOp::And, lhs, rhs } => {
|
||||||
|
self.declare_guard_bindings(lhs, scope_span, visibility_scope);
|
||||||
|
self.declare_guard_bindings(rhs, scope_span, visibility_scope);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn storage_live_binding(
|
pub(crate) fn storage_live_binding(
|
||||||
|
@ -2009,7 +2050,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
// * So we eagerly create the reference for the arm and then take a
|
// * So we eagerly create the reference for the arm and then take a
|
||||||
// reference to that.
|
// reference to that.
|
||||||
if let Some((arm, match_scope)) = arm_match_scope
|
if let Some((arm, match_scope)) = arm_match_scope
|
||||||
&& let Some(guard) = &arm.guard
|
&& let Some(guard) = arm.guard
|
||||||
{
|
{
|
||||||
let tcx = self.tcx;
|
let tcx = self.tcx;
|
||||||
let bindings = parent_bindings
|
let bindings = parent_bindings
|
||||||
|
@ -2034,21 +2075,16 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
let mut guard_span = rustc_span::DUMMY_SP;
|
let mut guard_span = rustc_span::DUMMY_SP;
|
||||||
|
|
||||||
let (post_guard_block, otherwise_post_guard_block) =
|
let (post_guard_block, otherwise_post_guard_block) =
|
||||||
self.in_if_then_scope(match_scope, guard_span, |this| match *guard {
|
self.in_if_then_scope(match_scope, guard_span, |this| {
|
||||||
Guard::If(e) => {
|
guard_span = this.thir[guard].span;
|
||||||
guard_span = this.thir[e].span;
|
|
||||||
this.then_else_break(
|
this.then_else_break(
|
||||||
block,
|
block,
|
||||||
e,
|
guard,
|
||||||
None,
|
None,
|
||||||
match_scope,
|
match_scope,
|
||||||
this.source_info(arm.span),
|
this.source_info(arm.span),
|
||||||
|
false,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
Guard::IfLet(ref pat, s) => {
|
|
||||||
guard_span = this.thir[s].span;
|
|
||||||
this.lower_let_expr(block, s, pat, match_scope, None, arm.span, false)
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let source_info = self.source_info(guard_span);
|
let source_info = self.source_info(guard_span);
|
||||||
|
|
|
@ -855,13 +855,8 @@ impl<'tcx> Cx<'tcx> {
|
||||||
|
|
||||||
fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId {
|
fn convert_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) -> ArmId {
|
||||||
let arm = Arm {
|
let arm = Arm {
|
||||||
pattern: self.pattern_from_hir(arm.pat),
|
pattern: self.pattern_from_hir(&arm.pat),
|
||||||
guard: arm.guard.as_ref().map(|g| match g {
|
guard: arm.guard.as_ref().map(|g| self.mirror_expr(g)),
|
||||||
hir::Guard::If(e) => Guard::If(self.mirror_expr(e)),
|
|
||||||
hir::Guard::IfLet(l) => {
|
|
||||||
Guard::IfLet(self.pattern_from_hir(l.pat), self.mirror_expr(l.init))
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
body: self.mirror_expr(arm.body),
|
body: self.mirror_expr(arm.body),
|
||||||
lint_level: LintLevel::Explicit(arm.hir_id),
|
lint_level: LintLevel::Explicit(arm.hir_id),
|
||||||
scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
|
scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
|
||||||
|
|
|
@ -100,21 +100,11 @@ impl<'p, 'tcx> Visitor<'p, 'tcx> for MatchVisitor<'p, 'tcx> {
|
||||||
#[instrument(level = "trace", skip(self))]
|
#[instrument(level = "trace", skip(self))]
|
||||||
fn visit_arm(&mut self, arm: &'p Arm<'tcx>) {
|
fn visit_arm(&mut self, arm: &'p Arm<'tcx>) {
|
||||||
self.with_lint_level(arm.lint_level, |this| {
|
self.with_lint_level(arm.lint_level, |this| {
|
||||||
match arm.guard {
|
if let Some(expr) = arm.guard {
|
||||||
Some(Guard::If(expr)) => {
|
|
||||||
this.with_let_source(LetSource::IfLetGuard, |this| {
|
this.with_let_source(LetSource::IfLetGuard, |this| {
|
||||||
this.visit_expr(&this.thir[expr])
|
this.visit_expr(&this.thir[expr])
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Some(Guard::IfLet(ref pat, expr)) => {
|
|
||||||
this.with_let_source(LetSource::IfLetGuard, |this| {
|
|
||||||
this.check_let(pat, Some(expr), pat.span);
|
|
||||||
this.visit_pat(pat);
|
|
||||||
this.visit_expr(&this.thir[expr]);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
None => {}
|
|
||||||
}
|
|
||||||
this.visit_pat(&arm.pattern);
|
this.visit_pat(&arm.pattern);
|
||||||
this.visit_expr(&self.thir[arm.body]);
|
this.visit_expr(&self.thir[arm.body]);
|
||||||
});
|
});
|
||||||
|
|
|
@ -593,9 +593,9 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
|
||||||
print_indented!(self, "pattern: ", depth_lvl + 1);
|
print_indented!(self, "pattern: ", depth_lvl + 1);
|
||||||
self.print_pat(pattern, depth_lvl + 2);
|
self.print_pat(pattern, depth_lvl + 2);
|
||||||
|
|
||||||
if let Some(guard) = guard {
|
if let Some(guard) = *guard {
|
||||||
print_indented!(self, "guard: ", depth_lvl + 1);
|
print_indented!(self, "guard: ", depth_lvl + 1);
|
||||||
self.print_guard(guard, depth_lvl + 2);
|
self.print_expr(guard, depth_lvl + 2);
|
||||||
} else {
|
} else {
|
||||||
print_indented!(self, "guard: None", depth_lvl + 1);
|
print_indented!(self, "guard: None", depth_lvl + 1);
|
||||||
}
|
}
|
||||||
|
@ -764,27 +764,6 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
|
||||||
print_indented!(self, "}", depth_lvl);
|
print_indented!(self, "}", depth_lvl);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_guard(&mut self, guard: &Guard<'tcx>, depth_lvl: usize) {
|
|
||||||
print_indented!(self, "Guard {", depth_lvl);
|
|
||||||
|
|
||||||
match guard {
|
|
||||||
Guard::If(expr_id) => {
|
|
||||||
print_indented!(self, "If (", depth_lvl + 1);
|
|
||||||
self.print_expr(*expr_id, depth_lvl + 2);
|
|
||||||
print_indented!(self, ")", depth_lvl + 1);
|
|
||||||
}
|
|
||||||
Guard::IfLet(pat, expr_id) => {
|
|
||||||
print_indented!(self, "IfLet (", depth_lvl + 1);
|
|
||||||
self.print_pat(pat, depth_lvl + 2);
|
|
||||||
print_indented!(self, ",", depth_lvl + 1);
|
|
||||||
self.print_expr(*expr_id, depth_lvl + 2);
|
|
||||||
print_indented!(self, ")", depth_lvl + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
print_indented!(self, "}", depth_lvl);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn print_closure_expr(&mut self, expr: &ClosureExpr<'tcx>, depth_lvl: usize) {
|
fn print_closure_expr(&mut self, expr: &ClosureExpr<'tcx>, depth_lvl: usize) {
|
||||||
let ClosureExpr { closure_id, args, upvars, movability, fake_reads } = expr;
|
let ClosureExpr { closure_id, args, upvars, movability, fake_reads } = expr;
|
||||||
|
|
||||||
|
|
|
@ -351,10 +351,7 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
||||||
self.add_from_pat(arm.pat);
|
self.add_from_pat(&arm.pat);
|
||||||
if let Some(hir::Guard::IfLet(let_expr)) = arm.guard {
|
|
||||||
self.add_from_pat(let_expr.pat);
|
|
||||||
}
|
|
||||||
intravisit::walk_arm(self, arm);
|
intravisit::walk_arm(self, arm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -921,14 +918,11 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
let body_succ = self.propagate_through_expr(arm.body, succ);
|
let body_succ = self.propagate_through_expr(arm.body, succ);
|
||||||
|
|
||||||
let guard_succ = arm.guard.as_ref().map_or(body_succ, |g| match g {
|
let guard_succ = arm
|
||||||
hir::Guard::If(e) => self.propagate_through_expr(e, body_succ),
|
.guard
|
||||||
hir::Guard::IfLet(let_expr) => {
|
.as_ref()
|
||||||
let let_bind = self.define_bindings_in_pat(let_expr.pat, body_succ);
|
.map_or(body_succ, |g| self.propagate_through_expr(g, body_succ));
|
||||||
self.propagate_through_expr(let_expr.init, let_bind)
|
let arm_succ = self.define_bindings_in_pat(&arm.pat, guard_succ);
|
||||||
}
|
|
||||||
});
|
|
||||||
let arm_succ = self.define_bindings_in_pat(arm.pat, guard_succ);
|
|
||||||
self.merge_from_succ(ln, arm_succ);
|
self.merge_from_succ(ln, arm_succ);
|
||||||
}
|
}
|
||||||
self.propagate_through_expr(e, ln)
|
self.propagate_through_expr(e, ln)
|
||||||
|
@ -1328,9 +1322,6 @@ impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
|
||||||
|
|
||||||
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
||||||
self.check_unused_vars_in_pat(arm.pat, None, None, |_, _, _, _| {});
|
self.check_unused_vars_in_pat(arm.pat, None, None, |_, _, _, _| {});
|
||||||
if let Some(hir::Guard::IfLet(let_expr)) = arm.guard {
|
|
||||||
self.check_unused_vars_in_pat(let_expr.pat, None, None, |_, _, _, _| {});
|
|
||||||
}
|
|
||||||
intravisit::walk_arm(self, arm);
|
intravisit::walk_arm(self, arm);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use core::fmt::{self, Write};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::hir_id::HirIdSet;
|
use rustc_hir::hir_id::HirIdSet;
|
||||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||||
use rustc_hir::{Block, Expr, ExprKind, Guard, HirId, Let, Pat, Stmt, StmtKind, UnOp};
|
use rustc_hir::{Block, Expr, ExprKind, HirId, Pat, Stmt, StmtKind, UnOp};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
|
use rustc_span::{Span, SyntaxContext, DUMMY_SP};
|
||||||
|
@ -465,7 +465,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
||||||
let mut is_map_used = self.is_map_used;
|
let mut is_map_used = self.is_map_used;
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
self.visit_pat(arm.pat);
|
self.visit_pat(arm.pat);
|
||||||
if let Some(Guard::If(guard) | Guard::IfLet(&Let { init: guard, .. })) = arm.guard {
|
if let Some(guard) = arm.guard {
|
||||||
self.visit_non_tail_expr(guard);
|
self.visit_non_tail_expr(guard);
|
||||||
}
|
}
|
||||||
is_map_used |= self.visit_cond_arm(arm.body);
|
is_map_used |= self.visit_cond_arm(arm.body);
|
||||||
|
|
|
@ -11,7 +11,7 @@ use clippy_utils::{
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_errors::{Applicability, Diagnostic};
|
use rustc_errors::{Applicability, Diagnostic};
|
||||||
use rustc_hir::def::Res;
|
use rustc_hir::def::Res;
|
||||||
use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, Guard, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind};
|
use rustc_hir::{Arm, BinOpKind, Block, Expr, ExprKind, HirId, PatKind, PathSegment, PrimTy, QPath, StmtKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_session::impl_lint_pass;
|
use rustc_session::impl_lint_pass;
|
||||||
|
@ -394,7 +394,7 @@ fn is_match_pattern<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Opt
|
||||||
// Find possible min/max branches
|
// Find possible min/max branches
|
||||||
let minmax_values = |a: &'tcx Arm<'tcx>| {
|
let minmax_values = |a: &'tcx Arm<'tcx>| {
|
||||||
if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
|
if let PatKind::Binding(_, var_hir_id, _, None) = &a.pat.kind
|
||||||
&& let Some(Guard::If(e)) = a.guard
|
&& let Some(e) = a.guard
|
||||||
{
|
{
|
||||||
Some((e, var_hir_id, a.body))
|
Some((e, var_hir_id, a.body))
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,7 +7,7 @@ use clippy_utils::{
|
||||||
};
|
};
|
||||||
use rustc_errors::MultiSpan;
|
use rustc_errors::MultiSpan;
|
||||||
use rustc_hir::LangItem::OptionNone;
|
use rustc_hir::LangItem::OptionNone;
|
||||||
use rustc_hir::{Arm, Expr, Guard, HirId, Let, Pat, PatKind};
|
use rustc_hir::{Arm, Expr, HirId, Pat, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ use super::COLLAPSIBLE_MATCH;
|
||||||
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
|
||||||
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
if let Some(els_arm) = arms.iter().rfind(|arm| arm_is_wild_like(cx, arm)) {
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
check_arm(cx, true, arm.pat, arm.body, arm.guard.as_ref(), Some(els_arm.body));
|
check_arm(cx, true, arm.pat, arm.body, arm.guard, Some(els_arm.body));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ fn check_arm<'tcx>(
|
||||||
outer_is_match: bool,
|
outer_is_match: bool,
|
||||||
outer_pat: &'tcx Pat<'tcx>,
|
outer_pat: &'tcx Pat<'tcx>,
|
||||||
outer_then_body: &'tcx Expr<'tcx>,
|
outer_then_body: &'tcx Expr<'tcx>,
|
||||||
outer_guard: Option<&'tcx Guard<'tcx>>,
|
outer_guard: Option<&'tcx Expr<'tcx>>,
|
||||||
outer_else_body: Option<&'tcx Expr<'tcx>>,
|
outer_else_body: Option<&'tcx Expr<'tcx>>,
|
||||||
) {
|
) {
|
||||||
let inner_expr = peel_blocks_with_stmt(outer_then_body);
|
let inner_expr = peel_blocks_with_stmt(outer_then_body);
|
||||||
|
@ -71,7 +71,7 @@ fn check_arm<'tcx>(
|
||||||
// the binding must not be used in the if guard
|
// the binding must not be used in the if guard
|
||||||
&& outer_guard.map_or(
|
&& outer_guard.map_or(
|
||||||
true,
|
true,
|
||||||
|(Guard::If(e) | Guard::IfLet(Let { init: e, .. }))| !is_local_used(cx, *e, binding_id)
|
|e| !is_local_used(cx, e, binding_id)
|
||||||
)
|
)
|
||||||
// ...or anywhere in the inner expression
|
// ...or anywhere in the inner expression
|
||||||
&& match inner {
|
&& match inner {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use clippy_utils::source::snippet_with_applicability;
|
||||||
use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment};
|
use clippy_utils::{is_lint_allowed, is_wild, span_contains_comment};
|
||||||
use rustc_ast::{Attribute, LitKind};
|
use rustc_ast::{Attribute, LitKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Guard, Pat, PatKind, QPath};
|
use rustc_hir::{Arm, BorrowKind, Expr, ExprKind, Pat, PatKind, QPath};
|
||||||
use rustc_lint::{LateContext, LintContext};
|
use rustc_lint::{LateContext, LintContext};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_span::source_map::Spanned;
|
use rustc_span::source_map::Spanned;
|
||||||
|
@ -41,14 +41,8 @@ pub(super) fn check_match<'tcx>(
|
||||||
find_matches_sugg(
|
find_matches_sugg(
|
||||||
cx,
|
cx,
|
||||||
scrutinee,
|
scrutinee,
|
||||||
arms.iter().map(|arm| {
|
arms.iter()
|
||||||
(
|
.map(|arm| (cx.tcx.hir().attrs(arm.hir_id), Some(arm.pat), arm.body, arm.guard)),
|
||||||
cx.tcx.hir().attrs(arm.hir_id),
|
|
||||||
Some(arm.pat),
|
|
||||||
arm.body,
|
|
||||||
arm.guard.as_ref(),
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
e,
|
e,
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
@ -67,14 +61,7 @@ where
|
||||||
I: Clone
|
I: Clone
|
||||||
+ DoubleEndedIterator
|
+ DoubleEndedIterator
|
||||||
+ ExactSizeIterator
|
+ ExactSizeIterator
|
||||||
+ Iterator<
|
+ Iterator<Item = (&'a [Attribute], Option<&'a Pat<'b>>, &'a Expr<'b>, Option<&'a Expr<'b>>)>,
|
||||||
Item = (
|
|
||||||
&'a [Attribute],
|
|
||||||
Option<&'a Pat<'b>>,
|
|
||||||
&'a Expr<'b>,
|
|
||||||
Option<&'a Guard<'b>>,
|
|
||||||
),
|
|
||||||
>,
|
|
||||||
{
|
{
|
||||||
if !span_contains_comment(cx.sess().source_map(), expr.span)
|
if !span_contains_comment(cx.sess().source_map(), expr.span)
|
||||||
&& iter.len() >= 2
|
&& iter.len() >= 2
|
||||||
|
@ -115,7 +102,7 @@ where
|
||||||
})
|
})
|
||||||
.join(" | ")
|
.join(" | ")
|
||||||
};
|
};
|
||||||
let pat_and_guard = if let Some(Guard::If(g)) = first_guard {
|
let pat_and_guard = if let Some(g) = first_guard {
|
||||||
format!(
|
format!(
|
||||||
"{pat} if {}",
|
"{pat} if {}",
|
||||||
snippet_with_applicability(cx, g.span, "..", &mut applicability)
|
snippet_with_applicability(cx, g.span, "..", &mut applicability)
|
||||||
|
|
|
@ -8,7 +8,7 @@ use clippy_utils::{
|
||||||
};
|
};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::LangItem::OptionNone;
|
use rustc_hir::LangItem::OptionNone;
|
||||||
use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, Guard, ItemKind, Node, Pat, PatKind, Path, QPath};
|
use rustc_hir::{Arm, BindingAnnotation, ByRef, Expr, ExprKind, ItemKind, Node, Pat, PatKind, Path, QPath};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::sym;
|
use rustc_span::sym;
|
||||||
|
|
||||||
|
@ -66,18 +66,9 @@ fn check_all_arms(cx: &LateContext<'_>, match_expr: &Expr<'_>, arms: &[Arm<'_>])
|
||||||
let arm_expr = peel_blocks_with_stmt(arm.body);
|
let arm_expr = peel_blocks_with_stmt(arm.body);
|
||||||
|
|
||||||
if let Some(guard_expr) = &arm.guard {
|
if let Some(guard_expr) = &arm.guard {
|
||||||
match guard_expr {
|
if guard_expr.can_have_side_effects() {
|
||||||
// gives up if `pat if expr` can have side effects
|
|
||||||
Guard::If(if_cond) => {
|
|
||||||
if if_cond.can_have_side_effects() {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
},
|
|
||||||
// gives up `pat if let ...` arm
|
|
||||||
Guard::IfLet(_) => {
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let PatKind::Wild = arm.pat.kind {
|
if let PatKind::Wild = arm.pat.kind {
|
||||||
|
|
|
@ -5,7 +5,7 @@ use clippy_utils::visitors::{for_each_expr, is_local_used};
|
||||||
use rustc_ast::{BorrowKind, LitKind};
|
use rustc_ast::{BorrowKind, LitKind};
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, Guard, MatchSource, Node, Pat, PatKind};
|
use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, Pat, PatKind};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_span::symbol::Ident;
|
use rustc_span::symbol::Ident;
|
||||||
use rustc_span::{Span, Symbol};
|
use rustc_span::{Span, Symbol};
|
||||||
|
@ -21,8 +21,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
};
|
};
|
||||||
|
|
||||||
// `Some(x) if matches!(x, y)`
|
// `Some(x) if matches!(x, y)`
|
||||||
if let Guard::If(if_expr) = guard
|
if let ExprKind::Match(
|
||||||
&& let ExprKind::Match(
|
|
||||||
scrutinee,
|
scrutinee,
|
||||||
[
|
[
|
||||||
arm,
|
arm,
|
||||||
|
@ -34,7 +33,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
MatchSource::Normal,
|
MatchSource::Normal,
|
||||||
) = if_expr.kind
|
) = guard.kind
|
||||||
&& let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm)
|
&& let Some(binding) = get_pat_binding(cx, scrutinee, outer_arm)
|
||||||
{
|
{
|
||||||
let pat_span = match (arm.pat.kind, binding.byref_ident) {
|
let pat_span = match (arm.pat.kind, binding.byref_ident) {
|
||||||
|
@ -45,14 +44,14 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(
|
||||||
cx,
|
cx,
|
||||||
outer_arm,
|
outer_arm,
|
||||||
if_expr.span,
|
guard.span,
|
||||||
snippet(cx, pat_span, "<binding>"),
|
snippet(cx, pat_span, "<binding>"),
|
||||||
&binding,
|
&binding,
|
||||||
arm.guard,
|
arm.guard,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
// `Some(x) if let Some(2) = x`
|
// `Some(x) if let Some(2) = x`
|
||||||
else if let Guard::IfLet(let_expr) = guard
|
else if let ExprKind::Let(let_expr) = guard.kind
|
||||||
&& let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm)
|
&& let Some(binding) = get_pat_binding(cx, let_expr.init, outer_arm)
|
||||||
{
|
{
|
||||||
let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
|
let pat_span = match (let_expr.pat.kind, binding.byref_ident) {
|
||||||
|
@ -71,8 +70,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
}
|
}
|
||||||
// `Some(x) if x == Some(2)`
|
// `Some(x) if x == Some(2)`
|
||||||
// `Some(x) if Some(2) == x`
|
// `Some(x) if Some(2) == x`
|
||||||
else if let Guard::If(if_expr) = guard
|
else if let ExprKind::Binary(bin_op, local, pat) = guard.kind
|
||||||
&& let ExprKind::Binary(bin_op, local, pat) = if_expr.kind
|
|
||||||
&& matches!(bin_op.node, BinOpKind::Eq)
|
&& matches!(bin_op.node, BinOpKind::Eq)
|
||||||
// Ensure they have the same type. If they don't, we'd need deref coercion which isn't
|
// Ensure they have the same type. If they don't, we'd need deref coercion which isn't
|
||||||
// possible (currently) in a pattern. In some cases, you can use something like
|
// possible (currently) in a pattern. In some cases, you can use something like
|
||||||
|
@ -96,16 +94,15 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>]) {
|
||||||
emit_redundant_guards(
|
emit_redundant_guards(
|
||||||
cx,
|
cx,
|
||||||
outer_arm,
|
outer_arm,
|
||||||
if_expr.span,
|
guard.span,
|
||||||
snippet(cx, pat_span, "<binding>"),
|
snippet(cx, pat_span, "<binding>"),
|
||||||
&binding,
|
&binding,
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
} else if let Guard::If(if_expr) = guard
|
} else if let ExprKind::MethodCall(path, recv, args, ..) = guard.kind
|
||||||
&& let ExprKind::MethodCall(path, recv, args, ..) = if_expr.kind
|
|
||||||
&& let Some(binding) = get_pat_binding(cx, recv, outer_arm)
|
&& let Some(binding) = get_pat_binding(cx, recv, outer_arm)
|
||||||
{
|
{
|
||||||
check_method_calls(cx, outer_arm, path.ident.name, recv, args, if_expr, &binding);
|
check_method_calls(cx, outer_arm, path.ident.name, recv, args, guard, &binding);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +213,7 @@ fn emit_redundant_guards<'tcx>(
|
||||||
guard_span: Span,
|
guard_span: Span,
|
||||||
binding_replacement: Cow<'static, str>,
|
binding_replacement: Cow<'static, str>,
|
||||||
pat_binding: &PatBindingInfo,
|
pat_binding: &PatBindingInfo,
|
||||||
inner_guard: Option<Guard<'_>>,
|
inner_guard: Option<&Expr<'_>>,
|
||||||
) {
|
) {
|
||||||
span_lint_and_then(
|
span_lint_and_then(
|
||||||
cx,
|
cx,
|
||||||
|
@ -242,12 +239,7 @@ fn emit_redundant_guards<'tcx>(
|
||||||
(
|
(
|
||||||
guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()),
|
guard_span.source_callsite().with_lo(outer_arm.pat.span.hi()),
|
||||||
inner_guard.map_or_else(String::new, |guard| {
|
inner_guard.map_or_else(String::new, |guard| {
|
||||||
let (prefix, span) = match guard {
|
format!(" if {}", snippet(cx, guard.span, "<guard>"))
|
||||||
Guard::If(e) => ("if", e.span),
|
|
||||||
Guard::IfLet(l) => ("if let", l.span),
|
|
||||||
};
|
|
||||||
|
|
||||||
format!(" {prefix} {}", snippet(cx, span, "<guard>"))
|
|
||||||
}),
|
}),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -9,7 +9,7 @@ use rustc_ast::ast::LitKind;
|
||||||
use rustc_errors::Applicability;
|
use rustc_errors::Applicability;
|
||||||
use rustc_hir::def::{DefKind, Res};
|
use rustc_hir::def::{DefKind, Res};
|
||||||
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
|
use rustc_hir::LangItem::{self, OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
|
||||||
use rustc_hir::{Arm, Expr, ExprKind, Guard, Node, Pat, PatKind, QPath, UnOp};
|
use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
|
||||||
use rustc_lint::LateContext;
|
use rustc_lint::LateContext;
|
||||||
use rustc_middle::ty::{self, GenericArgKind, Ty};
|
use rustc_middle::ty::{self, GenericArgKind, Ty};
|
||||||
use rustc_span::{sym, Span, Symbol};
|
use rustc_span::{sym, Span, Symbol};
|
||||||
|
@ -277,8 +277,6 @@ pub(super) fn check_match<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, op
|
||||||
let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_"));
|
let mut sugg = format!("{}.{good_method}", snippet(cx, result_expr.span, "_"));
|
||||||
|
|
||||||
if let Some(guard) = maybe_guard {
|
if let Some(guard) = maybe_guard {
|
||||||
let Guard::If(guard) = *guard else { return }; // `...is_none() && let ...` is a syntax error
|
|
||||||
|
|
||||||
// wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying!
|
// wow, the HIR for match guards in `PAT if let PAT = expr && expr => ...` is annoying!
|
||||||
// `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs,
|
// `guard` here is `Guard::If` with the let expression somewhere deep in the tree of exprs,
|
||||||
// counter to the intuition that it should be `Guard::IfLet`, so we need another check
|
// counter to the intuition that it should be `Guard::IfLet`, so we need another check
|
||||||
|
@ -319,7 +317,7 @@ fn found_good_method<'tcx>(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
arms: &'tcx [Arm<'tcx>],
|
arms: &'tcx [Arm<'tcx>],
|
||||||
node: (&PatKind<'_>, &PatKind<'_>),
|
node: (&PatKind<'_>, &PatKind<'_>),
|
||||||
) -> Option<(&'static str, Option<&'tcx Guard<'tcx>>)> {
|
) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> {
|
||||||
match node {
|
match node {
|
||||||
(
|
(
|
||||||
PatKind::TupleStruct(ref path_left, patterns_left, _),
|
PatKind::TupleStruct(ref path_left, patterns_left, _),
|
||||||
|
@ -409,7 +407,7 @@ fn get_good_method<'tcx>(
|
||||||
cx: &LateContext<'_>,
|
cx: &LateContext<'_>,
|
||||||
arms: &'tcx [Arm<'tcx>],
|
arms: &'tcx [Arm<'tcx>],
|
||||||
path_left: &QPath<'_>,
|
path_left: &QPath<'_>,
|
||||||
) -> Option<(&'static str, Option<&'tcx Guard<'tcx>>)> {
|
) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> {
|
||||||
if let Some(name) = get_ident(path_left) {
|
if let Some(name) = get_ident(path_left) {
|
||||||
let (expected_item_left, should_be_left, should_be_right) = match name.as_str() {
|
let (expected_item_left, should_be_left, should_be_right) = match name.as_str() {
|
||||||
"Ok" => (Item::Lang(ResultOk), "is_ok()", "is_err()"),
|
"Ok" => (Item::Lang(ResultOk), "is_ok()", "is_err()"),
|
||||||
|
@ -478,7 +476,7 @@ fn find_good_method_for_match<'a, 'tcx>(
|
||||||
expected_item_right: Item,
|
expected_item_right: Item,
|
||||||
should_be_left: &'a str,
|
should_be_left: &'a str,
|
||||||
should_be_right: &'a str,
|
should_be_right: &'a str,
|
||||||
) -> Option<(&'a str, Option<&'tcx Guard<'tcx>>)> {
|
) -> Option<(&'a str, Option<&'tcx Expr<'tcx>>)> {
|
||||||
let first_pat = arms[0].pat;
|
let first_pat = arms[0].pat;
|
||||||
let second_pat = arms[1].pat;
|
let second_pat = arms[1].pat;
|
||||||
|
|
||||||
|
@ -496,8 +494,8 @@ fn find_good_method_for_match<'a, 'tcx>(
|
||||||
|
|
||||||
match body_node_pair {
|
match body_node_pair {
|
||||||
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
|
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
|
||||||
(LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard.as_ref())),
|
(LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard)),
|
||||||
(LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard.as_ref())),
|
(LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
@ -511,7 +509,7 @@ fn find_good_method_for_matches_macro<'a, 'tcx>(
|
||||||
expected_item_left: Item,
|
expected_item_left: Item,
|
||||||
should_be_left: &'a str,
|
should_be_left: &'a str,
|
||||||
should_be_right: &'a str,
|
should_be_right: &'a str,
|
||||||
) -> Option<(&'a str, Option<&'tcx Guard<'tcx>>)> {
|
) -> Option<(&'a str, Option<&'tcx Expr<'tcx>>)> {
|
||||||
let first_pat = arms[0].pat;
|
let first_pat = arms[0].pat;
|
||||||
|
|
||||||
let body_node_pair = if is_pat_variant(cx, first_pat, path_left, expected_item_left) {
|
let body_node_pair = if is_pat_variant(cx, first_pat, path_left, expected_item_left) {
|
||||||
|
@ -522,8 +520,8 @@ fn find_good_method_for_matches_macro<'a, 'tcx>(
|
||||||
|
|
||||||
match body_node_pair {
|
match body_node_pair {
|
||||||
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
|
(ExprKind::Lit(lit_left), ExprKind::Lit(lit_right)) => match (&lit_left.node, &lit_right.node) {
|
||||||
(LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard.as_ref())),
|
(LitKind::Bool(true), LitKind::Bool(false)) => Some((should_be_left, arms[0].guard)),
|
||||||
(LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard.as_ref())),
|
(LitKind::Bool(false), LitKind::Bool(true)) => Some((should_be_right, arms[1].guard)),
|
||||||
_ => None,
|
_ => None,
|
||||||
},
|
},
|
||||||
_ => None,
|
_ => None,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
|
use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
|
||||||
use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
|
use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
|
||||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||||
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, Guard, HirId, Local, Node, Stmt, StmtKind};
|
use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, Local, Node, Stmt, StmtKind};
|
||||||
use rustc_lint::{LateContext, LateLintPass};
|
use rustc_lint::{LateContext, LateLintPass};
|
||||||
use rustc_middle::ty;
|
use rustc_middle::ty;
|
||||||
use rustc_session::declare_lint_pass;
|
use rustc_session::declare_lint_pass;
|
||||||
|
@ -119,7 +119,7 @@ impl<'a, 'tcx> DivergenceVisitor<'a, 'tcx> {
|
||||||
ExprKind::Match(e, arms, _) => {
|
ExprKind::Match(e, arms, _) => {
|
||||||
self.visit_expr(e);
|
self.visit_expr(e);
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
if let Some(Guard::If(if_expr)) = arm.guard {
|
if let Some(if_expr) = arm.guard {
|
||||||
self.visit_expr(if_expr);
|
self.visit_expr(if_expr);
|
||||||
}
|
}
|
||||||
// make sure top level arm expressions aren't linted
|
// make sure top level arm expressions aren't linted
|
||||||
|
|
|
@ -318,17 +318,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
||||||
self.pat(field!(arm.pat));
|
self.pat(field!(arm.pat));
|
||||||
match arm.value.guard {
|
match arm.value.guard {
|
||||||
None => chain!(self, "{arm}.guard.is_none()"),
|
None => chain!(self, "{arm}.guard.is_none()"),
|
||||||
Some(hir::Guard::If(expr)) => {
|
Some(expr) => {
|
||||||
bind!(self, expr);
|
bind!(self, expr);
|
||||||
chain!(self, "let Some(Guard::If({expr})) = {arm}.guard");
|
chain!(self, "let Some({expr}) = {arm}.guard");
|
||||||
self.expr(expr);
|
self.expr(expr);
|
||||||
},
|
},
|
||||||
Some(hir::Guard::IfLet(let_expr)) => {
|
|
||||||
bind!(self, let_expr);
|
|
||||||
chain!(self, "let Some(Guard::IfLet({let_expr}) = {arm}.guard");
|
|
||||||
self.pat(field!(let_expr.pat));
|
|
||||||
self.expr(field!(let_expr.init));
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
self.expr(field!(arm.body));
|
self.expr(field!(arm.body));
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ use rustc_hir::def::Res;
|
||||||
use rustc_hir::MatchSource::TryDesugar;
|
use rustc_hir::MatchSource::TryDesugar;
|
||||||
use rustc_hir::{
|
use rustc_hir::{
|
||||||
ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
|
ArrayLen, BinOpKind, BindingAnnotation, Block, BodyId, Closure, Expr, ExprField, ExprKind, FnRetTy, GenericArg,
|
||||||
GenericArgs, Guard, HirId, HirIdMap, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path,
|
GenericArgs, HirId, HirIdMap, InlineAsmOperand, Let, Lifetime, LifetimeName, Pat, PatField, PatKind, Path,
|
||||||
PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
|
PathSegment, PrimTy, QPath, Stmt, StmtKind, Ty, TyKind, TypeBinding,
|
||||||
};
|
};
|
||||||
use rustc_lexer::{tokenize, TokenKind};
|
use rustc_lexer::{tokenize, TokenKind};
|
||||||
|
@ -320,7 +320,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||||
&& self.eq_expr(le, re)
|
&& self.eq_expr(le, re)
|
||||||
&& over(la, ra, |l, r| {
|
&& over(la, ra, |l, r| {
|
||||||
self.eq_pat(l.pat, r.pat)
|
self.eq_pat(l.pat, r.pat)
|
||||||
&& both(&l.guard, &r.guard, |l, r| self.eq_guard(l, r))
|
&& both(&l.guard, &r.guard, |l, r| self.eq_expr(l, r))
|
||||||
&& self.eq_expr(l.body, r.body)
|
&& self.eq_expr(l.body, r.body)
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
@ -410,16 +410,6 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||||
left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
|
left.ident.name == right.ident.name && self.eq_expr(left.expr, right.expr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eq_guard(&mut self, left: &Guard<'_>, right: &Guard<'_>) -> bool {
|
|
||||||
match (left, right) {
|
|
||||||
(Guard::If(l), Guard::If(r)) => self.eq_expr(l, r),
|
|
||||||
(Guard::IfLet(l), Guard::IfLet(r)) => {
|
|
||||||
self.eq_pat(l.pat, r.pat) && both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r)) && self.eq_expr(l.init, r.init)
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
|
fn eq_generic_arg(&mut self, left: &GenericArg<'_>, right: &GenericArg<'_>) -> bool {
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
|
(GenericArg::Const(l), GenericArg::Const(r)) => self.eq_body(l.value.body, r.value.body),
|
||||||
|
@ -876,7 +866,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
||||||
for arm in arms {
|
for arm in arms {
|
||||||
self.hash_pat(arm.pat);
|
self.hash_pat(arm.pat);
|
||||||
if let Some(ref e) = arm.guard {
|
if let Some(ref e) = arm.guard {
|
||||||
self.hash_guard(e);
|
self.hash_expr(e);
|
||||||
}
|
}
|
||||||
self.hash_expr(arm.body);
|
self.hash_expr(arm.body);
|
||||||
}
|
}
|
||||||
|
@ -1056,14 +1046,6 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hash_guard(&mut self, g: &Guard<'_>) {
|
|
||||||
match g {
|
|
||||||
Guard::If(expr) | Guard::IfLet(Let { init: expr, .. }) => {
|
|
||||||
self.hash_expr(expr);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
|
pub fn hash_lifetime(&mut self, lifetime: &Lifetime) {
|
||||||
lifetime.ident.name.hash(&mut self.s);
|
lifetime.ident.name.hash(&mut self.s);
|
||||||
std::mem::discriminant(&lifetime.res).hash(&mut self.s);
|
std::mem::discriminant(&lifetime.res).hash(&mut self.s);
|
||||||
|
|
|
@ -3164,7 +3164,7 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<
|
||||||
self.is_never = false;
|
self.is_never = false;
|
||||||
if let Some(guard) = arm.guard {
|
if let Some(guard) = arm.guard {
|
||||||
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
||||||
self.visit_expr(guard.body());
|
self.visit_expr(guard);
|
||||||
self.in_final_expr = in_final_expr;
|
self.in_final_expr = in_final_expr;
|
||||||
// The compiler doesn't consider diverging guards as causing the arm to diverge.
|
// The compiler doesn't consider diverging guards as causing the arm to diverge.
|
||||||
self.is_never = false;
|
self.is_never = false;
|
||||||
|
@ -3223,7 +3223,7 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<
|
||||||
fn visit_arm(&mut self, arm: &Arm<'tcx>) {
|
fn visit_arm(&mut self, arm: &Arm<'tcx>) {
|
||||||
if let Some(guard) = arm.guard {
|
if let Some(guard) = arm.guard {
|
||||||
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
let in_final_expr = mem::replace(&mut self.in_final_expr, false);
|
||||||
self.visit_expr(guard.body());
|
self.visit_expr(guard);
|
||||||
self.in_final_expr = in_final_expr;
|
self.in_final_expr = in_final_expr;
|
||||||
}
|
}
|
||||||
self.visit_expr(arm.body);
|
self.visit_expr(arm.body);
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
warning: irrefutable `if let` guard pattern
|
warning: irrefutable `if let` guard pattern
|
||||||
--> $DIR/issue-88118-2.rs:10:29
|
--> $DIR/issue-88118-2.rs:10:25
|
||||||
|
|
|
|
||||||
LL | Registry if let _ = registry.try_find_description() => { }
|
LL | Registry if let _ = registry.try_find_description() => { }
|
||||||
| ^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: this pattern will always match, so the guard is useless
|
= note: this pattern will always match, so the guard is useless
|
||||||
= help: consider removing the guard and adding a `let` inside the match arm
|
= help: consider removing the guard and adding a `let` inside the match arm
|
||||||
|
|
|
@ -11,10 +11,10 @@ LL | #[deny(bindings_with_variant_name)]
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
error: irrefutable `if let` guard pattern
|
error: irrefutable `if let` guard pattern
|
||||||
--> $DIR/lint-match-arms-2.rs:18:18
|
--> $DIR/lint-match-arms-2.rs:18:14
|
||||||
|
|
|
|
||||||
LL | a if let b = a => {}
|
LL | a if let b = a => {}
|
||||||
| ^
|
| ^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: this pattern will always match, so the guard is useless
|
= note: this pattern will always match, so the guard is useless
|
||||||
= help: consider removing the guard and adding a `let` inside the match arm
|
= help: consider removing the guard and adding a `let` inside the match arm
|
||||||
|
|
|
@ -22,10 +22,10 @@ LL | while let _ = 5 {
|
||||||
= help: consider instead using a `loop { ... }` with a `let` inside it
|
= help: consider instead using a `loop { ... }` with a `let` inside it
|
||||||
|
|
||||||
error: irrefutable `if let` guard pattern
|
error: irrefutable `if let` guard pattern
|
||||||
--> $DIR/deny-irrefutable-let-patterns.rs:13:18
|
--> $DIR/deny-irrefutable-let-patterns.rs:13:14
|
||||||
|
|
|
|
||||||
LL | _ if let _ = 2 => {}
|
LL | _ if let _ = 2 => {}
|
||||||
| ^
|
| ^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: this pattern will always match, so the guard is useless
|
= note: this pattern will always match, so the guard is useless
|
||||||
= help: consider removing the guard and adding a `let` inside the match arm
|
= help: consider removing the guard and adding a `let` inside the match arm
|
||||||
|
|
72
tests/ui/rfcs/rfc-2294-if-let-guard/drop-scope.rs
Normal file
72
tests/ui/rfcs/rfc-2294-if-let-guard/drop-scope.rs
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
// Ensure that temporaries in if-let guards live for the arm
|
||||||
|
// regression test for #118593
|
||||||
|
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
#![feature(if_let_guard)]
|
||||||
|
#![feature(let_chains)]
|
||||||
|
|
||||||
|
fn get_temp() -> Option<String> {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn let_guard(num: u8) {
|
||||||
|
match num {
|
||||||
|
1 | 2 if let Some(ref a) = get_temp() => {
|
||||||
|
let _b = a;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match num {
|
||||||
|
3 | 4 if let Some(ref mut c) = get_temp() => {
|
||||||
|
let _d = c;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn let_let_chain_guard(num: u8) {
|
||||||
|
match num {
|
||||||
|
5 | 6
|
||||||
|
if let Some(ref a) = get_temp()
|
||||||
|
&& let Some(ref b) = get_temp() =>
|
||||||
|
{
|
||||||
|
let _x = a;
|
||||||
|
let _y = b;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match num {
|
||||||
|
7 | 8
|
||||||
|
if let Some(ref mut c) = get_temp()
|
||||||
|
&& let Some(ref mut d) = get_temp() =>
|
||||||
|
{
|
||||||
|
let _w = c;
|
||||||
|
let _z = d;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn let_cond_chain_guard(num: u8) {
|
||||||
|
match num {
|
||||||
|
9 | 10
|
||||||
|
if let Some(ref a) = get_temp()
|
||||||
|
&& true =>
|
||||||
|
{
|
||||||
|
let _x = a;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match num {
|
||||||
|
11 | 12
|
||||||
|
if let Some(ref mut b) = get_temp()
|
||||||
|
&& true =>
|
||||||
|
{
|
||||||
|
let _w = b;
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
30
tests/ui/rfcs/rfc-2294-if-let-guard/scope.rs
Normal file
30
tests/ui/rfcs/rfc-2294-if-let-guard/scope.rs
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
// Tests for #88015 when using if let chains in match guards
|
||||||
|
|
||||||
|
//run-pass
|
||||||
|
|
||||||
|
#![feature(if_let_guard)]
|
||||||
|
#![feature(let_chains)]
|
||||||
|
#![allow(irrefutable_let_patterns)]
|
||||||
|
|
||||||
|
fn lhs_let(opt: Option<bool>) {
|
||||||
|
match opt {
|
||||||
|
None | Some(false) | Some(true) if let x = 42 && true => assert_eq!(x, 42),
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rhs_let(opt: Option<bool>) {
|
||||||
|
match opt {
|
||||||
|
None | Some(false) | Some(true) if true && let x = 41 => assert_eq!(x, 41),
|
||||||
|
_ => panic!()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
lhs_let(None);
|
||||||
|
lhs_let(Some(false));
|
||||||
|
lhs_let(Some(true));
|
||||||
|
rhs_let(None);
|
||||||
|
rhs_let(Some(false));
|
||||||
|
rhs_let(Some(true));
|
||||||
|
}
|
|
@ -1,8 +1,8 @@
|
||||||
error: irrefutable `if let` guard pattern
|
error: irrefutable `if let` guard pattern
|
||||||
--> $DIR/warns.rs:6:24
|
--> $DIR/warns.rs:6:20
|
||||||
|
|
|
|
||||||
LL | Some(x) if let () = x => {}
|
LL | Some(x) if let () = x => {}
|
||||||
| ^^
|
| ^^^^^^^^^^
|
||||||
|
|
|
|
||||||
= note: this pattern will always match, so the guard is useless
|
= note: this pattern will always match, so the guard is useless
|
||||||
= help: consider removing the guard and adding a `let` inside the match arm
|
= help: consider removing the guard and adding a `let` inside the match arm
|
||||||
|
|
|
@ -128,8 +128,8 @@ hir-stats Param 64 ( 0.7%) 2 32
|
||||||
hir-stats Body 72 ( 0.8%) 3 24
|
hir-stats Body 72 ( 0.8%) 3 24
|
||||||
hir-stats InlineAsm 72 ( 0.8%) 1 72
|
hir-stats InlineAsm 72 ( 0.8%) 1 72
|
||||||
hir-stats ImplItemRef 72 ( 0.8%) 2 36
|
hir-stats ImplItemRef 72 ( 0.8%) 2 36
|
||||||
|
hir-stats Arm 80 ( 0.9%) 2 40
|
||||||
hir-stats FieldDef 96 ( 1.1%) 2 48
|
hir-stats FieldDef 96 ( 1.1%) 2 48
|
||||||
hir-stats Arm 96 ( 1.1%) 2 48
|
|
||||||
hir-stats Stmt 96 ( 1.1%) 3 32
|
hir-stats Stmt 96 ( 1.1%) 3 32
|
||||||
hir-stats - Local 32 ( 0.4%) 1
|
hir-stats - Local 32 ( 0.4%) 1
|
||||||
hir-stats - Semi 32 ( 0.4%) 1
|
hir-stats - Semi 32 ( 0.4%) 1
|
||||||
|
@ -151,11 +151,11 @@ hir-stats - Wild 72 ( 0.8%) 1
|
||||||
hir-stats - Struct 72 ( 0.8%) 1
|
hir-stats - Struct 72 ( 0.8%) 1
|
||||||
hir-stats - Binding 216 ( 2.4%) 3
|
hir-stats - Binding 216 ( 2.4%) 3
|
||||||
hir-stats GenericParam 400 ( 4.4%) 5 80
|
hir-stats GenericParam 400 ( 4.4%) 5 80
|
||||||
hir-stats Generics 560 ( 6.1%) 10 56
|
hir-stats Generics 560 ( 6.2%) 10 56
|
||||||
hir-stats Ty 720 ( 7.9%) 15 48
|
hir-stats Ty 720 ( 7.9%) 15 48
|
||||||
hir-stats - Ptr 48 ( 0.5%) 1
|
hir-stats - Ptr 48 ( 0.5%) 1
|
||||||
hir-stats - Ref 48 ( 0.5%) 1
|
hir-stats - Ref 48 ( 0.5%) 1
|
||||||
hir-stats - Path 624 ( 6.8%) 13
|
hir-stats - Path 624 ( 6.9%) 13
|
||||||
hir-stats Expr 768 ( 8.4%) 12 64
|
hir-stats Expr 768 ( 8.4%) 12 64
|
||||||
hir-stats - Path 64 ( 0.7%) 1
|
hir-stats - Path 64 ( 0.7%) 1
|
||||||
hir-stats - Struct 64 ( 0.7%) 1
|
hir-stats - Struct 64 ( 0.7%) 1
|
||||||
|
@ -174,5 +174,5 @@ hir-stats - Use 352 ( 3.9%) 4
|
||||||
hir-stats Path 1_240 (13.6%) 31 40
|
hir-stats Path 1_240 (13.6%) 31 40
|
||||||
hir-stats PathSegment 1_920 (21.1%) 40 48
|
hir-stats PathSegment 1_920 (21.1%) 40 48
|
||||||
hir-stats ----------------------------------------------------------------
|
hir-stats ----------------------------------------------------------------
|
||||||
hir-stats Total 9_112
|
hir-stats Total 9_096
|
||||||
hir-stats
|
hir-stats
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue