mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-13 12:36:47 +00:00
lower let-else in MIR instead
This commit is contained in:
parent
38b72154de
commit
6c529ded86
@ -1,8 +1,8 @@
|
||||
use crate::{ImplTraitContext, ImplTraitPosition, LoweringContext};
|
||||
use rustc_ast::{AttrVec, Block, BlockCheckMode, Expr, Local, LocalKind, Stmt, StmtKind};
|
||||
use rustc_ast::{Block, BlockCheckMode, Local, LocalKind, Stmt, StmtKind};
|
||||
use rustc_hir as hir;
|
||||
use rustc_session::parse::feature_err;
|
||||
use rustc_span::{sym, DesugaringKind};
|
||||
use rustc_span::sym;
|
||||
|
||||
use smallvec::SmallVec;
|
||||
|
||||
@ -36,21 +36,25 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
match s.kind {
|
||||
StmtKind::Local(ref local) => {
|
||||
let hir_id = self.lower_node_id(s.id);
|
||||
match &local.kind {
|
||||
LocalKind::InitElse(init, els) => {
|
||||
let e = self.lower_let_else(hir_id, local, init, els, tail);
|
||||
expr = Some(e);
|
||||
// remaining statements are in let-else expression
|
||||
break;
|
||||
let els = if let LocalKind::InitElse(_, els) = &local.kind {
|
||||
if !self.tcx.features().let_else {
|
||||
feature_err(
|
||||
&self.tcx.sess.parse_sess,
|
||||
sym::let_else,
|
||||
s.span,
|
||||
"`let...else` statements are unstable",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
_ => {
|
||||
let local = self.lower_local(local);
|
||||
self.alias_attrs(hir_id, local.hir_id);
|
||||
let kind = hir::StmtKind::Local(local);
|
||||
let span = self.lower_span(s.span);
|
||||
stmts.push(hir::Stmt { hir_id, kind, span });
|
||||
}
|
||||
}
|
||||
Some(self.lower_block(els, false))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let local = self.lower_local(local);
|
||||
self.alias_attrs(hir_id, local.hir_id);
|
||||
let kind = hir::StmtKind::Local(local, els);
|
||||
let span = self.lower_span(s.span);
|
||||
stmts.push(hir::Stmt { hir_id, kind, span });
|
||||
}
|
||||
StmtKind::Item(ref it) => {
|
||||
stmts.extend(self.lower_item_ref(it).into_iter().enumerate().map(
|
||||
@ -115,59 +119,4 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_let_else(
|
||||
&mut self,
|
||||
stmt_hir_id: hir::HirId,
|
||||
local: &Local,
|
||||
init: &Expr,
|
||||
els: &Block,
|
||||
tail: &[Stmt],
|
||||
) -> &'hir hir::Expr<'hir> {
|
||||
let ty = local
|
||||
.ty
|
||||
.as_ref()
|
||||
.map(|t| self.lower_ty(t, ImplTraitContext::Disallowed(ImplTraitPosition::Variable)));
|
||||
let span = self.lower_span(local.span);
|
||||
let span = self.mark_span_with_reason(DesugaringKind::LetElse, span, None);
|
||||
let init = self.lower_expr(init);
|
||||
let local_hir_id = self.lower_node_id(local.id);
|
||||
self.lower_attrs(local_hir_id, &local.attrs);
|
||||
let let_expr = {
|
||||
let lex = self.arena.alloc(hir::Let {
|
||||
hir_id: local_hir_id,
|
||||
pat: self.lower_pat(&local.pat),
|
||||
ty,
|
||||
init,
|
||||
span,
|
||||
});
|
||||
self.arena.alloc(self.expr(span, hir::ExprKind::Let(lex), AttrVec::new()))
|
||||
};
|
||||
let then_expr = {
|
||||
let (stmts, expr) = self.lower_stmts(tail);
|
||||
let block = self.block_all(span, stmts, expr);
|
||||
self.arena.alloc(self.expr_block(block, AttrVec::new()))
|
||||
};
|
||||
let else_expr = {
|
||||
let block = self.lower_block(els, false);
|
||||
self.arena.alloc(self.expr_block(block, AttrVec::new()))
|
||||
};
|
||||
self.alias_attrs(let_expr.hir_id, local_hir_id);
|
||||
self.alias_attrs(else_expr.hir_id, local_hir_id);
|
||||
let if_expr = self.arena.alloc(hir::Expr {
|
||||
hir_id: stmt_hir_id,
|
||||
span,
|
||||
kind: hir::ExprKind::If(let_expr, then_expr, Some(else_expr)),
|
||||
});
|
||||
if !self.tcx.features().let_else {
|
||||
feature_err(
|
||||
&self.tcx.sess.parse_sess,
|
||||
sym::let_else,
|
||||
local.span,
|
||||
"`let...else` statements are unstable",
|
||||
)
|
||||
.emit();
|
||||
}
|
||||
if_expr
|
||||
}
|
||||
}
|
||||
|
@ -284,10 +284,10 @@ impl<'a, 'hir> Visitor<'hir> for NodeCollector<'a, 'hir> {
|
||||
});
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'hir Local<'hir>) {
|
||||
fn visit_local(&mut self, l: &'hir Local<'hir>, e: Option<&'hir Block<'hir>>) {
|
||||
self.insert(l.span, l.hir_id, Node::Local(l));
|
||||
self.with_parent(l.hir_id, |this| {
|
||||
intravisit::walk_local(this, l);
|
||||
intravisit::walk_local(this, l, e);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -2147,7 +2147,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.attrs.insert(hir_id.local_id, a);
|
||||
}
|
||||
let local = hir::Local { hir_id, init, pat, source, span: self.lower_span(span), ty: None };
|
||||
self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local)))
|
||||
self.stmt(span, hir::StmtKind::Local(self.arena.alloc(local), None))
|
||||
}
|
||||
|
||||
fn block_expr(&mut self, expr: &'hir hir::Expr<'hir>) -> &'hir hir::Block<'hir> {
|
||||
|
@ -1296,7 +1296,8 @@ pub struct Stmt<'hir> {
|
||||
#[derive(Debug, HashStable_Generic)]
|
||||
pub enum StmtKind<'hir> {
|
||||
/// A local (`let`) binding.
|
||||
Local(&'hir Local<'hir>),
|
||||
/// FIXME: bundle the last two components into another `struct`
|
||||
Local(&'hir Local<'hir>, Option<&'hir Block<'hir>>),
|
||||
|
||||
/// An item binding.
|
||||
Item(ItemId),
|
||||
|
@ -310,8 +310,8 @@ pub trait Visitor<'v>: Sized {
|
||||
fn visit_foreign_item(&mut self, i: &'v ForeignItem<'v>) {
|
||||
walk_foreign_item(self, i)
|
||||
}
|
||||
fn visit_local(&mut self, l: &'v Local<'v>) {
|
||||
walk_local(self, l)
|
||||
fn visit_local(&mut self, l: &'v Local<'v>, els: Option<&'v Block<'v>>) {
|
||||
walk_local(self, l, els)
|
||||
}
|
||||
fn visit_block(&mut self, b: &'v Block<'v>) {
|
||||
walk_block(self, b)
|
||||
@ -466,12 +466,19 @@ pub fn walk_body<'v, V: Visitor<'v>>(visitor: &mut V, body: &'v Body<'v>) {
|
||||
visitor.visit_expr(&body.value);
|
||||
}
|
||||
|
||||
pub fn walk_local<'v, V: Visitor<'v>>(visitor: &mut V, local: &'v Local<'v>) {
|
||||
pub fn walk_local<'v, V: Visitor<'v>>(
|
||||
visitor: &mut V,
|
||||
local: &'v Local<'v>,
|
||||
els: Option<&'v Block<'v>>,
|
||||
) {
|
||||
// Intentionally visiting the expr first - the initialization expr
|
||||
// dominates the local's definition.
|
||||
walk_list!(visitor, visit_expr, &local.init);
|
||||
visitor.visit_id(local.hir_id);
|
||||
visitor.visit_pat(&local.pat);
|
||||
if let Some(els) = els {
|
||||
visitor.visit_block(els);
|
||||
}
|
||||
walk_list!(visitor, visit_ty, &local.ty);
|
||||
}
|
||||
|
||||
@ -1055,9 +1062,9 @@ pub fn walk_block<'v, V: Visitor<'v>>(visitor: &mut V, block: &'v Block<'v>) {
|
||||
|
||||
pub fn walk_stmt<'v, V: Visitor<'v>>(visitor: &mut V, statement: &'v Stmt<'v>) {
|
||||
visitor.visit_id(statement.hir_id);
|
||||
match statement.kind {
|
||||
StmtKind::Local(ref local) => visitor.visit_local(local),
|
||||
StmtKind::Item(item) => visitor.visit_nested_item(item),
|
||||
match &statement.kind {
|
||||
StmtKind::Local(ref local, els) => visitor.visit_local(local, *els),
|
||||
StmtKind::Item(item) => visitor.visit_nested_item(*item),
|
||||
StmtKind::Expr(ref expression) | StmtKind::Semi(ref expression) => {
|
||||
visitor.visit_expr(expression)
|
||||
}
|
||||
|
@ -883,7 +883,12 @@ impl<'a> State<'a> {
|
||||
self.ann.post(self, AnnNode::SubItem(ii.hir_id()))
|
||||
}
|
||||
|
||||
pub fn print_local(&mut self, init: Option<&hir::Expr<'_>>, decl: impl Fn(&mut Self)) {
|
||||
pub fn print_local(
|
||||
&mut self,
|
||||
init: Option<&hir::Expr<'_>>,
|
||||
els: Option<&hir::Block<'_>>,
|
||||
decl: impl Fn(&mut Self),
|
||||
) {
|
||||
self.space_if_not_bol();
|
||||
self.ibox(INDENT_UNIT);
|
||||
self.word_nbsp("let");
|
||||
@ -897,14 +902,21 @@ impl<'a> State<'a> {
|
||||
self.word_space("=");
|
||||
self.print_expr(init);
|
||||
}
|
||||
|
||||
if let Some(els) = els {
|
||||
self.nbsp();
|
||||
self.word_space("else");
|
||||
self.print_block(els);
|
||||
}
|
||||
|
||||
self.end()
|
||||
}
|
||||
|
||||
pub fn print_stmt(&mut self, st: &hir::Stmt<'_>) {
|
||||
self.maybe_print_comment(st.span.lo());
|
||||
match st.kind {
|
||||
hir::StmtKind::Local(loc) => {
|
||||
self.print_local(loc.init, |this| this.print_local_decl(loc));
|
||||
hir::StmtKind::Local(loc, els) => {
|
||||
self.print_local(loc.init, els, |this| this.print_local_decl(loc));
|
||||
}
|
||||
hir::StmtKind::Item(item) => self.ann.nested(self, Nested::Item(item)),
|
||||
hir::StmtKind::Expr(expr) => {
|
||||
@ -1404,7 +1416,7 @@ impl<'a> State<'a> {
|
||||
|
||||
// Print `let _t = $init;`:
|
||||
let temp = Ident::from_str("_t");
|
||||
self.print_local(Some(init), |this| this.print_ident(temp));
|
||||
self.print_local(Some(init), None, |this| this.print_ident(temp));
|
||||
self.word(";");
|
||||
|
||||
// Print `_t`:
|
||||
@ -2293,7 +2305,7 @@ fn expr_requires_semi_to_be_stmt(e: &hir::Expr<'_>) -> bool {
|
||||
/// seen the semicolon, and thus don't need another.
|
||||
fn stmt_ends_with_semi(stmt: &hir::StmtKind<'_>) -> bool {
|
||||
match *stmt {
|
||||
hir::StmtKind::Local(_) => true,
|
||||
hir::StmtKind::Local(_, _) => true,
|
||||
hir::StmtKind::Item(_) => false,
|
||||
hir::StmtKind::Expr(e) => expr_requires_semi_to_be_stmt(e),
|
||||
hir::StmtKind::Semi(..) => false,
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::infer::type_variable::TypeVariableOriginKind;
|
||||
use crate::infer::InferCtxt;
|
||||
use hir::{Block, LocalSource};
|
||||
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def::{CtorOf, DefKind, Namespace};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::intravisit::{self, Visitor};
|
||||
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local, LocalSource};
|
||||
use rustc_hir::{Body, Expr, ExprKind, FnRetTy, HirId, Local};
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::infer::unify_key::ConstVariableOriginKind;
|
||||
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
|
||||
@ -952,8 +953,8 @@ impl<'a, 'tcx> Visitor<'tcx> for FindInferSourceVisitor<'a, 'tcx> {
|
||||
self.infcx.tcx.hir()
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, local: &'tcx Local<'tcx>) {
|
||||
intravisit::walk_local(self, local);
|
||||
fn visit_local(&mut self, local: &'tcx Local<'tcx>, els: Option<&'tcx Block<'tcx>>) {
|
||||
intravisit::walk_local(self, local, els);
|
||||
|
||||
if let Some(ty) = self.opt_node_type(local.hir_id) {
|
||||
if self.generic_arg_contains_target(ty.into()) {
|
||||
|
@ -251,10 +251,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) {
|
||||
self.with_lint_attrs(l.hir_id, |cx| {
|
||||
lint_callback!(cx, check_local, l);
|
||||
hir_visit::walk_local(cx, l);
|
||||
lint_callback!(cx, check_local, l, e);
|
||||
hir_visit::walk_local(cx, l, e);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -783,9 +783,9 @@ impl<'tcx> intravisit::Visitor<'tcx> for LintLevelMapBuilder<'tcx> {
|
||||
})
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) {
|
||||
self.with_lint_attrs(l.hir_id, |builder| {
|
||||
intravisit::walk_local(builder, l);
|
||||
intravisit::walk_local(builder, l, e);
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ macro_rules! late_lint_methods {
|
||||
fn check_foreign_item_post(a: &$hir hir::ForeignItem<$hir>);
|
||||
fn check_item(a: &$hir hir::Item<$hir>);
|
||||
fn check_item_post(a: &$hir hir::Item<$hir>);
|
||||
fn check_local(a: &$hir hir::Local<$hir>);
|
||||
fn check_local(a: &$hir hir::Local<$hir>, b: Option<&$hir hir::Block<$hir>>);
|
||||
fn check_block(a: &$hir hir::Block<$hir>);
|
||||
fn check_block_post(a: &$hir hir::Block<$hir>);
|
||||
fn check_stmt(a: &$hir hir::Stmt<$hir>);
|
||||
|
@ -789,7 +789,7 @@ impl<'hir> Map<'hir> {
|
||||
| Node::ForeignItem(_)
|
||||
| Node::TraitItem(_)
|
||||
| Node::ImplItem(_)
|
||||
| Node::Stmt(Stmt { kind: StmtKind::Local(_), .. }) => break,
|
||||
| Node::Stmt(Stmt { kind: StmtKind::Local(_, _), .. }) => break,
|
||||
Node::Expr(expr @ Expr { kind: ExprKind::If(..) | ExprKind::Match(..), .. }) => {
|
||||
return Some(expr);
|
||||
}
|
||||
|
@ -182,6 +182,9 @@ pub enum StmtKind<'tcx> {
|
||||
/// `let pat: ty = <INIT>`
|
||||
initializer: Option<ExprId>,
|
||||
|
||||
/// `let pat: ty = <INIT> else { <ELSE> }
|
||||
else_block: Option<Block>,
|
||||
|
||||
/// The lint level for this `let` statement.
|
||||
lint_level: LintLevel,
|
||||
},
|
||||
|
@ -167,11 +167,15 @@ pub fn walk_stmt<'a, 'tcx: 'a, V: Visitor<'a, 'tcx>>(visitor: &mut V, stmt: &Stm
|
||||
init_scope: _,
|
||||
ref pattern,
|
||||
lint_level: _,
|
||||
else_block,
|
||||
} => {
|
||||
if let Some(init) = initializer {
|
||||
visitor.visit_expr(&visitor.thir()[*init]);
|
||||
}
|
||||
visitor.visit_pat(pattern);
|
||||
if let Some(block) = else_block {
|
||||
visitor.visit_block(block)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -99,6 +99,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
ref pattern,
|
||||
initializer,
|
||||
lint_level,
|
||||
else_block,
|
||||
} => {
|
||||
let ignores_expr_result = matches!(*pattern.kind, PatKind::Wild);
|
||||
this.block_context.push(BlockFrame::Statement { ignores_expr_result });
|
||||
@ -124,18 +125,30 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|this| {
|
||||
let scope = (*init_scope, source_info);
|
||||
this.in_scope(scope, *lint_level, |this| {
|
||||
this.declare_bindings(
|
||||
visibility_scope,
|
||||
remainder_span,
|
||||
pattern,
|
||||
ArmHasGuard(false),
|
||||
Some((None, initializer_span)),
|
||||
);
|
||||
this.expr_into_pattern(block, pattern.clone(), init)
|
||||
if let Some(else_block) = else_block {
|
||||
this.ast_let_else(
|
||||
block,
|
||||
init,
|
||||
initializer_span,
|
||||
else_block,
|
||||
visibility_scope,
|
||||
remainder_span,
|
||||
pattern,
|
||||
)
|
||||
} else {
|
||||
this.declare_bindings(
|
||||
visibility_scope,
|
||||
remainder_span,
|
||||
pattern,
|
||||
ArmHasGuard(false),
|
||||
Some((None, initializer_span)),
|
||||
);
|
||||
this.expr_into_pattern(block, pattern.clone(), init) // irrefutable pattern
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
)
|
||||
);
|
||||
)
|
||||
} else {
|
||||
let scope = (*init_scope, source_info);
|
||||
unpack!(this.in_scope(scope, *lint_level, |this| {
|
||||
|
@ -1615,7 +1615,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
// those N possible outcomes, create a (initially empty)
|
||||
// vector of candidates. Those are the candidates that still
|
||||
// apply if the test has that particular outcome.
|
||||
debug!("match_candidates: test={:?} match_pair={:?}", test, match_pair);
|
||||
debug!("test_candidates: test={:?} match_pair={:?}", test, match_pair);
|
||||
let mut target_candidates: Vec<Vec<&mut Candidate<'pat, 'tcx>>> = vec![];
|
||||
target_candidates.resize_with(test.targets(), Default::default);
|
||||
|
||||
@ -1635,8 +1635,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
// at least the first candidate ought to be tested
|
||||
assert!(total_candidate_count > candidates.len());
|
||||
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
|
||||
debug!("untested_candidates: {}", candidates.len());
|
||||
debug!("test_candidates: tested_candidates: {}", total_candidate_count - candidates.len());
|
||||
debug!("test_candidates: untested_candidates: {}", candidates.len());
|
||||
|
||||
// HACK(matthewjasper) This is a closure so that we can let the test
|
||||
// create its blocks before the rest of the match. This currently
|
||||
@ -2274,4 +2274,75 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
debug!("declare_binding: vars={:?}", locals);
|
||||
self.var_indices.insert(var_id, locals);
|
||||
}
|
||||
|
||||
pub(crate) fn ast_let_else(
|
||||
&mut self,
|
||||
mut block: BasicBlock,
|
||||
init: &Expr<'tcx>,
|
||||
initializer_span: Span,
|
||||
else_block: &Block,
|
||||
visibility_scope: Option<SourceScope>,
|
||||
remainder_span: Span,
|
||||
pattern: &Pat<'tcx>,
|
||||
) -> BlockAnd<()> {
|
||||
let scrutinee = unpack!(block = self.lower_scrutinee(block, init, initializer_span));
|
||||
let pat = Pat { ty: init.ty, span: else_block.span, kind: Box::new(PatKind::Wild) };
|
||||
let mut wildcard = Candidate::new(scrutinee.clone(), &pat, false);
|
||||
self.declare_bindings(
|
||||
visibility_scope,
|
||||
remainder_span,
|
||||
pattern,
|
||||
ArmHasGuard(false),
|
||||
Some((None, initializer_span)),
|
||||
);
|
||||
let mut candidate = Candidate::new(scrutinee.clone(), pattern, false);
|
||||
let fake_borrow_temps = self.lower_match_tree(
|
||||
block,
|
||||
initializer_span,
|
||||
pattern.span,
|
||||
false,
|
||||
&mut [&mut candidate, &mut wildcard],
|
||||
);
|
||||
// This block is for the matching case
|
||||
let matching = self.bind_pattern(
|
||||
self.source_info(pattern.span),
|
||||
candidate,
|
||||
None,
|
||||
&fake_borrow_temps,
|
||||
initializer_span,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
// This block is for the failure case
|
||||
let failure = self.bind_pattern(
|
||||
self.source_info(else_block.span),
|
||||
wildcard,
|
||||
None,
|
||||
&fake_borrow_temps,
|
||||
initializer_span,
|
||||
None,
|
||||
None,
|
||||
None,
|
||||
);
|
||||
// This place is not really used because this destination place
|
||||
// should never be used to take values at the end of the failure
|
||||
// block.
|
||||
let dummy_place = Place { local: RETURN_PLACE, projection: ty::List::empty() };
|
||||
let failure_block;
|
||||
unpack!(
|
||||
failure_block = self.ast_block(
|
||||
dummy_place,
|
||||
failure,
|
||||
else_block,
|
||||
self.source_info(else_block.span),
|
||||
)
|
||||
);
|
||||
self.cfg.terminate(
|
||||
failure_block,
|
||||
self.source_info(else_block.span),
|
||||
TerminatorKind::Unreachable,
|
||||
);
|
||||
matching.unit()
|
||||
}
|
||||
}
|
||||
|
@ -48,7 +48,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
.filter_map(|(index, stmt)| {
|
||||
let hir_id = stmt.hir_id;
|
||||
let opt_dxn_ext = self.region_scope_tree.opt_destruction_scope(hir_id.local_id);
|
||||
match stmt.kind {
|
||||
match &stmt.kind {
|
||||
hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
|
||||
let stmt = Stmt {
|
||||
kind: StmtKind::Expr {
|
||||
@ -66,7 +66,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
// ignore for purposes of the MIR
|
||||
None
|
||||
}
|
||||
hir::StmtKind::Local(ref local) => {
|
||||
hir::StmtKind::Local(local, els) => {
|
||||
let remainder_scope = region::Scope {
|
||||
id: block_id,
|
||||
data: region::ScopeData::Remainder(region::FirstStatementIndex::new(
|
||||
@ -74,6 +74,8 @@ impl<'tcx> Cx<'tcx> {
|
||||
)),
|
||||
};
|
||||
|
||||
let else_block = els.map(|els| self.mirror_block(els));
|
||||
|
||||
let mut pattern = self.pattern_from_hir(local.pat);
|
||||
debug!(?pattern);
|
||||
|
||||
@ -110,6 +112,7 @@ impl<'tcx> Cx<'tcx> {
|
||||
},
|
||||
pattern,
|
||||
initializer: local.init.map(|init| self.mirror_expr(init)),
|
||||
else_block,
|
||||
lint_level: LintLevel::Explicit(local.hir_id),
|
||||
},
|
||||
opt_destruction_scope: opt_dxn_ext,
|
||||
|
@ -21,7 +21,7 @@ use rustc_session::lint::builtin::{
|
||||
};
|
||||
use rustc_session::Session;
|
||||
use rustc_span::source_map::Spanned;
|
||||
use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span};
|
||||
use rustc_span::{BytePos, Span};
|
||||
|
||||
pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: DefId) {
|
||||
let body_id = match def_id.as_local() {
|
||||
@ -75,8 +75,11 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>) {
|
||||
intravisit::walk_local(self, loc);
|
||||
fn visit_local(&mut self, loc: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) {
|
||||
intravisit::walk_local(self, loc, els);
|
||||
if let Some(init) = &loc.init && els.is_some() {
|
||||
self.check_let(&loc.pat, &init, loc.span);
|
||||
}
|
||||
|
||||
let (msg, sp) = match loc.source {
|
||||
hir::LocalSource::Normal => ("local binding", Some(loc.span)),
|
||||
@ -84,7 +87,9 @@ impl<'tcx> Visitor<'tcx> for MatchVisitor<'_, '_, 'tcx> {
|
||||
hir::LocalSource::AwaitDesugar => ("`await` future binding", None),
|
||||
hir::LocalSource::AssignDesugar(_) => ("destructuring assignment binding", None),
|
||||
};
|
||||
self.check_irrefutable(&loc.pat, msg, sp);
|
||||
if els.is_none() {
|
||||
self.check_irrefutable(&loc.pat, msg, sp);
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) {
|
||||
@ -1125,17 +1130,16 @@ fn let_source_parent(tcx: TyCtxt<'_>, parent: HirId, pat_id: Option<HirId>) -> L
|
||||
}) if Some(*hir_id) == pat_id => {
|
||||
return LetSource::IfLetGuard;
|
||||
}
|
||||
hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Let(..), span, .. }) => {
|
||||
let expn_data = span.ctxt().outer_expn_data();
|
||||
if let ExpnKind::Desugaring(DesugaringKind::LetElse) = expn_data.kind {
|
||||
return LetSource::LetElse(expn_data.call_site);
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
let parent_parent = hir.get_parent_node(parent);
|
||||
let parent_parent_node = hir.get(parent_parent);
|
||||
if let hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(_, Some(_)), span, .. }) =
|
||||
parent_parent_node
|
||||
{
|
||||
return LetSource::LetElse(*span);
|
||||
}
|
||||
|
||||
let parent_parent_parent = hir.get_parent_node(parent_parent);
|
||||
let parent_parent_parent_parent = hir.get_parent_node(parent_parent_parent);
|
||||
|
@ -102,7 +102,7 @@ fn is_needs_drop_and_init<'tcx>(
|
||||
let field_needs_drop_and_init = |(f, f_ty, mpi)| {
|
||||
let child = move_path_children_matching(move_data, mpi, |x| x.is_field_to(f));
|
||||
let Some(mpi) = child else {
|
||||
return f_ty.needs_drop(tcx, param_env);
|
||||
return Ty::needs_drop(f_ty, tcx, param_env);
|
||||
};
|
||||
|
||||
is_needs_drop_and_init(tcx, param_env, maybe_inits, move_data, f_ty, mpi)
|
||||
|
@ -2311,7 +2311,7 @@ impl<'tcx> Visitor<'tcx> for CheckAttrVisitor<'tcx> {
|
||||
|
||||
fn visit_stmt(&mut self, stmt: &'tcx hir::Stmt<'tcx>) {
|
||||
// When checking statements ignore expressions, they will be checked later.
|
||||
if let hir::StmtKind::Local(ref l) = stmt.kind {
|
||||
if let hir::StmtKind::Local(ref l, _) = stmt.kind {
|
||||
self.check_attributes(l.hir_id, stmt.span, Target::Statement, None);
|
||||
}
|
||||
intravisit::walk_stmt(self, stmt)
|
||||
|
@ -131,9 +131,9 @@ impl<'v> hir_visit::Visitor<'v> for StatCollector<'v> {
|
||||
hir_visit::walk_foreign_item(self, i)
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'v hir::Local<'v>) {
|
||||
fn visit_local(&mut self, l: &'v hir::Local<'v>, e: Option<&'v hir::Block<'v>>) {
|
||||
self.record("Local", Id::Node(l.hir_id), l);
|
||||
hir_visit::walk_local(self, l)
|
||||
hir_visit::walk_local(self, l, e)
|
||||
}
|
||||
|
||||
fn visit_block(&mut self, b: &'v hir::Block<'v>) {
|
||||
|
@ -278,7 +278,7 @@ impl<'tcx> IrMaps<'tcx> {
|
||||
pats.extend(inner_pat.iter());
|
||||
}
|
||||
Struct(_, fields, _) => {
|
||||
let (short, not_short): (Vec<&_>, Vec<&_>) =
|
||||
let (short, not_short): (Vec<_>, _) =
|
||||
fields.iter().partition(|f| f.is_shorthand);
|
||||
shorthand_field_ids.extend(short.iter().map(|f| f.pat.hir_id));
|
||||
pats.extend(not_short.iter().map(|f| f.pat));
|
||||
@ -298,7 +298,7 @@ impl<'tcx> IrMaps<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
return shorthand_field_ids;
|
||||
shorthand_field_ids
|
||||
}
|
||||
|
||||
fn add_from_pat(&mut self, pat: &hir::Pat<'tcx>) {
|
||||
@ -366,9 +366,12 @@ impl<'tcx> Visitor<'tcx> for IrMaps<'tcx> {
|
||||
lsets.warn_about_unused_args(body, entry_ln);
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) {
|
||||
self.add_from_pat(&local.pat);
|
||||
intravisit::walk_local(self, local);
|
||||
if els.is_some() {
|
||||
self.add_live_node_for_node(local.hir_id, ExprNode(local.span, local.hir_id));
|
||||
}
|
||||
intravisit::walk_local(self, local, els);
|
||||
}
|
||||
|
||||
fn visit_arm(&mut self, arm: &'tcx hir::Arm<'tcx>) {
|
||||
@ -785,7 +788,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
|
||||
fn propagate_through_stmt(&mut self, stmt: &hir::Stmt<'_>, succ: LiveNode) -> LiveNode {
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(ref local) => {
|
||||
hir::StmtKind::Local(ref local, els) => {
|
||||
// Note: we mark the variable as defined regardless of whether
|
||||
// there is an initializer. Initially I had thought to only mark
|
||||
// the live variable as defined if it was initialized, and then we
|
||||
@ -800,8 +803,40 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
// initialization, which is mildly more complex than checking
|
||||
// once at the func header but otherwise equivalent.
|
||||
|
||||
let succ = self.propagate_through_opt_expr(local.init, succ);
|
||||
self.define_bindings_in_pat(&local.pat, succ)
|
||||
if let Some(els) = els {
|
||||
// Eventually, `let pat: ty = init else { els };` is mostly equivalent to
|
||||
// `let (bindings, ...) = match init { pat => (bindings, ...), _ => els };`
|
||||
// except that extended lifetime applies at the `init` location.
|
||||
//
|
||||
// (e)
|
||||
// |
|
||||
// v
|
||||
// (expr)
|
||||
// / \
|
||||
// | |
|
||||
// v v
|
||||
// bindings els
|
||||
// |
|
||||
// v
|
||||
// ( succ )
|
||||
//
|
||||
if let Some(init) = local.init {
|
||||
let else_ln = self.propagate_through_block(els, succ);
|
||||
let ln = self.live_node(local.hir_id, local.span);
|
||||
self.init_from_succ(ln, succ);
|
||||
self.merge_from_succ(ln, else_ln);
|
||||
let succ = self.propagate_through_expr(init, ln);
|
||||
self.define_bindings_in_pat(&local.pat, succ)
|
||||
} else {
|
||||
span_bug!(
|
||||
stmt.span,
|
||||
"variable is uninitialized but an unexpected else branch is found"
|
||||
)
|
||||
}
|
||||
} else {
|
||||
let succ = self.propagate_through_opt_expr(local.init, succ);
|
||||
self.define_bindings_in_pat(&local.pat, succ)
|
||||
}
|
||||
}
|
||||
hir::StmtKind::Item(..) => succ,
|
||||
hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => {
|
||||
@ -1121,7 +1156,7 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
// (rvalue) || (rvalue)
|
||||
// | || |
|
||||
// v || v
|
||||
// (write of place) || (place components)
|
||||
// (write of place) || (place components)
|
||||
// | || |
|
||||
// v || v
|
||||
// (succ) || (succ)
|
||||
@ -1306,14 +1341,14 @@ impl<'a, 'tcx> Liveness<'a, 'tcx> {
|
||||
// Checking for error conditions
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for Liveness<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) {
|
||||
self.check_unused_vars_in_pat(&local.pat, None, |spans, hir_id, ln, var| {
|
||||
if local.init.is_some() {
|
||||
self.warn_about_dead_assign(spans, hir_id, ln, var);
|
||||
}
|
||||
});
|
||||
|
||||
intravisit::walk_local(self, local);
|
||||
intravisit::walk_local(self, local, els);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
|
@ -1275,7 +1275,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
|
||||
intravisit::walk_pat(self, pattern);
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) {
|
||||
if let Some(init) = local.init {
|
||||
if self.check_expr_pat_type(init.hir_id, init.span) {
|
||||
// Do not report duplicate errors for `let x = y`.
|
||||
@ -1283,7 +1283,7 @@ impl<'tcx> Visitor<'tcx> for TypePrivacyVisitor<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
intravisit::walk_local(self, local);
|
||||
intravisit::walk_local(self, local, els);
|
||||
}
|
||||
|
||||
// Check types in item interfaces.
|
||||
|
@ -82,14 +82,7 @@ impl<'tcx> DumpVisitor<'tcx> {
|
||||
pub fn new(save_ctxt: SaveContext<'tcx>) -> DumpVisitor<'tcx> {
|
||||
let span_utils = SpanUtils::new(&save_ctxt.tcx.sess);
|
||||
let dumper = Dumper::new(save_ctxt.config.clone());
|
||||
DumpVisitor {
|
||||
tcx: save_ctxt.tcx,
|
||||
save_ctxt,
|
||||
dumper,
|
||||
span: span_utils,
|
||||
// mac_defs: FxHashSet::default(),
|
||||
// macro_calls: FxHashSet::default(),
|
||||
}
|
||||
DumpVisitor { tcx: save_ctxt.tcx, save_ctxt, dumper, span: span_utils }
|
||||
}
|
||||
|
||||
pub fn analysis(&self) -> &rls_data::Analysis {
|
||||
@ -1421,13 +1414,14 @@ impl<'tcx> Visitor<'tcx> for DumpVisitor<'tcx> {
|
||||
intravisit::walk_stmt(self, s)
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) {
|
||||
self.process_macro_use(l.span);
|
||||
self.process_var_decl(&l.pat);
|
||||
|
||||
// Just walk the initializer and type (don't want to walk the pattern again).
|
||||
// Just walk the initializer, the else branch and type (don't want to walk the pattern again).
|
||||
walk_list!(self, visit_ty, &l.ty);
|
||||
walk_list!(self, visit_expr, &l.init);
|
||||
walk_list!(self, visit_block, e);
|
||||
}
|
||||
|
||||
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
|
||||
|
@ -1141,7 +1141,6 @@ pub enum DesugaringKind {
|
||||
Async,
|
||||
Await,
|
||||
ForLoop,
|
||||
LetElse,
|
||||
WhileLoop,
|
||||
}
|
||||
|
||||
@ -1157,7 +1156,6 @@ impl DesugaringKind {
|
||||
DesugaringKind::YeetExpr => "`do yeet` expression",
|
||||
DesugaringKind::OpaqueTy => "`impl Trait`",
|
||||
DesugaringKind::ForLoop => "`for` loop",
|
||||
DesugaringKind::LetElse => "`let...else`",
|
||||
DesugaringKind::WhileLoop => "`while` loop",
|
||||
}
|
||||
}
|
||||
|
@ -734,7 +734,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
let hir_id = hir.local_def_id_to_hir_id(def_id.as_local()?);
|
||||
let parent_node = hir.get_parent_node(hir_id);
|
||||
match hir.find(parent_node) {
|
||||
Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local), .. })) => {
|
||||
Some(hir::Node::Stmt(hir::Stmt { kind: hir::StmtKind::Local(local, _), .. })) => {
|
||||
get_name(err, &local.pat.kind)
|
||||
}
|
||||
// Different to previous arm because one is `&hir::Local` and the other
|
||||
@ -1311,7 +1311,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
|
||||
visitor.visit_body(&body);
|
||||
|
||||
let typeck_results = self.in_progress_typeck_results.map(|t| t.borrow()).unwrap();
|
||||
let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id) else { return false; };
|
||||
let Some(liberated_sig) = typeck_results.liberated_fn_sigs().get(fn_hir_id).copied() else { return false; };
|
||||
|
||||
let ret_types = visitor
|
||||
.returns
|
||||
|
@ -997,26 +997,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
coerce.coerce(self, &self.misc(sp), then_expr, then_ty);
|
||||
|
||||
if let Some(else_expr) = opt_else_expr {
|
||||
let else_ty = if sp.desugaring_kind() == Some(DesugaringKind::LetElse) {
|
||||
// todo introduce `check_expr_with_expectation(.., Expectation::LetElse)`
|
||||
// for errors that point to the offending expression rather than the entire block.
|
||||
// We could use `check_expr_eq_type(.., tcx.types.never)`, but then there is no
|
||||
// way to detect that the expected type originated from let-else and provide
|
||||
// a customized error.
|
||||
let else_ty = self.check_expr(else_expr);
|
||||
let cause = self.cause(else_expr.span, ObligationCauseCode::LetElse);
|
||||
|
||||
if let Some(mut err) =
|
||||
self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
|
||||
{
|
||||
err.emit();
|
||||
self.tcx.ty_error()
|
||||
} else {
|
||||
else_ty
|
||||
}
|
||||
} else {
|
||||
self.check_expr_with_expectation(else_expr, expected)
|
||||
};
|
||||
let else_ty = self.check_expr_with_expectation(else_expr, expected);
|
||||
let else_diverges = self.diverges.get();
|
||||
|
||||
let opt_suggest_box_span = self.opt_suggest_box_span(else_ty, orig_expected);
|
||||
|
@ -1218,8 +1218,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
}
|
||||
|
||||
/// Type check a `let` statement.
|
||||
pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) {
|
||||
pub fn check_decl_local(
|
||||
&self,
|
||||
local: &'tcx hir::Local<'tcx>,
|
||||
els: Option<&'tcx hir::Block<'tcx>>,
|
||||
) {
|
||||
self.check_decl(local.into());
|
||||
if let Some(blk) = els {
|
||||
let previous_diverges = self.diverges.get();
|
||||
let else_ty = self.check_block_with_expected(blk, NoExpectation);
|
||||
let cause = self.cause(blk.span, ObligationCauseCode::LetElse);
|
||||
if let Some(mut err) =
|
||||
self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty)
|
||||
{
|
||||
err.emit();
|
||||
}
|
||||
self.diverges.set(previous_diverges);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) {
|
||||
@ -1236,8 +1251,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let old_has_errors = self.has_errors.replace(false);
|
||||
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(ref l) => {
|
||||
self.check_decl_local(&l);
|
||||
hir::StmtKind::Local(l, e) => {
|
||||
self.check_decl_local(l, e);
|
||||
}
|
||||
// Ignore for now.
|
||||
hir::StmtKind::Item(_) => {}
|
||||
@ -1396,7 +1411,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
source:
|
||||
hir::LocalSource::AssignDesugar(_),
|
||||
..
|
||||
}),
|
||||
}, _),
|
||||
..
|
||||
},
|
||||
hir::Stmt {
|
||||
|
@ -99,9 +99,9 @@ impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> {
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
|
||||
// Add explicitly-declared locals.
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) {
|
||||
fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>, els: Option<&'tcx hir::Block<'tcx>>) {
|
||||
self.declare(local.into());
|
||||
intravisit::walk_local(self, local);
|
||||
intravisit::walk_local(self, local, els)
|
||||
}
|
||||
|
||||
fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) {
|
||||
|
@ -460,6 +460,7 @@ fn resolve_local<'tcx>(
|
||||
visitor: &mut RegionResolutionVisitor<'tcx>,
|
||||
pat: Option<&'tcx hir::Pat<'tcx>>,
|
||||
init: Option<&'tcx hir::Expr<'tcx>>,
|
||||
els: Option<&'tcx hir::Block<'tcx>>,
|
||||
) {
|
||||
debug!("resolve_local(pat={:?}, init={:?})", pat, init);
|
||||
|
||||
@ -537,13 +538,18 @@ fn resolve_local<'tcx>(
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure we visit the initializer first, so expr_and_pat_count remains correct
|
||||
// Make sure we visit the initializer first, so expr_and_pat_count remains correct.
|
||||
// The correct order, as shared between generator_interior, drop_ranges and intravisitor,
|
||||
// is to walk initializer, followed by pattern bindings, finally followed by the `else` block.
|
||||
if let Some(expr) = init {
|
||||
visitor.visit_expr(expr);
|
||||
}
|
||||
if let Some(pat) = pat {
|
||||
visitor.visit_pat(pat);
|
||||
}
|
||||
if let Some(els) = els {
|
||||
visitor.visit_block(els);
|
||||
}
|
||||
|
||||
/// Returns `true` if `pat` match the `P&` non-terminal.
|
||||
///
|
||||
@ -764,7 +770,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
|
||||
// (i.e., `'static`), which means that after `g` returns, it drops,
|
||||
// and all the associated destruction scope rules apply.
|
||||
self.cx.var_parent = None;
|
||||
resolve_local(self, None, Some(&body.value));
|
||||
resolve_local(self, None, Some(&body.value), None);
|
||||
}
|
||||
|
||||
if body.generator_kind.is_some() {
|
||||
@ -790,8 +796,8 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
|
||||
fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
|
||||
resolve_expr(self, ex);
|
||||
}
|
||||
fn visit_local(&mut self, l: &'tcx Local<'tcx>) {
|
||||
resolve_local(self, Some(&l.pat), l.init);
|
||||
fn visit_local(&mut self, l: &'tcx Local<'tcx>, e: Option<&'tcx Block<'tcx>>) {
|
||||
resolve_local(self, Some(&l.pat), l.init, e)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -321,8 +321,8 @@ impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> {
|
||||
intravisit::walk_pat(self, p);
|
||||
}
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) {
|
||||
intravisit::walk_local(self, l);
|
||||
fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>, e: Option<&'tcx hir::Block<'tcx>>) {
|
||||
intravisit::walk_local(self, l, e);
|
||||
let var_ty = self.fcx.local_ty(l.span, l.hir_id).decl_ty;
|
||||
let var_ty = self.resolve(var_ty, &l.span);
|
||||
self.write_ty_to_typeck_results(l.hir_id, var_ty);
|
||||
|
@ -252,7 +252,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
hir::ExprKind::Let(hir::Let { pat, init, .. }) => {
|
||||
self.walk_local(init, pat, |t| t.borrow_expr(init, ty::ImmBorrow));
|
||||
self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow))
|
||||
}
|
||||
|
||||
hir::ExprKind::Match(ref discr, arms, _) => {
|
||||
@ -453,11 +453,11 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
|
||||
fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }) => {
|
||||
self.walk_local(expr, pat, |_| {});
|
||||
hir::StmtKind::Local(hir::Local { pat, init: Some(expr), .. }, els) => {
|
||||
self.walk_local(expr, pat, els, |_| {})
|
||||
}
|
||||
|
||||
hir::StmtKind::Local(_) => {}
|
||||
hir::StmtKind::Local(_, _) => {}
|
||||
|
||||
hir::StmtKind::Item(_) => {
|
||||
// We don't visit nested items in this visitor,
|
||||
@ -470,13 +470,23 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn walk_local<F>(&mut self, expr: &hir::Expr<'_>, pat: &hir::Pat<'_>, mut f: F)
|
||||
where
|
||||
fn walk_local<F>(
|
||||
&mut self,
|
||||
expr: &hir::Expr<'_>,
|
||||
pat: &hir::Pat<'_>,
|
||||
els: Option<&hir::Block<'_>>,
|
||||
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);
|
||||
if let Some(els) = els {
|
||||
// borrowing because we need to test the descriminant
|
||||
self.borrow_expr(expr, ImmBorrow);
|
||||
self.walk_block(els)
|
||||
}
|
||||
self.walk_irrefutable_pat(&expr_place, &pat);
|
||||
}
|
||||
|
||||
@ -667,7 +677,7 @@ impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> {
|
||||
let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self;
|
||||
return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| {
|
||||
if let PatKind::Binding(_, canonical_id, ..) = pat.kind {
|
||||
debug!("walk_pat: binding place={:?} pat={:?}", place, pat,);
|
||||
debug!("walk_pat: binding place={:?} pat={:?}", place, pat);
|
||||
if let Some(bm) =
|
||||
mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span)
|
||||
{
|
||||
|
@ -2,7 +2,9 @@ error[E0308]: mismatched types
|
||||
--> $DIR/let-else-binding-explicit-mut-annotated.rs:9:37
|
||||
|
|
||||
LL | let Some(n): &mut Option<i32> = &&Some(5i32) else { return };
|
||||
| ^^^^^^^^^^^^ types differ in mutability
|
||||
| ---------------- ^^^^^^^^^^^^ types differ in mutability
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected mutable reference `&mut Option<i32>`
|
||||
found reference `&&Option<i32>`
|
||||
@ -11,7 +13,9 @@ error[E0308]: mismatched types
|
||||
--> $DIR/let-else-binding-explicit-mut-annotated.rs:13:37
|
||||
|
|
||||
LL | let Some(n): &mut Option<i32> = &&mut Some(5i32) else { return };
|
||||
| ^^^^^^^^^^^^^^^^ types differ in mutability
|
||||
| ---------------- ^^^^^^^^^^^^^^^^ types differ in mutability
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected mutable reference `&mut Option<i32>`
|
||||
found reference `&&mut Option<i32>`
|
||||
|
@ -1,8 +1,8 @@
|
||||
error: unused variable: `x`
|
||||
--> $DIR/let-else-check.rs:18:9
|
||||
--> $DIR/let-else-check.rs:14:13
|
||||
|
|
||||
LL | let x = 1;
|
||||
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
||||
LL | let x = 1;
|
||||
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
||||
|
|
||||
note: the lint level is defined here
|
||||
--> $DIR/let-else-check.rs:3:9
|
||||
@ -11,10 +11,10 @@ LL | #![deny(unused_variables)]
|
||||
| ^^^^^^^^^^^^^^^^
|
||||
|
||||
error: unused variable: `x`
|
||||
--> $DIR/let-else-check.rs:14:13
|
||||
--> $DIR/let-else-check.rs:18:9
|
||||
|
|
||||
LL | let x = 1;
|
||||
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
||||
LL | let x = 1;
|
||||
| ^ help: if this is intentional, prefix it with an underscore: `_x`
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
|
||||
|
@ -1,8 +1,11 @@
|
||||
error[E0308]: `else` clause of `let...else` does not diverge
|
||||
--> $DIR/let-else-non-diverging.rs:12:32
|
||||
--> $DIR/let-else-non-diverging.rs:4:32
|
||||
|
|
||||
LL | let Some(x) = Some(1) else { Some(2) };
|
||||
| ^^^^^^^^^^^ expected `!`, found enum `Option`
|
||||
LL | let Some(x) = Some(1) else {
|
||||
| ________________________________^
|
||||
LL | | Some(2)
|
||||
LL | | };
|
||||
| |_____^ expected `!`, found enum `Option`
|
||||
|
|
||||
= note: expected type `!`
|
||||
found enum `Option<{integer}>`
|
||||
@ -26,13 +29,10 @@ LL | | };
|
||||
= help: ...or use `match` instead of `let...else`
|
||||
|
||||
error[E0308]: `else` clause of `let...else` does not diverge
|
||||
--> $DIR/let-else-non-diverging.rs:4:32
|
||||
--> $DIR/let-else-non-diverging.rs:12:32
|
||||
|
|
||||
LL | let Some(x) = Some(1) else {
|
||||
| ________________________________^
|
||||
LL | | Some(2)
|
||||
LL | | };
|
||||
| |_____^ expected `!`, found enum `Option`
|
||||
LL | let Some(x) = Some(1) else { Some(2) };
|
||||
| ^^^^^^^^^^^ expected `!`, found enum `Option`
|
||||
|
|
||||
= note: expected type `!`
|
||||
found enum `Option<{integer}>`
|
||||
|
@ -20,7 +20,9 @@ error[E0308]: mismatched types
|
||||
--> $DIR/let-else-ref-bindings.rs:24:34
|
||||
|
|
||||
LL | let Some(a): Option<&[u8]> = some else { return };
|
||||
| ^^^^ expected `&[u8]`, found struct `Vec`
|
||||
| ------------- ^^^^ expected `&[u8]`, found struct `Vec`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected enum `Option<&[u8]>`
|
||||
found enum `Option<Vec<u8>>`
|
||||
@ -29,7 +31,9 @@ error[E0308]: mismatched types
|
||||
--> $DIR/let-else-ref-bindings.rs:27:34
|
||||
|
|
||||
LL | let Some(a): Option<&[u8]> = &some else { return };
|
||||
| ^^^^^ expected enum `Option`, found `&Option<Vec<u8>>`
|
||||
| ------------- ^^^^^ expected enum `Option`, found `&Option<Vec<u8>>`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected enum `Option<&[u8]>`
|
||||
found reference `&Option<Vec<u8>>`
|
||||
@ -56,7 +60,9 @@ error[E0308]: mismatched types
|
||||
--> $DIR/let-else-ref-bindings.rs:52:38
|
||||
|
|
||||
LL | let Some(a): Option<&mut [u8]> = some else { return };
|
||||
| ^^^^ expected `&mut [u8]`, found struct `Vec`
|
||||
| ----------------- ^^^^ expected `&mut [u8]`, found struct `Vec`
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected enum `Option<&mut [u8]>`
|
||||
found enum `Option<Vec<u8>>`
|
||||
@ -65,7 +71,9 @@ error[E0308]: mismatched types
|
||||
--> $DIR/let-else-ref-bindings.rs:55:38
|
||||
|
|
||||
LL | let Some(a): Option<&mut [u8]> = &mut some else { return };
|
||||
| ^^^^^^^^^ expected enum `Option`, found mutable reference
|
||||
| ----------------- ^^^^^^^^^ expected enum `Option`, found mutable reference
|
||||
| |
|
||||
| expected due to this
|
||||
|
|
||||
= note: expected enum `Option<&mut [u8]>`
|
||||
found mutable reference `&mut Option<Vec<u8>>`
|
||||
|
25
src/test/ui/let-else/let-else-temporary-lifetime.rs
Normal file
25
src/test/ui/let-else/let-else-temporary-lifetime.rs
Normal file
@ -0,0 +1,25 @@
|
||||
// run-pass
|
||||
#![feature(let_else)]
|
||||
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
static TRACKER: AtomicU8 = AtomicU8::new(0);
|
||||
|
||||
#[derive(Default)]
|
||||
struct Droppy {
|
||||
inner: u32,
|
||||
}
|
||||
|
||||
impl Drop for Droppy {
|
||||
fn drop(&mut self) {
|
||||
TRACKER.store(1, Ordering::Release);
|
||||
println!("I've been dropped");
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
assert_eq!(TRACKER.load(Ordering::Acquire), 0);
|
||||
let 0 = Droppy::default().inner else { return };
|
||||
assert_eq!(TRACKER.load(Ordering::Acquire), 1);
|
||||
println!("Should have dropped 👆");
|
||||
}
|
@ -505,7 +505,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
|
||||
.as_ref()
|
||||
.map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
|
||||
|stmt| match &stmt.kind {
|
||||
StmtKind::Local(_) => true,
|
||||
StmtKind::Local(_, _) => true,
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
|
||||
StmtKind::Item(_) => false,
|
||||
},
|
||||
|
@ -324,7 +324,7 @@ impl BlockEq {
|
||||
|
||||
/// If the statement is a local, checks if the bound names match the expected list of names.
|
||||
fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
|
||||
if let StmtKind::Local(l) = s.kind {
|
||||
if let StmtKind::Local(l, _) = s.kind {
|
||||
let mut i = 0usize;
|
||||
let mut res = true;
|
||||
l.pat.each_binding_or_first(&mut |_, _, _, name| {
|
||||
@ -349,7 +349,7 @@ fn eq_stmts(
|
||||
eq: &mut HirEqInterExpr<'_, '_, '_>,
|
||||
moved_bindings: &mut Vec<(HirId, Symbol)>,
|
||||
) -> bool {
|
||||
(if let StmtKind::Local(l) = stmt.kind {
|
||||
(if let StmtKind::Local(l, _) = stmt.kind {
|
||||
let old_count = moved_bindings.len();
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, name| {
|
||||
moved_bindings.push((id, name.name));
|
||||
@ -435,7 +435,7 @@ fn scan_block_for_eq(cx: &LateContext<'_>, _conds: &[&Expr<'_>], block: &Block<'
|
||||
// Clear out all locals seen at the end so far. None of them can be moved.
|
||||
let stmts = &blocks[0].stmts;
|
||||
for stmt in &stmts[stmts.len() - init..=stmts.len() - offset] {
|
||||
if let StmtKind::Local(l) = stmt.kind {
|
||||
if let StmtKind::Local(l, _) = stmt.kind {
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, _| {
|
||||
eq.locals.remove(&id);
|
||||
});
|
||||
|
@ -126,7 +126,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
|
||||
// checked and the name of the bound variable
|
||||
let (local, variant, binding_name, binding_type, span) = if_chain! {
|
||||
// only take `let ...` statements
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let StmtKind::Local(local, _) = stmt.kind;
|
||||
if let Some(expr) = local.init;
|
||||
if !any_parent_is_automatically_derived(cx.tcx, expr.hir_id);
|
||||
if !expr.span.from_expansion();
|
||||
|
@ -192,7 +192,7 @@ impl<'a, 'tcx> Visitor<'tcx> for NumericFallbackVisitor<'a, 'tcx> {
|
||||
|
||||
fn visit_stmt(&mut self, stmt: &'tcx Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Local(local, _) => {
|
||||
if local.ty.is_some() {
|
||||
self.ty_bounds.push(TyBound::Any);
|
||||
} else {
|
||||
|
@ -386,7 +386,7 @@ impl<'tcx> Visitor<'tcx> for InsertSearcher<'_, 'tcx> {
|
||||
}
|
||||
},
|
||||
StmtKind::Expr(e) => self.visit_expr(e),
|
||||
StmtKind::Local(l) => {
|
||||
StmtKind::Local(l, _) => {
|
||||
self.visit_pat(l.pat);
|
||||
if let Some(e) = l.init {
|
||||
self.allow_insert_closure &= !self.in_tail_pos;
|
||||
|
@ -116,7 +116,7 @@ fn look_in_block<'tcx, 'hir>(cx: &LateContext<'tcx>, kind: &'tcx ExprKind<'hir>)
|
||||
if_chain! {
|
||||
if let ExprKind::Block(block, _label @ None) = kind;
|
||||
if let Block {
|
||||
stmts: [Stmt { kind: StmtKind::Local(local), .. }],
|
||||
stmts: [Stmt { kind: StmtKind::Local(local, _), .. }],
|
||||
expr: Some(expr_end_of_block),
|
||||
rules: BlockCheckMode::DefaultBlock,
|
||||
..
|
||||
|
@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
|
||||
while let Some(stmt) = it.next() {
|
||||
if_chain! {
|
||||
if let Some(expr) = it.peek();
|
||||
if let hir::StmtKind::Local(local) = stmt.kind;
|
||||
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(hir::Expr { kind: hir::ExprKind::DropTemps(cond), ..}, then, else_) = if_.kind;
|
||||
|
@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
|
||||
use clippy_utils::ty::{is_must_use_ty, match_type};
|
||||
use clippy_utils::{is_must_use_func_call, paths};
|
||||
use if_chain::if_chain;
|
||||
use rustc_hir::{Local, PatKind};
|
||||
use rustc_hir::{Block, Local, PatKind};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
@ -109,7 +109,7 @@ const SYNC_GUARD_PATHS: [&[&str]; 6] = [
|
||||
];
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>, _: Option<&Block<'_>>) {
|
||||
if in_external_macro(cx.tcx.sess, local.span) {
|
||||
return;
|
||||
}
|
||||
|
@ -76,7 +76,7 @@ fn check_needless_collect_indirect_usage<'tcx>(expr: &'tcx Expr<'_>, cx: &LateCo
|
||||
if let ExprKind::Block(block, _) = expr.kind {
|
||||
for stmt in block.stmts {
|
||||
if_chain! {
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let StmtKind::Local(local, _) = stmt.kind;
|
||||
if let PatKind::Binding(_, id, ..) = local.pat.kind;
|
||||
if let Some(init_expr) = local.init;
|
||||
if let ExprKind::MethodCall(method_name, &[ref iter_source], ..) = init_expr.kind;
|
||||
@ -276,7 +276,7 @@ fn get_expr_and_hir_id_from_stmt<'v>(stmt: &'v Stmt<'v>) -> Option<(&'v Expr<'v>
|
||||
match stmt.kind {
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some((expr, None)),
|
||||
StmtKind::Item(..) => None,
|
||||
StmtKind::Local(Local { init, pat, .. }) => {
|
||||
StmtKind::Local(Local { init, pat, .. }, _) => {
|
||||
if let PatKind::Binding(_, hir_id, ..) = pat.kind {
|
||||
init.map(|init_expr| (init_expr, Some(hir_id)))
|
||||
} else {
|
||||
|
@ -104,7 +104,7 @@ fn never_loop_expr_seq<'a, T: Iterator<Item = &'a Expr<'a>>>(es: &mut T, main_lo
|
||||
fn stmt_to_expr<'tcx>(stmt: &Stmt<'tcx>) -> Option<&'tcx Expr<'tcx>> {
|
||||
match stmt.kind {
|
||||
StmtKind::Semi(e, ..) | StmtKind::Expr(e, ..) => Some(e),
|
||||
StmtKind::Local(local) => local.init,
|
||||
StmtKind::Local(local, _) => local.init,
|
||||
StmtKind::Item(..) => None,
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ use if_chain::if_chain;
|
||||
use rustc_ast::ast::{LitIntType, LitKind};
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, walk_local, walk_pat, walk_stmt, Visitor};
|
||||
use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
|
||||
use rustc_hir::{BinOpKind, Block, BorrowKind, Expr, ExprKind, HirId, HirIdMap, Local, Mutability, Pat, PatKind, Stmt};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::hir::nested_filter;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
@ -148,7 +148,7 @@ impl<'a, 'tcx> InitializeVisitor<'a, 'tcx> {
|
||||
impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
|
||||
type NestedFilter = nested_filter::OnlyBodies;
|
||||
|
||||
fn visit_local(&mut self, l: &'tcx Local<'_>) {
|
||||
fn visit_local(&mut self, l: &'tcx Local<'_>, e: Option<&'tcx Block<'_>>) {
|
||||
// Look for declarations of the variable
|
||||
if_chain! {
|
||||
if l.pat.hir_id == self.var_id;
|
||||
@ -166,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for InitializeVisitor<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
walk_local(self, l);
|
||||
walk_local(self, l, e);
|
||||
}
|
||||
|
||||
fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
|
||||
|
@ -11,7 +11,7 @@ use rustc_lint::LateContext;
|
||||
pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
|
||||
let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
|
||||
([stmt, stmts @ ..], expr) => {
|
||||
if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
|
||||
if let StmtKind::Local(&Local { init: Some(e), .. }, None) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
|
||||
(e, !stmts.is_empty() || expr.is_some())
|
||||
} else {
|
||||
return;
|
||||
|
@ -8,7 +8,7 @@ use clippy_utils::{
|
||||
use if_chain::if_chain;
|
||||
use rustc_errors::Applicability;
|
||||
use rustc_hir::intravisit::{walk_expr, Visitor};
|
||||
use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
|
||||
use rustc_hir::{def::Res, Block, Expr, ExprKind, HirId, Local, Mutability, PatKind, QPath, UnOp};
|
||||
use rustc_lint::LateContext;
|
||||
use rustc_middle::ty::adjustment::Adjust;
|
||||
use rustc_span::{symbol::sym, Symbol};
|
||||
@ -283,7 +283,7 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
|
||||
used_after: bool,
|
||||
}
|
||||
impl<'a, 'b, 'tcx> Visitor<'tcx> for NestedLoopVisitor<'a, 'b, 'tcx> {
|
||||
fn visit_local(&mut self, l: &'tcx Local<'_>) {
|
||||
fn visit_local(&mut self, l: &'tcx Local<'_>, _: Option<&'tcx Block<'_>>) {
|
||||
if !self.after_loop {
|
||||
l.pat.each_binding_or_first(&mut |_, id, _, _| {
|
||||
if id == self.local_id {
|
||||
|
@ -144,7 +144,7 @@ fn reduce_unit_expression<'a>(cx: &LateContext<'_>, expr: &'a hir::Expr<'_>) ->
|
||||
// If block only contains statements,
|
||||
// reduce `{ X; }` to `X` or `X;`
|
||||
match inner_stmt.kind {
|
||||
hir::StmtKind::Local(local) => Some(local.span),
|
||||
hir::StmtKind::Local(local, _) => Some(local.span),
|
||||
hir::StmtKind::Expr(e) => Some(e.span),
|
||||
hir::StmtKind::Semi(..) => Some(inner_stmt.span),
|
||||
hir::StmtKind::Item(..) => None,
|
||||
|
@ -1,6 +1,6 @@
|
||||
use clippy_utils::source::{snippet_opt, span_starts_with, walk_span_to_context};
|
||||
use clippy_utils::{higher, in_constant, meets_msrv, msrvs};
|
||||
use rustc_hir::{Arm, Expr, ExprKind, Local, MatchSource, Pat};
|
||||
use rustc_hir::{Arm, Block, Expr, ExprKind, Local, MatchSource, Pat};
|
||||
use rustc_lexer::{tokenize, TokenKind};
|
||||
use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
@ -1040,8 +1040,14 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'_>) {
|
||||
self.infallible_destructuring_match_linted |= infallible_destructuring_match::check(cx, local);
|
||||
fn check_local(
|
||||
&mut self,
|
||||
cx: &LateContext<'tcx>,
|
||||
local: &'tcx Local<'_>,
|
||||
els: Option<&'tcx Block<'_>>,
|
||||
) {
|
||||
self.infallible_destructuring_match_linted |=
|
||||
els.is_none() && infallible_destructuring_match::check(cx, local);
|
||||
}
|
||||
|
||||
fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
|
||||
|
@ -220,7 +220,7 @@ fn indirect_usage<'tcx>(
|
||||
init: Some(init_expr),
|
||||
hir_id: local_hir_id,
|
||||
..
|
||||
}) = stmt.kind
|
||||
}, _) = stmt.kind
|
||||
{
|
||||
let mut path_to_binding = None;
|
||||
expr_visitor(cx, |expr| {
|
||||
|
@ -161,7 +161,7 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if_chain! {
|
||||
if !in_external_macro(cx.tcx.sess, stmt.span);
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let StmtKind::Local(local, _) = stmt.kind;
|
||||
if let PatKind::Binding(an, .., name, None) = local.pat.kind;
|
||||
if let Some(init) = local.init;
|
||||
if an == BindingAnnotation::Ref || an == BindingAnnotation::RefMut;
|
||||
|
@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for EvalOrderDependence {
|
||||
}
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
match stmt.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Local(local, _) => {
|
||||
if let Local { init: Some(e), .. } = local {
|
||||
DivergenceVisitor { cx }.visit_expr(e);
|
||||
}
|
||||
@ -273,7 +273,7 @@ fn check_stmt<'a, 'tcx>(vis: &mut ReadVisitor<'a, 'tcx>, stmt: &'tcx Stmt<'_>) -
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => check_expr(vis, expr),
|
||||
// If the declaration is of a local variable, check its initializer
|
||||
// expression if it has one. Otherwise, keep going.
|
||||
StmtKind::Local(local) => local
|
||||
StmtKind::Local(local, _) => local
|
||||
.init
|
||||
.as_ref()
|
||||
.map_or(StopEarly::KeepGoing, |expr| check_expr(vis, expr)),
|
||||
|
@ -101,7 +101,12 @@ impl<'tcx> LateLintPass<'tcx> for MutableKeyType {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &hir::Local<'_>) {
|
||||
fn check_local(
|
||||
&mut self,
|
||||
cx: &LateContext<'_>,
|
||||
local: &hir::Local<'_>,
|
||||
_: Option<&hir::Block<'_>>,
|
||||
) {
|
||||
if let hir::PatKind::Wild = local.pat.kind {
|
||||
return;
|
||||
}
|
||||
|
@ -92,7 +92,7 @@ fn contains_let(cond: &Expr<'_>) -> bool {
|
||||
}
|
||||
|
||||
fn stmt_needs_ordered_drop(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
let StmtKind::Local(local) = stmt.kind else { return false };
|
||||
let StmtKind::Local(local, _) = stmt.kind else { return false };
|
||||
!local.pat.walk_short(|pat| {
|
||||
if let PatKind::Binding(.., None) = pat.kind {
|
||||
!needs_ordered_drop(cx, cx.typeck_results().pat_ty(pat))
|
||||
@ -367,7 +367,7 @@ fn check<'tcx>(
|
||||
}
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>, _: Option<&'tcx Block<'tcx>>) {
|
||||
let mut parents = cx.tcx.hir().parent_iter(local.hir_id);
|
||||
if_chain! {
|
||||
if let Local {
|
||||
|
@ -88,10 +88,11 @@ fn check_no_effect(cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
|
||||
span_lint_hir(cx, NO_EFFECT, expr.hir_id, stmt.span, "statement with no effect");
|
||||
return true;
|
||||
}
|
||||
} else if let StmtKind::Local(local) = stmt.kind {
|
||||
} else if let StmtKind::Local(local, els) = stmt.kind {
|
||||
if_chain! {
|
||||
if !is_lint_allowed(cx, NO_EFFECT_UNDERSCORE_BINDING, local.hir_id);
|
||||
if let Some(init) = local.init;
|
||||
if els.is_none();
|
||||
if !local.pat.span.from_expansion();
|
||||
if has_no_effect(cx, init);
|
||||
if let PatKind::Binding(_, _, ident, _) = local.pat.kind;
|
||||
|
@ -261,13 +261,13 @@ impl<'tcx> Visitor<'tcx> for SideEffectVisit<'tcx> {
|
||||
match s.kind {
|
||||
StmtKind::Local(Local {
|
||||
pat, init: Some(init), ..
|
||||
}) => {
|
||||
}, _) => {
|
||||
self.visit_pat_expr(pat, init, false);
|
||||
},
|
||||
StmtKind::Item(_) | StmtKind::Expr(_) | StmtKind::Semi(_) => {
|
||||
walk_stmt(self, s);
|
||||
},
|
||||
StmtKind::Local(_) => {},
|
||||
StmtKind::Local(_, _) => {},
|
||||
}
|
||||
self.ret_vars.clear();
|
||||
}
|
||||
|
@ -83,7 +83,7 @@ declare_lint_pass!(PatternTypeMismatch => [PATTERN_TYPE_MISMATCH]);
|
||||
|
||||
impl<'tcx> LateLintPass<'tcx> for PatternTypeMismatch {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
if let StmtKind::Local(local) = stmt.kind {
|
||||
if let StmtKind::Local(local, _) = stmt.kind {
|
||||
if in_external_macro(cx.sess(), local.pat.span) {
|
||||
return;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ impl<'tcx> LateLintPass<'tcx> for ReadZeroByteVec {
|
||||
for (idx, stmt) in block.stmts.iter().enumerate() {
|
||||
if !stmt.span.from_expansion()
|
||||
// matches `let v = Vec::new();`
|
||||
&& let StmtKind::Local(local) = stmt.kind
|
||||
&& let StmtKind::Local(local, _) = stmt.kind
|
||||
&& let Local { pat, init: Some(init), .. } = local
|
||||
&& let PatKind::Binding(_, _, ident, _) = pat.kind
|
||||
&& let Some(vec_init_kind) = get_vec_init_kind(cx, init)
|
||||
|
@ -133,7 +133,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
|
||||
|
||||
for w in block.stmts.windows(2) {
|
||||
if_chain! {
|
||||
if let hir::StmtKind::Local(local) = w[0].kind;
|
||||
if let hir::StmtKind::Local(local, _) = w[0].kind;
|
||||
if let Option::Some(t) = local.init;
|
||||
if let hir::ExprKind::Closure { .. } = t.kind;
|
||||
if let hir::PatKind::Binding(_, _, ident, _) = local.pat.kind;
|
||||
|
@ -10,7 +10,6 @@ use rustc_lint::{LateContext, LateLintPass, LintContext};
|
||||
use rustc_middle::lint::in_external_macro;
|
||||
use rustc_middle::ty::subst::GenericArgKind;
|
||||
use rustc_session::{declare_lint_pass, declare_tool_lint};
|
||||
use rustc_span::hygiene::DesugaringKind;
|
||||
use rustc_span::source_map::Span;
|
||||
use rustc_span::sym;
|
||||
|
||||
@ -83,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for Return {
|
||||
if_chain! {
|
||||
if let Some(retexpr) = block.expr;
|
||||
if let Some(stmt) = block.stmts.iter().last();
|
||||
if let StmtKind::Local(local) = &stmt.kind;
|
||||
if let StmtKind::Local(local, _) = &stmt.kind;
|
||||
if local.ty.is_none();
|
||||
if cx.tcx.hir().attrs(local.hir_id).is_empty();
|
||||
if let Some(initexpr) = &local.init;
|
||||
@ -203,9 +202,7 @@ fn check_final_expr<'tcx>(
|
||||
check_block_return(cx, ifblock);
|
||||
}
|
||||
if let Some(else_clause) = else_clause_opt {
|
||||
if expr.span.desugaring_kind() != Some(DesugaringKind::LetElse) {
|
||||
check_final_expr(cx, else_clause, None, RetReplacement::Empty);
|
||||
}
|
||||
check_final_expr(cx, else_clause, None, RetReplacement::Empty);
|
||||
}
|
||||
},
|
||||
// a match expr, check all arms
|
||||
|
@ -98,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for SlowVectorInit {
|
||||
fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
|
||||
// Matches statements which initializes vectors. For example: `let mut vec = Vec::with_capacity(10)`
|
||||
if_chain! {
|
||||
if let StmtKind::Local(local) = stmt.kind;
|
||||
if let StmtKind::Local(local, _) = stmt.kind;
|
||||
if let PatKind::Binding(BindingAnnotation::Mutable, local_id, _, None) = local.pat.kind;
|
||||
if let Some(init) = local.init;
|
||||
if let Some(len_arg) = Self::is_vec_with_capacity(cx, init);
|
||||
|
@ -141,7 +141,7 @@ fn check_manual_swap(cx: &LateContext<'_>, block: &Block<'_>) {
|
||||
for w in block.stmts.windows(3) {
|
||||
if_chain! {
|
||||
// let t = foo();
|
||||
if let StmtKind::Local(tmp) = w[0].kind;
|
||||
if let StmtKind::Local(tmp, _) = w[0].kind;
|
||||
if let Some(tmp_init) = tmp.init;
|
||||
if let PatKind::Binding(.., ident, None) = tmp.pat.kind;
|
||||
|
||||
|
@ -12,7 +12,7 @@ mod vec_box;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_hir::{
|
||||
Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
|
||||
Block, Body, FnDecl, FnRetTy, GenericArg, HirId, ImplItem, ImplItemKind, Item, ItemKind, Local, MutTy, QPath, TraitItem,
|
||||
TraitItemKind, TyKind,
|
||||
};
|
||||
use rustc_lint::{LateContext, LateLintPass};
|
||||
@ -406,7 +406,7 @@ impl<'tcx> LateLintPass<'tcx> for Types {
|
||||
}
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>) {
|
||||
fn check_local(&mut self, cx: &LateContext<'_>, local: &Local<'_>, _: Option<&Block<'_>>) {
|
||||
if let Some(ty) = local.ty {
|
||||
self.check_ty(
|
||||
cx,
|
||||
|
@ -155,7 +155,7 @@ impl<'tcx> VecLocation<'tcx> {
|
||||
/// or `self` expression for `Vec::reserve()`.
|
||||
fn extract_init_or_reserve_target<'tcx>(cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) -> Option<TargetVec<'tcx>> {
|
||||
match stmt.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Local(local, _) => {
|
||||
if_chain! {
|
||||
if let Some(init_expr) = local.init;
|
||||
if let PatKind::Binding(_, hir_id, _, None) = local.pat.kind;
|
||||
|
@ -12,7 +12,7 @@ use rustc_middle::ty::{self, Ty, TypeVisitable, TypeSuperVisitable, TypeVisitor}
|
||||
use super::LET_UNIT_VALUE;
|
||||
|
||||
pub(super) fn check(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
|
||||
if let StmtKind::Local(local) = stmt.kind
|
||||
if let StmtKind::Local(local, _) = stmt.kind
|
||||
&& let Some(init) = local.init
|
||||
&& !local.pat.span.from_expansion()
|
||||
&& !in_external_macro(cx.sess(), stmt.span)
|
||||
|
@ -685,7 +685,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
|
||||
}
|
||||
|
||||
match stmt.value.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Local(local, _) => {
|
||||
bind!(self, local);
|
||||
kind!("Local({local})");
|
||||
self.option(field!(local.init), "init", |init| {
|
||||
|
@ -155,7 +155,7 @@ impl<'tcx> LateLintPass<'tcx> for VecInitThenPush {
|
||||
self.searcher = None;
|
||||
}
|
||||
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>) {
|
||||
fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx Local<'tcx>, _: Option<&'tcx Block<'tcx>>) {
|
||||
if let Some(init_expr) = local.init
|
||||
&& let PatKind::Binding(BindingAnnotation::Mutable, id, name, None) = local.pat.kind
|
||||
&& !in_external_macro(cx.sess(), local.span)
|
||||
|
@ -102,7 +102,7 @@ pub struct HirEqInterExpr<'a, 'b, 'tcx> {
|
||||
impl HirEqInterExpr<'_, '_, '_> {
|
||||
pub fn eq_stmt(&mut self, left: &Stmt<'_>, right: &Stmt<'_>) -> bool {
|
||||
match (&left.kind, &right.kind) {
|
||||
(&StmtKind::Local(l), &StmtKind::Local(r)) => {
|
||||
(&StmtKind::Local(l, le), &StmtKind::Local(r, re)) => {
|
||||
// This additional check ensures that the type of the locals are equivalent even if the init
|
||||
// expression or type have some inferred parts.
|
||||
if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results {
|
||||
@ -117,6 +117,7 @@ impl HirEqInterExpr<'_, '_, '_> {
|
||||
// these only get added if the init and type is equal.
|
||||
both(&l.init, &r.init, |l, r| self.eq_expr(l, r))
|
||||
&& both(&l.ty, &r.ty, |l, r| self.eq_ty(l, r))
|
||||
&& both(&le, &re, |l, r| self.eq_block(l, r))
|
||||
&& self.eq_pat(l.pat, r.pat)
|
||||
},
|
||||
(&StmtKind::Expr(l), &StmtKind::Expr(r)) | (&StmtKind::Semi(l), &StmtKind::Semi(r)) => self.eq_expr(l, r),
|
||||
@ -921,11 +922,14 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
|
||||
std::mem::discriminant(&b.kind).hash(&mut self.s);
|
||||
|
||||
match &b.kind {
|
||||
StmtKind::Local(local) => {
|
||||
StmtKind::Local(local, els) => {
|
||||
self.hash_pat(local.pat);
|
||||
if let Some(init) = local.init {
|
||||
self.hash_expr(init);
|
||||
}
|
||||
if let Some(els) = els {
|
||||
self.hash_block(els);
|
||||
}
|
||||
},
|
||||
StmtKind::Item(..) => {},
|
||||
StmtKind::Expr(expr) | StmtKind::Semi(expr) => {
|
||||
|
@ -1826,7 +1826,7 @@ pub fn is_expr_used_or_unified(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
|
||||
..
|
||||
},
|
||||
..
|
||||
}),
|
||||
}, _),
|
||||
..
|
||||
}),
|
||||
_
|
||||
|
Loading…
Reference in New Issue
Block a user