1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 //! A classic liveness analysis based on dataflow over the AST. Computes,
12 //! for each local variable in a function, whether that variable is live
13 //! at a given point. Program execution points are identified by their
18 //! The basic model is that each local variable is assigned an index. We
19 //! represent sets of local variables using a vector indexed by this
20 //! index. The value in the vector is either 0, indicating the variable
21 //! is dead, or the id of an expression that uses the variable.
23 //! We conceptually walk over the AST in reverse execution order. If we
24 //! find a use of a variable, we add it to the set of live variables. If
25 //! we find an assignment to a variable, we remove it from the set of live
26 //! variables. When we have to merge two flows, we take the union of
27 //! those two flows---if the variable is live on both paths, we simply
28 //! pick one id. In the event of loops, we continue doing this until a
29 //! fixed point is reached.
31 //! ## Checking initialization
33 //! At the function entry point, all variables must be dead. If this is
34 //! not the case, we can report an error using the id found in the set of
35 //! live variables, which identifies a use of the variable which is not
36 //! dominated by an assignment.
40 //! After each explicit move, the variable must be dead.
42 //! ## Computing last uses
44 //! Any use of the variable where the variable is dead afterwards is a
47 //! # Implementation details
49 //! The actual implementation contains two (nested) walks over the AST.
50 //! The outer walk has the job of building up the ir_maps instance for the
51 //! enclosing function. On the way down the tree, it identifies those AST
52 //! nodes and variable IDs that will be needed for the liveness analysis
53 //! and assigns them contiguous IDs. The liveness id for an AST node is
54 //! called a `live_node` (it's a newtype'd usize) and the id for a variable
55 //! is called a `variable` (another newtype'd usize).
57 //! On the way back up the tree, as we are about to exit from a function
58 //! declaration we allocate a `liveness` instance. Now that we know
59 //! precisely how many nodes and variables we need, we can allocate all
60 //! the various arrays that we will need to precisely the right size. We then
61 //! perform the actual propagation on the `liveness` instance.
63 //! This propagation is encoded in the various `propagate_through_*()`
64 //! methods. It effectively does a reverse walk of the AST; whenever we
65 //! reach a loop node, we iterate until a fixed point is reached.
67 //! ## The `Users` struct
69 //! At each live node `N`, we track three pieces of information for each
70 //! variable `V` (these are encapsulated in the `Users` struct):
72 //! - `reader`: the `LiveNode` ID of some node which will read the value
73 //! that `V` holds on entry to `N`. Formally: a node `M` such
74 //! that there exists a path `P` from `N` to `M` where `P` does not
75 //! write `V`. If the `reader` is `invalid_node()`, then the current
76 //! value will never be read (the variable is dead, essentially).
78 //! - `writer`: the `LiveNode` ID of some node which will write the
79 //! variable `V` and which is reachable from `N`. Formally: a node `M`
80 //! such that there exists a path `P` from `N` to `M` and `M` writes
81 //! `V`. If the `writer` is `invalid_node()`, then there is no writer
82 //! of `V` that follows `N`.
84 //! - `used`: a boolean value indicating whether `V` is *used*. We
85 //! distinguish a *read* from a *use* in that a *use* is some read that
86 //! is not just used to generate a new value. For example, `x += 1` is
87 //! a read but not a use. This is used to generate better warnings.
89 //! ## Special Variables
91 //! We generate various special variables for various, well, special purposes.
92 //! These are described in the `specials` struct:
94 //! - `exit_ln`: a live node that is generated to represent every 'exit' from
95 //! the function, whether it be by explicit return, panic, or other means.
97 //! - `fallthrough_ln`: a live node that represents a fallthrough
99 //! - `clean_exit_var`: a synthetic variable that is only 'read' from the
100 //! fallthrough node. It is only live if the function could converge
101 //! via means other than an explicit `return` expression. That is, it is
102 //! only dead if the end of the function's block can never be reached.
103 //! It is the responsibility of typeck to ensure that there are no
104 //! `return` expressions in a function declared as diverging.
105 use self::LoopKind
::*;
106 use self::LiveNodeKind
::*;
107 use self::VarKind
::*;
110 use ty
::{self, TyCtxt}
;
112 use util
::nodemap
::{NodeMap, NodeSet}
;
114 use std
::{fmt, usize}
;
115 use std
::io
::prelude
::*;
118 use syntax
::ast
::{self, NodeId}
;
119 use syntax
::symbol
::keywords
;
120 use syntax_pos
::Span
;
124 use hir
::intravisit
::{self, Visitor, FnKind, NestedVisitorMap}
;
126 /// For use with `propagate_through_loop`.
128 /// An endless `loop` loop.
130 /// A `while` loop, with the given expression as condition.
134 #[derive(Copy, Clone, PartialEq)]
135 struct Variable(usize);
137 #[derive(Copy, PartialEq)]
138 struct LiveNode(usize);
141 fn get(&self) -> usize { let Variable(v) = *self; v }
145 fn get(&self) -> usize { let LiveNode(v) = *self; v }
148 impl Clone
for LiveNode
{
149 fn clone(&self) -> LiveNode
{
154 #[derive(Copy, Clone, PartialEq, Debug)]
162 fn live_node_kind_to_string(lnk
: LiveNodeKind
, tcx
: TyCtxt
) -> String
{
163 let cm
= tcx
.sess
.codemap();
166 format
!("Free var node [{}]", cm
.span_to_string(s
))
169 format
!("Expr node [{}]", cm
.span_to_string(s
))
172 format
!("Var def node [{}]", cm
.span_to_string(s
))
174 ExitNode
=> "Exit node".to_string(),
178 impl<'a
, 'tcx
> Visitor
<'tcx
> for IrMaps
<'a
, 'tcx
> {
179 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
180 NestedVisitorMap
::OnlyBodies(&self.tcx
.hir
)
183 fn visit_fn(&mut self, fk
: FnKind
<'tcx
>, fd
: &'tcx hir
::FnDecl
,
184 b
: hir
::BodyId
, s
: Span
, id
: NodeId
) {
185 visit_fn(self, fk
, fd
, b
, s
, id
);
187 fn visit_local(&mut self, l
: &'tcx hir
::Local
) { visit_local(self, l); }
188 fn visit_expr(&mut self, ex
: &'tcx Expr
) { visit_expr(self, ex); }
189 fn visit_arm(&mut self, a
: &'tcx hir
::Arm
) { visit_arm(self, a); }
192 pub fn check_crate
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>) {
193 tcx
.hir
.krate().visit_all_item_likes(&mut IrMaps
::new(tcx
).as_deep_visitor());
194 tcx
.sess
.abort_if_errors();
197 impl fmt
::Debug
for LiveNode
{
198 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
199 write
!(f
, "ln({})", self.get())
203 impl fmt
::Debug
for Variable
{
204 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
205 write
!(f
, "v({})", self.get())
209 // ______________________________________________________________________
212 // This is the first pass and the one that drives the main
213 // computation. It walks up and down the IR once. On the way down,
214 // we count for each function the number of variables as well as
215 // liveness nodes. A liveness node is basically an expression or
216 // capture clause that does something of interest: either it has
217 // interesting control flow or it uses/defines a local variable.
219 // On the way back up, at each function node we create liveness sets
220 // (we now know precisely how big to make our various vectors and so
221 // forth) and then do the data-flow propagation to compute the set
222 // of live variables at each program point.
224 // Finally, we run back over the IR one last time and, using the
225 // computed liveness, check various safety conditions. For example,
226 // there must be no live nodes at the definition site for a variable
227 // unless it has an initializer. Similarly, each non-mutable local
228 // variable must not be assigned if there is some successor
229 // assignment. And so forth.
232 fn is_valid(&self) -> bool
{
233 self.get() != usize::MAX
237 fn invalid_node() -> LiveNode { LiveNode(usize::MAX) }
244 #[derive(Copy, Clone, Debug)]
251 #[derive(Copy, Clone, Debug)]
253 Arg(NodeId
, ast
::Name
),
258 struct IrMaps
<'a
, 'tcx
: 'a
> {
259 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
261 num_live_nodes
: usize,
263 live_node_map
: NodeMap
<LiveNode
>,
264 variable_map
: NodeMap
<Variable
>,
265 capture_info_map
: NodeMap
<Rc
<Vec
<CaptureInfo
>>>,
266 var_kinds
: Vec
<VarKind
>,
267 lnks
: Vec
<LiveNodeKind
>,
270 impl<'a
, 'tcx
> IrMaps
<'a
, 'tcx
> {
271 fn new(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>) -> IrMaps
<'a
, 'tcx
> {
276 live_node_map
: NodeMap(),
277 variable_map
: NodeMap(),
278 capture_info_map
: NodeMap(),
279 var_kinds
: Vec
::new(),
284 fn add_live_node(&mut self, lnk
: LiveNodeKind
) -> LiveNode
{
285 let ln
= LiveNode(self.num_live_nodes
);
287 self.num_live_nodes
+= 1;
289 debug
!("{:?} is of kind {}", ln
,
290 live_node_kind_to_string(lnk
, self.tcx
));
295 fn add_live_node_for_node(&mut self, node_id
: NodeId
, lnk
: LiveNodeKind
) {
296 let ln
= self.add_live_node(lnk
);
297 self.live_node_map
.insert(node_id
, ln
);
299 debug
!("{:?} is node {}", ln
, node_id
);
302 fn add_variable(&mut self, vk
: VarKind
) -> Variable
{
303 let v
= Variable(self.num_vars
);
304 self.var_kinds
.push(vk
);
308 Local(LocalInfo { id: node_id, .. }
) | Arg(node_id
, _
) => {
309 self.variable_map
.insert(node_id
, v
);
314 debug
!("{:?} is {:?}", v
, vk
);
319 fn variable(&self, node_id
: NodeId
, span
: Span
) -> Variable
{
320 match self.variable_map
.get(&node_id
) {
323 span_bug
!(span
, "no variable registered for id {}", node_id
);
328 fn variable_name(&self, var
: Variable
) -> String
{
329 match self.var_kinds
[var
.get()] {
330 Local(LocalInfo { name, .. }
) | Arg(_
, name
) => {
333 CleanExit
=> "<clean-exit>".to_string()
337 fn variable_is_shorthand(&self, var
: Variable
) -> bool
{
338 match self.var_kinds
[var
.get()] {
339 Local(LocalInfo { is_shorthand, .. }
) => is_shorthand
,
340 Arg(..) | CleanExit
=> false
344 fn set_captures(&mut self, node_id
: NodeId
, cs
: Vec
<CaptureInfo
>) {
345 self.capture_info_map
.insert(node_id
, Rc
::new(cs
));
348 fn lnk(&self, ln
: LiveNode
) -> LiveNodeKind
{
353 fn visit_fn
<'a
, 'tcx
: 'a
>(ir
: &mut IrMaps
<'a
, 'tcx
>,
355 decl
: &'tcx hir
::FnDecl
,
356 body_id
: hir
::BodyId
,
361 // swap in a new set of IR maps for this function body:
362 let mut fn_maps
= IrMaps
::new(ir
.tcx
);
364 debug
!("creating fn_maps: {:?}", &fn_maps
as *const IrMaps
);
366 let body
= ir
.tcx
.hir
.body(body_id
);
368 for arg
in &body
.arguments
{
369 arg
.pat
.each_binding(|_bm
, arg_id
, _x
, path1
| {
370 debug
!("adding argument {}", arg_id
);
371 let name
= path1
.node
;
372 fn_maps
.add_variable(Arg(arg_id
, name
));
376 // gather up the various local variables, significant expressions,
378 intravisit
::walk_fn(&mut fn_maps
, fk
, decl
, body_id
, sp
, id
);
381 let mut lsets
= Liveness
::new(&mut fn_maps
, body_id
);
382 let entry_ln
= lsets
.compute(&body
.value
);
384 // check for various error conditions
385 lsets
.visit_body(body
);
386 lsets
.warn_about_unused_args(body
, entry_ln
);
389 fn visit_local
<'a
, 'tcx
>(ir
: &mut IrMaps
<'a
, 'tcx
>, local
: &'tcx hir
::Local
) {
390 local
.pat
.each_binding(|_
, p_id
, sp
, path1
| {
391 debug
!("adding local variable {}", p_id
);
392 let name
= path1
.node
;
393 ir
.add_live_node_for_node(p_id
, VarDefNode(sp
));
394 ir
.add_variable(Local(LocalInfo
{
400 intravisit
::walk_local(ir
, local
);
403 fn visit_arm
<'a
, 'tcx
>(ir
: &mut IrMaps
<'a
, 'tcx
>, arm
: &'tcx hir
::Arm
) {
404 for pat
in &arm
.pats
{
405 // for struct patterns, take note of which fields used shorthand (`x`
406 // rather than `x: x`)
408 // FIXME: according to the rust-lang-nursery/rustc-guide book and
409 // librustc/README.md, `NodeId`s are to be phased out in favor of
410 // `HirId`s; however, we need to match the signature of `each_binding`,
411 // which uses `NodeIds`.
412 let mut shorthand_field_ids
= NodeSet();
413 if let hir
::PatKind
::Struct(_
, ref fields
, _
) = pat
.node
{
414 for field
in fields
{
415 if field
.node
.is_shorthand
{
416 shorthand_field_ids
.insert(field
.node
.pat
.id
);
421 pat
.each_binding(|bm
, p_id
, sp
, path1
| {
422 debug
!("adding local variable {} from match with bm {:?}",
424 let name
= path1
.node
;
425 ir
.add_live_node_for_node(p_id
, VarDefNode(sp
));
426 ir
.add_variable(Local(LocalInfo
{
429 is_shorthand
: shorthand_field_ids
.contains(&p_id
)
433 intravisit
::walk_arm(ir
, arm
);
436 fn visit_expr
<'a
, 'tcx
>(ir
: &mut IrMaps
<'a
, 'tcx
>, expr
: &'tcx Expr
) {
438 // live nodes required for uses or definitions of variables:
439 hir
::ExprPath(hir
::QPath
::Resolved(_
, ref path
)) => {
440 debug
!("expr {}: path that leads to {:?}", expr
.id
, path
.def
);
441 if let Def
::Local(..) = path
.def
{
442 ir
.add_live_node_for_node(expr
.id
, ExprNode(expr
.span
));
444 intravisit
::walk_expr(ir
, expr
);
446 hir
::ExprClosure(..) => {
447 // Interesting control flow (for loops can contain labeled
448 // breaks or continues)
449 ir
.add_live_node_for_node(expr
.id
, ExprNode(expr
.span
));
451 // Make a live_node for each captured variable, with the span
452 // being the location that the variable is used. This results
453 // in better error messages than just pointing at the closure
454 // construction site.
455 let mut call_caps
= Vec
::new();
456 ir
.tcx
.with_freevars(expr
.id
, |freevars
| {
458 if let Def
::Local(rv
) = fv
.def
{
459 let fv_ln
= ir
.add_live_node(FreeVarNode(fv
.span
));
460 call_caps
.push(CaptureInfo
{ln
: fv_ln
,
465 ir
.set_captures(expr
.id
, call_caps
);
467 intravisit
::walk_expr(ir
, expr
);
470 // live nodes required for interesting control flow:
471 hir
::ExprIf(..) | hir
::ExprMatch(..) | hir
::ExprWhile(..) | hir
::ExprLoop(..) => {
472 ir
.add_live_node_for_node(expr
.id
, ExprNode(expr
.span
));
473 intravisit
::walk_expr(ir
, expr
);
475 hir
::ExprBinary(op
, ..) if op
.node
.is_lazy() => {
476 ir
.add_live_node_for_node(expr
.id
, ExprNode(expr
.span
));
477 intravisit
::walk_expr(ir
, expr
);
480 // otherwise, live nodes are not required:
481 hir
::ExprIndex(..) | hir
::ExprField(..) | hir
::ExprTupField(..) |
482 hir
::ExprArray(..) | hir
::ExprCall(..) | hir
::ExprMethodCall(..) |
483 hir
::ExprTup(..) | hir
::ExprBinary(..) | hir
::ExprAddrOf(..) |
484 hir
::ExprCast(..) | hir
::ExprUnary(..) | hir
::ExprBreak(..) |
485 hir
::ExprAgain(_
) | hir
::ExprLit(_
) | hir
::ExprRet(..) |
486 hir
::ExprBlock(..) | hir
::ExprAssign(..) | hir
::ExprAssignOp(..) |
487 hir
::ExprStruct(..) | hir
::ExprRepeat(..) |
488 hir
::ExprInlineAsm(..) | hir
::ExprBox(..) | hir
::ExprYield(..) |
489 hir
::ExprType(..) | hir
::ExprPath(hir
::QPath
::TypeRelative(..)) => {
490 intravisit
::walk_expr(ir
, expr
);
495 // ______________________________________________________________________
496 // Computing liveness sets
498 // Actually we compute just a bit more than just liveness, but we use
499 // the same basic propagation framework in all cases.
501 #[derive(Clone, Copy)]
508 fn invalid_users() -> Users
{
510 reader
: invalid_node(),
511 writer
: invalid_node(),
516 #[derive(Copy, Clone)]
519 fallthrough_ln
: LiveNode
,
520 clean_exit_var
: Variable
523 const ACC_READ
: u32 = 1;
524 const ACC_WRITE
: u32 = 2;
525 const ACC_USE
: u32 = 4;
527 struct Liveness
<'a
, 'tcx
: 'a
> {
528 ir
: &'a
mut IrMaps
<'a
, 'tcx
>,
529 tables
: &'a ty
::TypeckTables
<'tcx
>,
531 successors
: Vec
<LiveNode
>,
534 // mappings from loop node ID to LiveNode
535 // ("break" label should map to loop node ID,
536 // it probably doesn't now)
537 break_ln
: NodeMap
<LiveNode
>,
538 cont_ln
: NodeMap
<LiveNode
>,
540 // mappings from node ID to LiveNode for "breakable" blocks-- currently only `catch {...}`
541 breakable_block_ln
: NodeMap
<LiveNode
>,
544 impl<'a
, 'tcx
> Liveness
<'a
, 'tcx
> {
545 fn new(ir
: &'a
mut IrMaps
<'a
, 'tcx
>, body
: hir
::BodyId
) -> Liveness
<'a
, 'tcx
> {
546 // Special nodes and variables:
547 // - exit_ln represents the end of the fn, either by return or panic
548 // - implicit_ret_var is a pseudo-variable that represents
549 // an implicit return
550 let specials
= Specials
{
551 exit_ln
: ir
.add_live_node(ExitNode
),
552 fallthrough_ln
: ir
.add_live_node(ExitNode
),
553 clean_exit_var
: ir
.add_variable(CleanExit
)
556 let tables
= ir
.tcx
.body_tables(body
);
558 let num_live_nodes
= ir
.num_live_nodes
;
559 let num_vars
= ir
.num_vars
;
565 successors
: vec
![invalid_node(); num_live_nodes
],
566 users
: vec
![invalid_users(); num_live_nodes
* num_vars
],
569 breakable_block_ln
: NodeMap(),
573 fn live_node(&self, node_id
: NodeId
, span
: Span
) -> LiveNode
{
574 match self.ir
.live_node_map
.get(&node_id
) {
577 // This must be a mismatch between the ir_map construction
578 // above and the propagation code below; the two sets of
579 // code have to agree about which AST nodes are worth
580 // creating liveness nodes for.
583 "no live node registered for node {}",
589 fn variable(&self, node_id
: NodeId
, span
: Span
) -> Variable
{
590 self.ir
.variable(node_id
, span
)
593 fn pat_bindings
<F
>(&mut self, pat
: &hir
::Pat
, mut f
: F
) where
594 F
: FnMut(&mut Liveness
<'a
, 'tcx
>, LiveNode
, Variable
, Span
, NodeId
),
596 pat
.each_binding(|_bm
, p_id
, sp
, _n
| {
597 let ln
= self.live_node(p_id
, sp
);
598 let var
= self.variable(p_id
, sp
);
599 f(self, ln
, var
, sp
, p_id
);
603 fn arm_pats_bindings
<F
>(&mut self, pat
: Option
<&hir
::Pat
>, f
: F
) where
604 F
: FnMut(&mut Liveness
<'a
, 'tcx
>, LiveNode
, Variable
, Span
, NodeId
),
606 if let Some(pat
) = pat
{
607 self.pat_bindings(pat
, f
);
611 fn define_bindings_in_pat(&mut self, pat
: &hir
::Pat
, succ
: LiveNode
)
613 self.define_bindings_in_arm_pats(Some(pat
), succ
)
616 fn define_bindings_in_arm_pats(&mut self, pat
: Option
<&hir
::Pat
>, succ
: LiveNode
)
619 self.arm_pats_bindings(pat
, |this
, ln
, var
, _sp
, _id
| {
620 this
.init_from_succ(ln
, succ
);
621 this
.define(ln
, var
);
627 fn idx(&self, ln
: LiveNode
, var
: Variable
) -> usize {
628 ln
.get() * self.ir
.num_vars
+ var
.get()
631 fn live_on_entry(&self, ln
: LiveNode
, var
: Variable
)
632 -> Option
<LiveNodeKind
> {
633 assert
!(ln
.is_valid());
634 let reader
= self.users
[self.idx(ln
, var
)].reader
;
635 if reader
.is_valid() {Some(self.ir.lnk(reader))}
else {None}
639 Is this variable live on entry to any of its successor nodes?
641 fn live_on_exit(&self, ln
: LiveNode
, var
: Variable
)
642 -> Option
<LiveNodeKind
> {
643 let successor
= self.successors
[ln
.get()];
644 self.live_on_entry(successor
, var
)
647 fn used_on_entry(&self, ln
: LiveNode
, var
: Variable
) -> bool
{
648 assert
!(ln
.is_valid());
649 self.users
[self.idx(ln
, var
)].used
652 fn assigned_on_entry(&self, ln
: LiveNode
, var
: Variable
)
653 -> Option
<LiveNodeKind
> {
654 assert
!(ln
.is_valid());
655 let writer
= self.users
[self.idx(ln
, var
)].writer
;
656 if writer
.is_valid() {Some(self.ir.lnk(writer))}
else {None}
659 fn assigned_on_exit(&self, ln
: LiveNode
, var
: Variable
)
660 -> Option
<LiveNodeKind
> {
661 let successor
= self.successors
[ln
.get()];
662 self.assigned_on_entry(successor
, var
)
665 fn indices2
<F
>(&mut self, ln
: LiveNode
, succ_ln
: LiveNode
, mut op
: F
) where
666 F
: FnMut(&mut Liveness
<'a
, 'tcx
>, usize, usize),
668 let node_base_idx
= self.idx(ln
, Variable(0));
669 let succ_base_idx
= self.idx(succ_ln
, Variable(0));
670 for var_idx
in 0..self.ir
.num_vars
{
671 op(self, node_base_idx
+ var_idx
, succ_base_idx
+ var_idx
);
675 fn write_vars
<F
>(&self,
679 -> io
::Result
<()> where
680 F
: FnMut(usize) -> LiveNode
,
682 let node_base_idx
= self.idx(ln
, Variable(0));
683 for var_idx
in 0..self.ir
.num_vars
{
684 let idx
= node_base_idx
+ var_idx
;
685 if test(idx
).is_valid() {
686 write
!(wr
, " {:?}", Variable(var_idx
))?
;
693 #[allow(unused_must_use)]
694 fn ln_str(&self, ln
: LiveNode
) -> String
{
695 let mut wr
= Vec
::new();
697 let wr
= &mut wr
as &mut Write
;
698 write
!(wr
, "[ln({:?}) of kind {:?} reads", ln
.get(), self.ir
.lnk(ln
));
699 self.write_vars(wr
, ln
, |idx
| self.users
[idx
].reader
);
700 write
!(wr
, " writes");
701 self.write_vars(wr
, ln
, |idx
| self.users
[idx
].writer
);
702 write
!(wr
, " precedes {:?}]", self.successors
[ln
.get()]);
704 String
::from_utf8(wr
).unwrap()
707 fn init_empty(&mut self, ln
: LiveNode
, succ_ln
: LiveNode
) {
708 self.successors
[ln
.get()] = succ_ln
;
710 // It is not necessary to initialize the
711 // values to empty because this is the value
712 // they have when they are created, and the sets
713 // only grow during iterations.
715 // self.indices(ln) { |idx|
716 // self.users[idx] = invalid_users();
720 fn init_from_succ(&mut self, ln
: LiveNode
, succ_ln
: LiveNode
) {
721 // more efficient version of init_empty() / merge_from_succ()
722 self.successors
[ln
.get()] = succ_ln
;
724 self.indices2(ln
, succ_ln
, |this
, idx
, succ_idx
| {
725 this
.users
[idx
] = this
.users
[succ_idx
]
727 debug
!("init_from_succ(ln={}, succ={})",
728 self.ln_str(ln
), self.ln_str(succ_ln
));
731 fn merge_from_succ(&mut self,
736 if ln
== succ_ln { return false; }
738 let mut changed
= false;
739 self.indices2(ln
, succ_ln
, |this
, idx
, succ_idx
| {
740 changed
|= copy_if_invalid(this
.users
[succ_idx
].reader
,
741 &mut this
.users
[idx
].reader
);
742 changed
|= copy_if_invalid(this
.users
[succ_idx
].writer
,
743 &mut this
.users
[idx
].writer
);
744 if this
.users
[succ_idx
].used
&& !this
.users
[idx
].used
{
745 this
.users
[idx
].used
= true;
750 debug
!("merge_from_succ(ln={:?}, succ={}, first_merge={}, changed={})",
751 ln
, self.ln_str(succ_ln
), first_merge
, changed
);
754 fn copy_if_invalid(src
: LiveNode
, dst
: &mut LiveNode
) -> bool
{
755 if src
.is_valid() && !dst
.is_valid() {
764 // Indicates that a local variable was *defined*; we know that no
765 // uses of the variable can precede the definition (resolve checks
766 // this) so we just clear out all the data.
767 fn define(&mut self, writer
: LiveNode
, var
: Variable
) {
768 let idx
= self.idx(writer
, var
);
769 self.users
[idx
].reader
= invalid_node();
770 self.users
[idx
].writer
= invalid_node();
772 debug
!("{:?} defines {:?} (idx={}): {}", writer
, var
,
773 idx
, self.ln_str(writer
));
776 // Either read, write, or both depending on the acc bitset
777 fn acc(&mut self, ln
: LiveNode
, var
: Variable
, acc
: u32) {
778 debug
!("{:?} accesses[{:x}] {:?}: {}",
779 ln
, acc
, var
, self.ln_str(ln
));
781 let idx
= self.idx(ln
, var
);
782 let user
= &mut self.users
[idx
];
784 if (acc
& ACC_WRITE
) != 0 {
785 user
.reader
= invalid_node();
789 // Important: if we both read/write, must do read second
790 // or else the write will override.
791 if (acc
& ACC_READ
) != 0 {
795 if (acc
& ACC_USE
) != 0 {
800 // _______________________________________________________________________
802 fn compute(&mut self, body
: &hir
::Expr
) -> LiveNode
{
803 // if there is a `break` or `again` at the top level, then it's
804 // effectively a return---this only occurs in `for` loops,
805 // where the body is really a closure.
807 debug
!("compute: using id for body, {}", self.ir
.tcx
.hir
.node_to_pretty_string(body
.id
));
809 let exit_ln
= self.s
.exit_ln
;
811 self.break_ln
.insert(body
.id
, exit_ln
);
812 self.cont_ln
.insert(body
.id
, exit_ln
);
814 // the fallthrough exit is only for those cases where we do not
815 // explicitly return:
817 self.init_from_succ(s
.fallthrough_ln
, s
.exit_ln
);
818 self.acc(s
.fallthrough_ln
, s
.clean_exit_var
, ACC_READ
);
820 let entry_ln
= self.propagate_through_expr(body
, s
.fallthrough_ln
);
822 // hack to skip the loop unless debug! is enabled:
823 debug
!("^^ liveness computation results for body {} (entry={:?})",
825 for ln_idx
in 0..self.ir
.num_live_nodes
{
826 debug
!("{:?}", self.ln_str(LiveNode(ln_idx
)));
835 fn propagate_through_block(&mut self, blk
: &hir
::Block
, succ
: LiveNode
)
837 if blk
.targeted_by_break
{
838 self.breakable_block_ln
.insert(blk
.id
, succ
);
840 let succ
= self.propagate_through_opt_expr(blk
.expr
.as_ref().map(|e
| &**e
), succ
);
841 blk
.stmts
.iter().rev().fold(succ
, |succ
, stmt
| {
842 self.propagate_through_stmt(stmt
, succ
)
846 fn propagate_through_stmt(&mut self, stmt
: &hir
::Stmt
, succ
: LiveNode
)
849 hir
::StmtDecl(ref decl
, _
) => {
850 self.propagate_through_decl(&decl
, succ
)
853 hir
::StmtExpr(ref expr
, _
) | hir
::StmtSemi(ref expr
, _
) => {
854 self.propagate_through_expr(&expr
, succ
)
859 fn propagate_through_decl(&mut self, decl
: &hir
::Decl
, succ
: LiveNode
)
862 hir
::DeclLocal(ref local
) => {
863 self.propagate_through_local(&local
, succ
)
865 hir
::DeclItem(_
) => succ
,
869 fn propagate_through_local(&mut self, local
: &hir
::Local
, succ
: LiveNode
)
871 // Note: we mark the variable as defined regardless of whether
872 // there is an initializer. Initially I had thought to only mark
873 // the live variable as defined if it was initialized, and then we
874 // could check for uninit variables just by scanning what is live
875 // at the start of the function. But that doesn't work so well for
876 // immutable variables defined in a loop:
877 // loop { let x; x = 5; }
878 // because the "assignment" loops back around and generates an error.
880 // So now we just check that variables defined w/o an
881 // initializer are not live at the point of their
882 // initialization, which is mildly more complex than checking
883 // once at the func header but otherwise equivalent.
885 let succ
= self.propagate_through_opt_expr(local
.init
.as_ref().map(|e
| &**e
), succ
);
886 self.define_bindings_in_pat(&local
.pat
, succ
)
889 fn propagate_through_exprs(&mut self, exprs
: &[Expr
], succ
: LiveNode
)
891 exprs
.iter().rev().fold(succ
, |succ
, expr
| {
892 self.propagate_through_expr(&expr
, succ
)
896 fn propagate_through_opt_expr(&mut self,
897 opt_expr
: Option
<&Expr
>,
900 opt_expr
.map_or(succ
, |expr
| self.propagate_through_expr(expr
, succ
))
903 fn propagate_through_expr(&mut self, expr
: &Expr
, succ
: LiveNode
)
905 debug
!("propagate_through_expr: {}", self.ir
.tcx
.hir
.node_to_pretty_string(expr
.id
));
908 // Interesting cases with control flow or which gen/kill
909 hir
::ExprPath(hir
::QPath
::Resolved(_
, ref path
)) => {
910 self.access_path(expr
.id
, path
, succ
, ACC_READ
| ACC_USE
)
913 hir
::ExprField(ref e
, _
) => {
914 self.propagate_through_expr(&e
, succ
)
917 hir
::ExprTupField(ref e
, _
) => {
918 self.propagate_through_expr(&e
, succ
)
921 hir
::ExprClosure(.., blk_id
, _
, _
) => {
922 debug
!("{} is an ExprClosure", self.ir
.tcx
.hir
.node_to_pretty_string(expr
.id
));
925 The next-node for a break is the successor of the entire
926 loop. The next-node for a continue is the top of this loop.
928 let node
= self.live_node(expr
.id
, expr
.span
);
932 self.break_ln
.insert(blk_id
.node_id
, break_ln
);
933 self.cont_ln
.insert(blk_id
.node_id
, cont_ln
);
935 // the construction of a closure itself is not important,
936 // but we have to consider the closed over variables.
937 let caps
= match self.ir
.capture_info_map
.get(&expr
.id
) {
938 Some(caps
) => caps
.clone(),
940 span_bug
!(expr
.span
, "no registered caps");
943 caps
.iter().rev().fold(succ
, |succ
, cap
| {
944 self.init_from_succ(cap
.ln
, succ
);
945 let var
= self.variable(cap
.var_nid
, expr
.span
);
946 self.acc(cap
.ln
, var
, ACC_READ
| ACC_USE
);
951 hir
::ExprIf(ref cond
, ref then
, ref els
) => {
965 let else_ln
= self.propagate_through_opt_expr(els
.as_ref().map(|e
| &**e
), succ
);
966 let then_ln
= self.propagate_through_expr(&then
, succ
);
967 let ln
= self.live_node(expr
.id
, expr
.span
);
968 self.init_from_succ(ln
, else_ln
);
969 self.merge_from_succ(ln
, then_ln
, false);
970 self.propagate_through_expr(&cond
, ln
)
973 hir
::ExprWhile(ref cond
, ref blk
, _
) => {
974 self.propagate_through_loop(expr
, WhileLoop(&cond
), &blk
, succ
)
977 // Note that labels have been resolved, so we don't need to look
978 // at the label ident
979 hir
::ExprLoop(ref blk
, _
, _
) => {
980 self.propagate_through_loop(expr
, LoopLoop
, &blk
, succ
)
983 hir
::ExprMatch(ref e
, ref arms
, _
) => {
998 let ln
= self.live_node(expr
.id
, expr
.span
);
999 self.init_empty(ln
, succ
);
1000 let mut first_merge
= true;
1003 self.propagate_through_expr(&arm
.body
, succ
);
1005 self.propagate_through_opt_expr(arm
.guard
.as_ref().map(|e
| &**e
), body_succ
);
1006 // only consider the first pattern; any later patterns must have
1007 // the same bindings, and we also consider the first pattern to be
1008 // the "authoritative" set of ids
1010 self.define_bindings_in_arm_pats(arm
.pats
.first().map(|p
| &**p
),
1012 self.merge_from_succ(ln
, arm_succ
, first_merge
);
1013 first_merge
= false;
1015 self.propagate_through_expr(&e
, ln
)
1018 hir
::ExprRet(ref o_e
) => {
1019 // ignore succ and subst exit_ln:
1020 let exit_ln
= self.s
.exit_ln
;
1021 self.propagate_through_opt_expr(o_e
.as_ref().map(|e
| &**e
), exit_ln
)
1024 hir
::ExprBreak(label
, ref opt_expr
) => {
1025 // Find which label this break jumps to
1026 let target
= match label
.target_id
{
1027 hir
::ScopeTarget
::Block(node_id
) =>
1028 self.breakable_block_ln
.get(&node_id
),
1029 hir
::ScopeTarget
::Loop(hir
::LoopIdResult
::Ok(node_id
)) =>
1030 self.break_ln
.get(&node_id
),
1031 hir
::ScopeTarget
::Loop(hir
::LoopIdResult
::Err(err
)) =>
1032 span_bug
!(expr
.span
, "loop scope error: {}", err
),
1035 // Now that we know the label we're going to,
1036 // look it up in the break loop nodes table
1039 Some(b
) => self.propagate_through_opt_expr(opt_expr
.as_ref().map(|e
| &**e
), b
),
1040 None
=> span_bug
!(expr
.span
, "break to unknown label")
1044 hir
::ExprAgain(label
) => {
1045 // Find which label this expr continues to
1046 let sc
= match label
.target_id
{
1047 hir
::ScopeTarget
::Block(_
) => bug
!("can't `continue` to a non-loop block"),
1048 hir
::ScopeTarget
::Loop(hir
::LoopIdResult
::Ok(node_id
)) => node_id
,
1049 hir
::ScopeTarget
::Loop(hir
::LoopIdResult
::Err(err
)) =>
1050 span_bug
!(expr
.span
, "loop scope error: {}", err
),
1053 // Now that we know the label we're going to,
1054 // look it up in the continue loop nodes table
1056 match self.cont_ln
.get(&sc
) {
1058 None
=> span_bug
!(expr
.span
, "continue to unknown label")
1062 hir
::ExprAssign(ref l
, ref r
) => {
1063 // see comment on places in
1064 // propagate_through_place_components()
1065 let succ
= self.write_place(&l
, succ
, ACC_WRITE
);
1066 let succ
= self.propagate_through_place_components(&l
, succ
);
1067 self.propagate_through_expr(&r
, succ
)
1070 hir
::ExprAssignOp(_
, ref l
, ref r
) => {
1071 // an overloaded assign op is like a method call
1072 if self.tables
.is_method_call(expr
) {
1073 let succ
= self.propagate_through_expr(&l
, succ
);
1074 self.propagate_through_expr(&r
, succ
)
1076 // see comment on places in
1077 // propagate_through_place_components()
1078 let succ
= self.write_place(&l
, succ
, ACC_WRITE
|ACC_READ
);
1079 let succ
= self.propagate_through_expr(&r
, succ
);
1080 self.propagate_through_place_components(&l
, succ
)
1084 // Uninteresting cases: just propagate in rev exec order
1086 hir
::ExprArray(ref exprs
) => {
1087 self.propagate_through_exprs(exprs
, succ
)
1090 hir
::ExprStruct(_
, ref fields
, ref with_expr
) => {
1091 let succ
= self.propagate_through_opt_expr(with_expr
.as_ref().map(|e
| &**e
), succ
);
1092 fields
.iter().rev().fold(succ
, |succ
, field
| {
1093 self.propagate_through_expr(&field
.expr
, succ
)
1097 hir
::ExprCall(ref f
, ref args
) => {
1098 // FIXME(canndrew): This is_never should really be an is_uninhabited
1099 let succ
= if self.tables
.expr_ty(expr
).is_never() {
1104 let succ
= self.propagate_through_exprs(args
, succ
);
1105 self.propagate_through_expr(&f
, succ
)
1108 hir
::ExprMethodCall(.., ref args
) => {
1109 // FIXME(canndrew): This is_never should really be an is_uninhabited
1110 let succ
= if self.tables
.expr_ty(expr
).is_never() {
1115 self.propagate_through_exprs(args
, succ
)
1118 hir
::ExprTup(ref exprs
) => {
1119 self.propagate_through_exprs(exprs
, succ
)
1122 hir
::ExprBinary(op
, ref l
, ref r
) if op
.node
.is_lazy() => {
1123 let r_succ
= self.propagate_through_expr(&r
, succ
);
1125 let ln
= self.live_node(expr
.id
, expr
.span
);
1126 self.init_from_succ(ln
, succ
);
1127 self.merge_from_succ(ln
, r_succ
, false);
1129 self.propagate_through_expr(&l
, ln
)
1132 hir
::ExprIndex(ref l
, ref r
) |
1133 hir
::ExprBinary(_
, ref l
, ref r
) => {
1134 let r_succ
= self.propagate_through_expr(&r
, succ
);
1135 self.propagate_through_expr(&l
, r_succ
)
1138 hir
::ExprBox(ref e
) |
1139 hir
::ExprAddrOf(_
, ref e
) |
1140 hir
::ExprCast(ref e
, _
) |
1141 hir
::ExprType(ref e
, _
) |
1142 hir
::ExprUnary(_
, ref e
) |
1143 hir
::ExprYield(ref e
) |
1144 hir
::ExprRepeat(ref e
, _
) => {
1145 self.propagate_through_expr(&e
, succ
)
1148 hir
::ExprInlineAsm(ref ia
, ref outputs
, ref inputs
) => {
1149 let succ
= ia
.outputs
.iter().zip(outputs
).rev().fold(succ
, |succ
, (o
, output
)| {
1150 // see comment on places
1151 // in propagate_through_place_components()
1153 self.propagate_through_expr(output
, succ
)
1155 let acc
= if o
.is_rw { ACC_WRITE|ACC_READ }
else { ACC_WRITE }
;
1156 let succ
= self.write_place(output
, succ
, acc
);
1157 self.propagate_through_place_components(output
, succ
)
1161 // Inputs are executed first. Propagate last because of rev order
1162 self.propagate_through_exprs(inputs
, succ
)
1165 hir
::ExprLit(..) | hir
::ExprPath(hir
::QPath
::TypeRelative(..)) => {
1169 hir
::ExprBlock(ref blk
) => {
1170 self.propagate_through_block(&blk
, succ
)
1175 fn propagate_through_place_components(&mut self,
1181 // In general, the full flow graph structure for an
1182 // assignment/move/etc can be handled in one of two ways,
1183 // depending on whether what is being assigned is a "tracked
1184 // value" or not. A tracked value is basically a local
1185 // variable or argument.
1187 // The two kinds of graphs are:
1189 // Tracked place Untracked place
1190 // ----------------------++-----------------------
1194 // (rvalue) || (rvalue)
1197 // (write of place) || (place components)
1202 // ----------------------++-----------------------
1204 // I will cover the two cases in turn:
1208 // A tracked place is a local variable/argument `x`. In
1209 // these cases, the link_node where the write occurs is linked
1210 // to node id of `x`. The `write_place()` routine generates
1211 // the contents of this node. There are no subcomponents to
1214 // # Non-tracked places
1216 // These are places like `x[5]` or `x.f`. In that case, we
1217 // basically ignore the value which is written to but generate
1218 // reads for the components---`x` in these two examples. The
1219 // components reads are generated by
1220 // `propagate_through_place_components()` (this fn).
1224 // It is still possible to observe assignments to non-places;
1225 // these errors are detected in the later pass borrowck. We
1226 // just ignore such cases and treat them as reads.
1229 hir
::ExprPath(_
) => succ
,
1230 hir
::ExprField(ref e
, _
) => self.propagate_through_expr(&e
, succ
),
1231 hir
::ExprTupField(ref e
, _
) => self.propagate_through_expr(&e
, succ
),
1232 _
=> self.propagate_through_expr(expr
, succ
)
1236 // see comment on propagate_through_place()
1237 fn write_place(&mut self, expr
: &Expr
, succ
: LiveNode
, acc
: u32)
1240 hir
::ExprPath(hir
::QPath
::Resolved(_
, ref path
)) => {
1241 self.access_path(expr
.id
, path
, succ
, acc
)
1244 // We do not track other places, so just propagate through
1245 // to their subcomponents. Also, it may happen that
1246 // non-places occur here, because those are detected in the
1247 // later pass borrowck.
1252 fn access_var(&mut self, id
: NodeId
, nid
: NodeId
, succ
: LiveNode
, acc
: u32, span
: Span
)
1254 let ln
= self.live_node(id
, span
);
1256 self.init_from_succ(ln
, succ
);
1257 let var
= self.variable(nid
, span
);
1258 self.acc(ln
, var
, acc
);
1263 fn access_path(&mut self, id
: NodeId
, path
: &hir
::Path
, succ
: LiveNode
, acc
: u32)
1266 Def
::Local(nid
) => {
1267 self.access_var(id
, nid
, succ
, acc
, path
.span
)
1273 fn propagate_through_loop(&mut self,
1282 We model control flow like this:
1300 let mut first_merge
= true;
1301 let ln
= self.live_node(expr
.id
, expr
.span
);
1302 self.init_empty(ln
, succ
);
1306 // If this is not a `loop` loop, then it's possible we bypass
1307 // the body altogether. Otherwise, the only way is via a `break`
1308 // in the loop body.
1309 self.merge_from_succ(ln
, succ
, first_merge
);
1310 first_merge
= false;
1313 debug
!("propagate_through_loop: using id for loop body {} {}",
1314 expr
.id
, self.ir
.tcx
.hir
.node_to_pretty_string(body
.id
));
1316 let break_ln
= succ
;
1318 self.break_ln
.insert(expr
.id
, break_ln
);
1319 self.cont_ln
.insert(expr
.id
, cont_ln
);
1321 let cond_ln
= match kind
{
1323 WhileLoop(ref cond
) => self.propagate_through_expr(&cond
, ln
),
1325 let body_ln
= self.propagate_through_block(body
, cond_ln
);
1327 // repeat until fixed point is reached:
1328 while self.merge_from_succ(ln
, body_ln
, first_merge
) {
1329 first_merge
= false;
1331 let new_cond_ln
= match kind
{
1333 WhileLoop(ref cond
) => {
1334 self.propagate_through_expr(&cond
, ln
)
1337 assert
!(cond_ln
== new_cond_ln
);
1338 assert
!(body_ln
== self.propagate_through_block(body
, cond_ln
));
1345 // _______________________________________________________________________
1346 // Checking for error conditions
1348 impl<'a
, 'tcx
> Visitor
<'tcx
> for Liveness
<'a
, 'tcx
> {
1349 fn nested_visit_map
<'this
>(&'this
mut self) -> NestedVisitorMap
<'this
, 'tcx
> {
1350 NestedVisitorMap
::None
1353 fn visit_local(&mut self, l
: &'tcx hir
::Local
) {
1354 check_local(self, l
);
1356 fn visit_expr(&mut self, ex
: &'tcx Expr
) {
1357 check_expr(self, ex
);
1359 fn visit_arm(&mut self, a
: &'tcx hir
::Arm
) {
1364 fn check_local
<'a
, 'tcx
>(this
: &mut Liveness
<'a
, 'tcx
>, local
: &'tcx hir
::Local
) {
1367 this
.warn_about_unused_or_dead_vars_in_pat(&local
.pat
);
1370 this
.pat_bindings(&local
.pat
, |this
, ln
, var
, sp
, id
| {
1371 this
.warn_about_unused(sp
, id
, ln
, var
);
1376 intravisit
::walk_local(this
, local
);
1379 fn check_arm
<'a
, 'tcx
>(this
: &mut Liveness
<'a
, 'tcx
>, arm
: &'tcx hir
::Arm
) {
1380 // only consider the first pattern; any later patterns must have
1381 // the same bindings, and we also consider the first pattern to be
1382 // the "authoritative" set of ids
1383 this
.arm_pats_bindings(arm
.pats
.first().map(|p
| &**p
), |this
, ln
, var
, sp
, id
| {
1384 this
.warn_about_unused(sp
, id
, ln
, var
);
1386 intravisit
::walk_arm(this
, arm
);
1389 fn check_expr
<'a
, 'tcx
>(this
: &mut Liveness
<'a
, 'tcx
>, expr
: &'tcx Expr
) {
1391 hir
::ExprAssign(ref l
, _
) => {
1392 this
.check_place(&l
);
1394 intravisit
::walk_expr(this
, expr
);
1397 hir
::ExprAssignOp(_
, ref l
, _
) => {
1398 if !this
.tables
.is_method_call(expr
) {
1399 this
.check_place(&l
);
1402 intravisit
::walk_expr(this
, expr
);
1405 hir
::ExprInlineAsm(ref ia
, ref outputs
, ref inputs
) => {
1406 for input
in inputs
{
1407 this
.visit_expr(input
);
1410 // Output operands must be places
1411 for (o
, output
) in ia
.outputs
.iter().zip(outputs
) {
1413 this
.check_place(output
);
1415 this
.visit_expr(output
);
1418 intravisit
::walk_expr(this
, expr
);
1421 // no correctness conditions related to liveness
1422 hir
::ExprCall(..) | hir
::ExprMethodCall(..) | hir
::ExprIf(..) |
1423 hir
::ExprMatch(..) | hir
::ExprWhile(..) | hir
::ExprLoop(..) |
1424 hir
::ExprIndex(..) | hir
::ExprField(..) | hir
::ExprTupField(..) |
1425 hir
::ExprArray(..) | hir
::ExprTup(..) | hir
::ExprBinary(..) |
1426 hir
::ExprCast(..) | hir
::ExprUnary(..) | hir
::ExprRet(..) |
1427 hir
::ExprBreak(..) | hir
::ExprAgain(..) | hir
::ExprLit(_
) |
1428 hir
::ExprBlock(..) | hir
::ExprAddrOf(..) |
1429 hir
::ExprStruct(..) | hir
::ExprRepeat(..) |
1430 hir
::ExprClosure(..) | hir
::ExprPath(_
) | hir
::ExprYield(..) |
1431 hir
::ExprBox(..) | hir
::ExprType(..) => {
1432 intravisit
::walk_expr(this
, expr
);
1437 impl<'a
, 'tcx
> Liveness
<'a
, 'tcx
> {
1438 fn check_place(&mut self, expr
: &'tcx Expr
) {
1440 hir
::ExprPath(hir
::QPath
::Resolved(_
, ref path
)) => {
1441 if let Def
::Local(nid
) = path
.def
{
1442 // Assignment to an immutable variable or argument: only legal
1443 // if there is no later assignment. If this local is actually
1444 // mutable, then check for a reassignment to flag the mutability
1446 let ln
= self.live_node(expr
.id
, expr
.span
);
1447 let var
= self.variable(nid
, expr
.span
);
1448 self.warn_about_dead_assign(expr
.span
, expr
.id
, ln
, var
);
1452 // For other kinds of places, no checks are required,
1453 // and any embedded expressions are actually rvalues
1454 intravisit
::walk_expr(self, expr
);
1459 fn should_warn(&self, var
: Variable
) -> Option
<String
> {
1460 let name
= self.ir
.variable_name(var
);
1461 if name
.is_empty() || name
.as_bytes()[0] == ('_'
as u8) {
1468 fn warn_about_unused_args(&self, body
: &hir
::Body
, entry_ln
: LiveNode
) {
1469 for arg
in &body
.arguments
{
1470 arg
.pat
.each_binding(|_bm
, p_id
, sp
, path1
| {
1471 let var
= self.variable(p_id
, sp
);
1472 // Ignore unused self.
1473 let name
= path1
.node
;
1474 if name
!= keywords
::SelfValue
.name() {
1475 if !self.warn_about_unused(sp
, p_id
, entry_ln
, var
) {
1476 if self.live_on_entry(entry_ln
, var
).is_none() {
1477 self.report_dead_assign(p_id
, sp
, var
, true);
1485 fn warn_about_unused_or_dead_vars_in_pat(&mut self, pat
: &hir
::Pat
) {
1486 self.pat_bindings(pat
, |this
, ln
, var
, sp
, id
| {
1487 if !this
.warn_about_unused(sp
, id
, ln
, var
) {
1488 this
.warn_about_dead_assign(sp
, id
, ln
, var
);
1493 fn warn_about_unused(&self,
1499 if !self.used_on_entry(ln
, var
) {
1500 let r
= self.should_warn(var
);
1501 if let Some(name
) = r
{
1503 // annoying: for parameters in funcs like `fn(x: i32)
1504 // {ret}`, there is only one node, so asking about
1505 // assigned_on_exit() is not meaningful.
1506 let is_assigned
= if ln
== self.s
.exit_ln
{
1509 self.assigned_on_exit(ln
, var
).is_some()
1512 let suggest_underscore_msg
= format
!("consider using `_{}` instead",
1516 .lint_node_note(lint
::builtin
::UNUSED_VARIABLES
, id
, sp
,
1517 &format
!("variable `{}` is assigned to, but never used",
1519 &suggest_underscore_msg
);
1520 } else if name
!= "self" {
1521 let msg
= format
!("unused variable: `{}`", name
);
1522 let mut err
= self.ir
.tcx
1523 .struct_span_lint_node(lint
::builtin
::UNUSED_VARIABLES
, id
, sp
, &msg
);
1524 if self.ir
.variable_is_shorthand(var
) {
1525 err
.span_suggestion(sp
, "try ignoring the field",
1526 format
!("{}: _", name
));
1528 err
.span_suggestion_short(sp
, &suggest_underscore_msg
,
1529 format
!("_{}", name
));
1540 fn warn_about_dead_assign(&self,
1545 if self.live_on_exit(ln
, var
).is_none() {
1546 self.report_dead_assign(id
, sp
, var
, false);
1550 fn report_dead_assign(&self, id
: NodeId
, sp
: Span
, var
: Variable
, is_argument
: bool
) {
1551 if let Some(name
) = self.should_warn(var
) {
1553 self.ir
.tcx
.lint_node(lint
::builtin
::UNUSED_ASSIGNMENTS
, id
, sp
,
1554 &format
!("value passed to `{}` is never read", name
));
1556 self.ir
.tcx
.lint_node(lint
::builtin
::UNUSED_ASSIGNMENTS
, id
, sp
,
1557 &format
!("value assigned to `{}` is never read", name
));