1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
//! This module provides support for removing the extraneous break statements
//! generated by the incremental relooper.
use super::*;
use syn::{Expr, ExprBlock, ExprBreak, ExprIf, ExprLit, ExprMatch, ExprReturn, Lit};
pub struct IncCleanup {
in_tail: Option<ImplicitReturnType>,
brk_lbl: Label,
}
impl IncCleanup {
pub fn new(in_tail: Option<ImplicitReturnType>, brk_lbl: Label) -> Self {
IncCleanup { in_tail, brk_lbl }
}
/// The only way we can say for sure that we don't need a labelled block is if we remove
/// the (unique) break to that label. We know that the label will be unique because relooper
/// never duplicates blocks.
///
/// Returns true if we manage to remove a tail expr.
pub fn remove_tail_expr(&self, stmts: &mut Vec<Stmt>) -> bool {
let mut stmt = if let Some(stmt) = stmts.pop() {
stmt
} else {
return false;
};
// If the very last stmt in our relooped output is a return/break, we can just
// remove that statement. We additionally know that there is definitely no need
// to label a block (if we were in that mode in the first place).
if self.is_idempotent_tail_expr(&stmt) {
return true;
}
let mut removed_tail_expr = false;
if let Stmt::Expr(expr, None) = &mut stmt {
match expr {
Expr::If(ExprIf {
cond: _,
then_branch,
else_branch,
..
}) => {
removed_tail_expr =
removed_tail_expr || self.remove_tail_expr(&mut then_branch.stmts);
if let Some((_token, else_)) = else_branch {
if let Expr::Block(ExprBlock { block, .. }) = &mut **else_ {
removed_tail_expr =
removed_tail_expr || self.remove_tail_expr(&mut block.stmts)
}
}
}
Expr::Match(ExprMatch { arms, .. }) => {
// Block label can be removed from any arm
for arm in arms {
if let Expr::Block(ExprBlock { block, .. }) = arm.body.as_mut() {
removed_tail_expr =
removed_tail_expr || self.remove_tail_expr(&mut block.stmts);
}
}
}
_ => {}
}
}
stmt = cleanup_if(stmt);
// In all other cases, we give up and accept that we can't get rid of the last
// stmt and that we might need a block label.
stmts.push(stmt);
removed_tail_expr
}
fn is_idempotent_tail_expr(&self, stmt: &Stmt) -> bool {
let tail_expr = if let Stmt::Expr(expr, Some(_token)) = stmt {
expr
} else {
return false;
};
match self.in_tail {
Some(ImplicitReturnType::Main) => {
if let Expr::Return(ExprReturn {
expr: Some(zero), ..
}) = tail_expr
{
if let Expr::Lit(ExprLit {
lit: Lit::Int(lit), ..
}) = &**zero
{
if lit.base10_digits() == "0" {
return true;
}
}
}
false
}
Some(ImplicitReturnType::Void) => {
if let Expr::Return(ExprReturn { expr: None, .. }) = tail_expr {
return true;
}
false
}
_ => {
if let Expr::Break(ExprBreak {
label: Some(brk_lbl),
expr: None,
..
}) = tail_expr
{
if brk_lbl.ident == mk().label(self.brk_lbl.pretty_print()).name.ident {
return true;
}
}
false
}
}
}
}
/// Remove empty else clauses from if expressions that can arise from
/// removing idempotent statements.
fn cleanup_if(stmt: Stmt) -> Stmt {
if let Stmt::Expr(
Expr::If(ExprIf {
cond,
then_branch,
else_branch: Some((token, else_)),
..
}),
None,
) = &stmt
{
if let Expr::Block(ExprBlock {
block, label: None, ..
}) = &**else_
{
if block.stmts.is_empty() {
return Stmt::Expr(
Expr::If(ExprIf {
cond: cond.clone(),
then_branch: then_branch.clone(),
else_branch: Default::default(),
attrs: Default::default(),
if_token: Default::default(),
}),
None,
);
} else if block.stmts.len() == 1 {
// flatten nested if expression to else if chain
if let Stmt::Expr(Expr::If(nested_if_expr), None) = &block.stmts[0] {
return Stmt::Expr(
Expr::If(ExprIf {
cond: cond.clone(),
then_branch: then_branch.clone(),
else_branch: Some((*token, Box::new(Expr::If(nested_if_expr.clone())))),
attrs: Default::default(),
if_token: Default::default(),
}),
None,
);
}
}
}
}
stmt
}