forked from swc-project/swc
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdead_code.rs
More file actions
131 lines (120 loc) · 4.37 KB
/
dead_code.rs
File metadata and controls
131 lines (120 loc) · 4.37 KB
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
use swc_common::util::take::Take;
use swc_ecma_ast::*;
use super::{BitCtx, Optimizer};
use crate::program_data::{ScopeData, VarUsageInfoFlags};
/// Methods related to option `dead_code`.
impl Optimizer<'_> {
/// Optimize return value or argument of throw.
///
/// This methods removes some useless assignments.
///
/// # Example
///
/// Note: `a` being declared in the function is important in the example
/// below.
///
/// ```ts
/// function foo(){
/// var a;
/// throw a = x();
/// }
/// ```
///
/// can be optimized as
///
/// ```ts
/// function foo(){
/// var a; // Will be dropped in next pass.
/// throw x();
/// }
/// ```
/// # Returns
///
/// returns true if `e` is changed.
pub(super) fn optimize_last_expr_before_termination(&mut self, e: &mut Expr) -> bool {
if !self.options.dead_code {
return false;
}
// A return statement in a try block may not terminate function.
if self.ctx.bit_ctx.contains(BitCtx::InTryBlock) {
return false;
}
if let Expr::Assign(assign @ AssignExpr { op: op!("="), .. }) = e {
self.optimize_last_expr_before_termination(&mut assign.right);
// We only handle identifiers on lhs for now.
if let Some(lhs) = assign.left.as_ident() {
let used_arguments = self
.data
.scopes
.get(&self.ctx.scope)
.map(|s| s.contains(ScopeData::USED_ARGUMENTS))
.unwrap_or(false);
if self
.data
.vars
.get(&lhs.to_id())
.map(|var| {
var.flags.contains(
VarUsageInfoFlags::DECLARED.union(VarUsageInfoFlags::IS_FN_LOCAL),
) && !(used_arguments
&& var.flags.contains(VarUsageInfoFlags::DECLARED_AS_FN_PARAM))
&& !var.flags.intersects(VarUsageInfoFlags::EXPORTED)
})
.unwrap_or(false)
{
report_change!(
"dead_code: Dropping an assignment to a variable declared in function \
because function is being terminated"
);
self.changed = true;
*e = *assign.right.take();
return true;
}
}
}
if let Expr::Assign(assign) = e {
// x += 1 => x + 1
if let Some(op) = assign.op.to_update() {
if op == op!("**") {
return false;
}
if let Some(lhs) = assign.left.as_ident() {
let used_arguments = self
.data
.scopes
.get(&self.ctx.scope)
.map(|s| s.contains(ScopeData::USED_ARGUMENTS))
.unwrap_or(false);
if self
.data
.vars
.get(&lhs.to_id())
.map(|var| {
var.flags.contains(
VarUsageInfoFlags::DECLARED.union(VarUsageInfoFlags::IS_FN_LOCAL),
) && !(used_arguments
&& var.flags.contains(VarUsageInfoFlags::DECLARED_AS_FN_PARAM))
&& !var.flags.contains(VarUsageInfoFlags::EXPORTED)
})
.unwrap_or(false)
{
report_change!(
"dead_code: Converting an assignment into a binary expression in \
function termination"
);
self.changed = true;
*e = BinExpr {
span: assign.span,
op,
left: lhs.clone().into(),
right: assign.right.take(),
}
.into();
return true;
}
}
}
}
false
}
}