-
Notifications
You must be signed in to change notification settings - Fork 45
Expand file tree
/
Copy pathswc_helpers.rs
More file actions
112 lines (105 loc) · 3.68 KB
/
swc_helpers.rs
File metadata and controls
112 lines (105 loc) · 3.68 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
// Copyright 2018-2024 the Deno authors. MIT license.
use std::ops::ControlFlow;
use deno_ast::swc::ast::ReturnStmt;
use deno_ast::swc::ast::Stmt;
pub enum FunctionKind {
/// function declarations, class method declarations (both class decl and class expr)
DeclarationLike,
/// function expressions, arrow functions, object method shorthand properties
ExpressionLike,
/// getters, both on classes and object literals
Getter,
/// setters, both on classes and object literals
Setter,
}
#[derive(Debug)]
pub enum ReturnStatementAnalysis {
/// There are no return statements in the function body.
None,
/// There are only return statements without arguments in the function body,
/// or if the function body is empty.
Void,
/// There is only a single return statement in the function body, and it has
/// an argument.
Single(ReturnStmt),
/// There are multiple return statements in the function body, and at least
/// one of them has an argument.
Multiple,
}
pub fn analyze_return_stmts_in_function_body(
body: &deno_ast::swc::ast::BlockStmt,
) -> ReturnStatementAnalysis {
if body.stmts.is_empty() {
ReturnStatementAnalysis::Void
} else {
let mut analysis = ReturnStatementAnalysis::None;
analyze_return_stmts_from_stmts(&body.stmts, &mut analysis);
analysis
}
}
fn analyze_return_stmts_from_stmts(
stmts: &[Stmt],
analysis: &mut ReturnStatementAnalysis,
) -> ControlFlow<(), ()> {
for stmt in stmts {
analyze_return_stmts_from_stmt(stmt, analysis)?;
}
ControlFlow::Continue(())
}
fn analyze_return_stmts_from_stmt(
stmt: &Stmt,
analysis: &mut ReturnStatementAnalysis,
) -> ControlFlow<(), ()> {
match stmt {
Stmt::Block(n) => analyze_return_stmts_from_stmts(&n.stmts, analysis),
Stmt::With(n) => analyze_return_stmts_from_stmt(&n.body, analysis),
Stmt::Return(n) => {
match (&n.arg, &*analysis) {
(None, ReturnStatementAnalysis::None) => {
*analysis = ReturnStatementAnalysis::Void;
}
(None, ReturnStatementAnalysis::Void) => {}
(Some(_), ReturnStatementAnalysis::None)
| (Some(_), ReturnStatementAnalysis::Void) => {
*analysis = ReturnStatementAnalysis::Single(n.clone());
}
(_, ReturnStatementAnalysis::Single(_)) => {
*analysis = ReturnStatementAnalysis::Multiple;
return ControlFlow::Break(());
}
(_, ReturnStatementAnalysis::Multiple) => unreachable!(), // we break early when analysis is Multiple
}
ControlFlow::Continue(())
}
Stmt::Labeled(n) => analyze_return_stmts_from_stmt(&n.body, analysis),
Stmt::If(n) => analyze_return_stmts_from_stmt(&n.cons, analysis),
Stmt::Switch(n) => {
for case in &n.cases {
analyze_return_stmts_from_stmts(&case.cons, analysis)?;
}
ControlFlow::Continue(())
}
Stmt::Try(n) => {
analyze_return_stmts_from_stmts(&n.block.stmts, analysis)?;
if let Some(n) = &n.handler {
analyze_return_stmts_from_stmts(&n.body.stmts, analysis)?;
}
if let Some(n) = &n.finalizer {
analyze_return_stmts_from_stmts(&n.stmts, analysis)?;
}
ControlFlow::Continue(())
}
Stmt::While(n) => analyze_return_stmts_from_stmt(&n.body, analysis),
Stmt::DoWhile(n) => analyze_return_stmts_from_stmt(&n.body, analysis),
Stmt::For(n) => analyze_return_stmts_from_stmt(&n.body, analysis),
Stmt::ForIn(n) => analyze_return_stmts_from_stmt(&n.body, analysis),
Stmt::ForOf(n) => analyze_return_stmts_from_stmt(&n.body, analysis),
Stmt::Break(_)
| Stmt::Continue(_)
| Stmt::Throw(_)
| Stmt::Debugger(_)
| Stmt::Decl(_)
| Stmt::Expr(_)
| Stmt::Empty(_) => ControlFlow::Continue(()),
}
}