use super::{ControlFlow, End, Metadata};
use crate::test_util;
use deno_ast::StartSourcePos;
fn analyze_flow(src: &str, callback: impl Fn(ControlFlow)) {
let parsed_source = test_util::parse(src);
parsed_source.with_view(|pg| {
let flow = ControlFlow::analyze(pg, parsed_source.unresolved_context());
callback(flow);
});
}
macro_rules! assert_flow {
($flow:ident, $lo:expr, $unreachable:expr, $end:expr) => {
assert_eq!(
$flow.meta(StartSourcePos::START_SOURCE_POS + $lo).unwrap(),
&Metadata {
unreachable: $unreachable,
end: $end,
}
);
};
}
#[test]
fn while_1() {
let src = r#"
function foo() {
while (a) {
break;
}
return 1;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(flow, 30, false, Some(End::Continue)); assert_flow!(flow, 49, false, Some(End::forced_return())); });
}
#[test]
fn while_2() {
let src = r#"
function foo() {
while (a) {
break;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 30, false, Some(End::Continue)); assert_flow!(flow, 49, false, None); });
}
#[test]
fn while_3() {
let src = r#"
function foo() {
while (a) {
bar();
}
baz();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 30, false, Some(End::Continue)); assert_flow!(flow, 36, false, None); assert_flow!(flow, 49, false, None); });
}
#[test]
fn while_4() {
let src = r#"
function foo() {
while (a) {
return 1;
}
baz();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue));
assert_flow!(flow, 30, false, Some(End::Continue));
assert_flow!(flow, 36, false, Some(End::forced_return())); assert_flow!(flow, 52, false, None); });
}
#[test]
fn while_5() {
let src = r#"
function foo() {
while (true) {
return 1;
}
baz();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return()));
assert_flow!(flow, 33, false, Some(End::forced_return()));
assert_flow!(flow, 39, false, Some(End::forced_return())); assert_flow!(flow, 55, true, None); });
}
#[test]
fn while_6() {
let src = r#"
while (true) {
if (x === 42) {
break;
}
throw new Error("error");
}
foo();
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, None); assert_flow!(flow, 14, false, Some(End::Continue)); assert_flow!(flow, 18, false, Some(End::Continue)); assert_flow!(flow, 32, false, Some(End::Break)); assert_flow!(flow, 38, false, Some(End::Break)); assert_flow!(flow, 51, false, Some(End::forced_throw())); assert_flow!(flow, 79, false, None); });
}
#[test]
fn do_while_1() {
let src = r#"
function foo() {
do {
break;
} while (a);
return 1;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(flow, 23, false, Some(End::Continue)); assert_flow!(flow, 53, false, Some(End::forced_return())); });
}
#[test]
fn do_while_2() {
let src = r#"
function foo() {
do {
break;
} while (a);
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 23, false, Some(End::Continue)); assert_flow!(flow, 53, false, None); });
}
#[test]
fn do_while_3() {
let src = r#"
function foo() {
do {
bar();
} while (a);
baz();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 23, false, Some(End::Continue)); assert_flow!(flow, 53, false, None); });
}
#[test]
fn do_while_4() {
let src = r#"
function foo() {
do {
bar();
} while (true);
return 1;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_infinite_loop())); assert_flow!(flow, 23, false, Some(End::forced_infinite_loop())); assert_flow!(
flow,
56,
true,
Some(End::Forced {
ret: true,
throw: false,
infinite_loop: true
})
); });
}
#[test]
fn do_while_5() {
let src = r#"
function foo() {
do {
return 0;
} while (a);
return 1;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(flow, 23, false, Some(End::forced_return())); assert_flow!(flow, 56, true, Some(End::forced_return())); });
}
#[test]
fn do_while_6() {
let src = r#"
function foo() {
do {
throw 0;
} while (false);
return 1;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_throw())); assert_flow!(flow, 23, false, Some(End::forced_throw())); assert_flow!(
flow,
59,
true,
Some(End::Forced {
ret: true,
throw: true,
infinite_loop: false
})
); });
}
#[test]
fn do_while_7() {
let src = r#"
function foo() {
do {
throw 0;
} while (a);
return 1;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_throw())); assert_flow!(flow, 23, false, Some(End::forced_throw())); assert_flow!(flow, 29, false, Some(End::forced_throw())); assert_flow!(
flow,
55,
true,
Some(End::Forced {
ret: true,
throw: true,
infinite_loop: false
})
); });
}
#[test]
fn do_while_8() {
let src = r#"
do {
if (x === 42) {
break;
}
throw new Error("error");
} while (true);
foo();
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, None); assert_flow!(flow, 4, false, Some(End::Continue)); assert_flow!(flow, 8, false, Some(End::Continue)); assert_flow!(flow, 22, false, Some(End::Break)); assert_flow!(flow, 28, false, Some(End::Break)); assert_flow!(flow, 41, false, Some(End::forced_throw())); assert_flow!(flow, 83, false, None); });
}
#[test]
fn for_1() {
let src = r#"
function foo() {
for (let i = 0; f(); i++) {
return 1;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue));
assert_flow!(flow, 46, false, Some(End::Continue));
assert_flow!(flow, 52, false, Some(End::forced_return())); assert_flow!(flow, 68, false, None); });
}
#[test]
fn for_2() {
let src = r#"
function foo() {
for (let i = 0; true; i++) {
return 1;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(flow, 47, false, Some(End::forced_return())); assert_flow!(flow, 53, false, Some(End::forced_return())); assert_flow!(flow, 69, true, None); });
}
#[test]
fn for_3() {
let src = r#"
function foo() {
for (let i = 0;; i++) {
return 1;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(flow, 42, false, Some(End::forced_return())); assert_flow!(flow, 48, false, Some(End::forced_return())); assert_flow!(flow, 64, true, None); });
}
#[test]
fn for_4() {
let src = r#"
function foo() {
for (let i = 0; false; i++) {
return 1;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 48, false, Some(End::Continue)); assert_flow!(flow, 54, false, Some(End::forced_return())); assert_flow!(flow, 70, false, None); });
}
#[test]
fn for_5() {
let src = r#"
for (let i = 0; true; i++) {
if (f(i)) {
break;
}
throw new Error("error");
}
foo();
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, None); assert_flow!(flow, 28, false, Some(End::Continue)); assert_flow!(flow, 32, false, Some(End::Continue)); assert_flow!(flow, 42, false, Some(End::Break)); assert_flow!(flow, 48, false, Some(End::Break)); assert_flow!(flow, 61, false, Some(End::forced_throw())); assert_flow!(flow, 89, false, None); });
}
#[test]
fn for_in_1() {
let src = r#"
function foo() {
for (let i in {}) {
return 1;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 38, false, Some(End::Continue)); assert_flow!(flow, 44, false, Some(End::forced_return())); assert_flow!(flow, 60, false, None); });
}
#[test]
fn for_in_2() {
let src = r#"
function foo() {
for (let i in {}) {
break;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 38, false, Some(End::Continue)); assert_flow!(flow, 44, false, Some(End::Break)); assert_flow!(flow, 57, false, None); });
}
#[test]
fn for_of_1() {
let src = r#"
function foo() {
for (let i of []) {
return 1;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 38, false, Some(End::Continue)); assert_flow!(flow, 44, false, Some(End::forced_return())); assert_flow!(flow, 60, false, None); });
}
#[test]
fn for_of_2() {
let src = r#"
function foo() {
for (let i of []) {
break;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 38, false, Some(End::Continue)); assert_flow!(flow, 44, false, Some(End::Break)); assert_flow!(flow, 57, false, None); });
}
#[test]
fn try_1() {
let src = r#"
function foo() {
try {
return 1;
} finally {
bar();
}
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(flow, 20, false, Some(End::forced_return())); assert_flow!(flow, 24, false, Some(End::forced_return())); assert_flow!(flow, 30, false, Some(End::forced_return())); assert_flow!(flow, 52, false, Some(End::Continue)); assert_flow!(flow, 58, false, None); });
}
#[test]
fn try_2() {
let src = r#"
function foo() {
try {
throw 1;
} catch (e) {
return 2;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(
flow,
20,
false,
Some(End::Forced {
ret: true,
throw: false,
infinite_loop: false
})
); assert_flow!(flow, 24, false, Some(End::forced_throw())); assert_flow!(flow, 30, false, Some(End::forced_throw())); assert_flow!(flow, 43, false, Some(End::forced_return())); assert_flow!(flow, 53, false, Some(End::forced_return())); assert_flow!(flow, 59, false, Some(End::forced_return())); assert_flow!(flow, 75, true, None); });
}
#[test]
fn try_3() {
let src = r#"
function foo() {
try {
throw 1;
} catch (e) {
bar();
}
baz();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 20, false, Some(End::Continue)); assert_flow!(flow, 24, false, Some(End::forced_throw())); assert_flow!(flow, 30, false, Some(End::forced_throw())); assert_flow!(flow, 43, false, Some(End::Continue)); assert_flow!(flow, 53, false, Some(End::Continue)); assert_flow!(flow, 59, false, None); assert_flow!(flow, 72, false, None); });
}
#[test]
fn try_4() {
let src = r#"
function foo() {
try {
throw 1;
} catch (e) {
bar();
} finally {
baz();
}
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 20, false, Some(End::Continue)); assert_flow!(flow, 24, false, Some(End::forced_throw())); assert_flow!(flow, 30, false, Some(End::forced_throw())); assert_flow!(flow, 43, false, Some(End::Continue)); assert_flow!(flow, 53, false, Some(End::Continue)); assert_flow!(flow, 59, false, None); assert_flow!(flow, 78, false, Some(End::Continue)); assert_flow!(flow, 84, false, None); });
}
#[test]
fn try_5() {
let src = r#"
function foo() {
try {
throw 1;
} catch (e) {
return 2;
} finally {
bar();
}
baz();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(
flow,
20,
false,
Some(End::Forced {
ret: true,
throw: false,
infinite_loop: false
})
); assert_flow!(flow, 24, false, Some(End::forced_throw())); assert_flow!(flow, 30, false, Some(End::forced_throw())); assert_flow!(flow, 43, false, Some(End::forced_return())); assert_flow!(flow, 53, false, Some(End::forced_return())); assert_flow!(flow, 59, false, Some(End::forced_return())); assert_flow!(flow, 81, false, Some(End::Continue)); assert_flow!(flow, 87, false, None); assert_flow!(flow, 100, true, None); });
}
#[test]
fn try_6() {
let src = r#"
try {}
finally {
break;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Break)); assert_flow!(flow, 5, false, Some(End::Continue)); assert_flow!(flow, 16, false, Some(End::Break)); assert_flow!(flow, 20, false, Some(End::Break)); });
}
#[test]
fn try_7() {
let src = r#"
try {
throw 0;
} catch (e) {
break;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Break)); assert_flow!(flow, 5, false, Some(End::forced_throw())); assert_flow!(flow, 9, false, Some(End::forced_throw())); assert_flow!(flow, 20, false, Some(End::Break)); assert_flow!(flow, 30, false, Some(End::Break)); assert_flow!(flow, 34, false, Some(End::Break)); });
}
#[test]
fn try_8() {
let src = r#"
try {
break;
} finally {}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Break)); assert_flow!(flow, 5, false, Some(End::Break)); assert_flow!(flow, 9, false, Some(End::Break)); assert_flow!(flow, 26, false, Some(End::Continue)); });
}
#[test]
fn try_9() {
let src = r#"
try {
try {
throw 1;
} catch {
throw 2;
}
} catch {
foo();
}
bar();
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Continue)); assert_flow!(flow, 5, false, Some(End::forced_throw())); assert_flow!(flow, 9, false, Some(End::forced_throw())); assert_flow!(flow, 13, false, Some(End::forced_throw())); assert_flow!(flow, 19, false, Some(End::forced_throw())); assert_flow!(flow, 32, false, Some(End::forced_throw())); assert_flow!(flow, 44, false, Some(End::forced_throw())); assert_flow!(flow, 59, false, Some(End::Continue)); assert_flow!(flow, 69, false, None); assert_flow!(flow, 78, false, None); });
}
#[test]
fn try_10() {
let src = r#"
try {
try {
throw 1;
} catch {
f();
}
} catch {
foo();
}
bar();
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Continue)); assert_flow!(flow, 5, false, Some(End::Continue)); assert_flow!(flow, 9, false, Some(End::Continue)); assert_flow!(flow, 13, false, Some(End::forced_throw())); assert_flow!(flow, 19, false, Some(End::forced_throw())); assert_flow!(flow, 32, false, Some(End::Continue)); assert_flow!(flow, 44, false, None); assert_flow!(flow, 55, false, Some(End::Continue)); assert_flow!(flow, 65, false, None); assert_flow!(flow, 74, false, None); });
}
#[test]
fn try_11() {
let src = r#"
try {
throw 0;
} catch (e) {
break;
} finally {
return;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::forced_return())); assert_flow!(flow, 5, false, Some(End::forced_throw())); assert_flow!(flow, 9, false, Some(End::forced_throw())); assert_flow!(flow, 20, false, Some(End::Break)); assert_flow!(flow, 30, false, Some(End::Break)); assert_flow!(flow, 34, false, Some(End::Break)); assert_flow!(flow, 51, false, Some(End::forced_return())); assert_flow!(flow, 55, false, Some(End::forced_return())); });
}
#[test]
fn if_1() {
let src = r#"
function foo() {
if (a) {
return 1;
}
bar();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 20, false, Some(End::Continue)); assert_flow!(flow, 27, false, Some(End::forced_return())); assert_flow!(flow, 33, false, Some(End::forced_return())); assert_flow!(flow, 49, false, None); });
}
#[test]
fn if_2() {
let src = r#"
function foo() {
if (a) {
bar();
} else {
return 1;
}
baz();
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 20, false, Some(End::Continue)); assert_flow!(flow, 27, false, Some(End::Continue)); assert_flow!(flow, 33, false, None); assert_flow!(flow, 49, false, Some(End::forced_return())); assert_flow!(flow, 55, false, Some(End::forced_return())); assert_flow!(flow, 71, false, None); });
}
#[test]
fn if_3() {
let src = r#"
function foo() {
if (a) {
return 1;
} else {
bar();
}
return 0;
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 16, false, Some(End::forced_return())); assert_flow!(flow, 20, false, Some(End::Continue)); assert_flow!(flow, 27, false, Some(End::forced_return())); assert_flow!(flow, 33, false, Some(End::forced_return())); assert_flow!(flow, 52, false, Some(End::Continue)); assert_flow!(flow, 58, false, None); assert_flow!(flow, 71, false, Some(End::forced_return())); });
}
#[test]
fn switch_1() {
let src = r#"
switch (foo) {
case 1:
return 0;
default: {
if (bar) {
break;
}
return 0;
}
}
throw err;
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Continue)); assert_flow!(flow, 18, false, Some(End::forced_return())); assert_flow!(flow, 30, false, Some(End::forced_return())); assert_flow!(flow, 42, false, Some(End::Break)); assert_flow!(flow, 51, false, Some(End::forced_return())); assert_flow!(flow, 57, false, Some(End::Continue)); assert_flow!(flow, 66, false, Some(End::Break)); assert_flow!(flow, 74, false, Some(End::Break)); assert_flow!(flow, 91, false, Some(End::forced_return())); assert_flow!(flow, 107, false, Some(End::forced_throw())); });
}
#[test]
fn switch_2() {
let src = r#"
switch (foo) {
case 1:
return 0;
default: {
return 0;
}
}
throw err;
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::forced_return())); assert_flow!(flow, 18, false, Some(End::forced_return())); assert_flow!(flow, 30, false, Some(End::forced_return())); assert_flow!(flow, 42, false, Some(End::forced_return())); assert_flow!(flow, 51, false, Some(End::forced_return())); assert_flow!(flow, 57, false, Some(End::forced_return())); assert_flow!(
flow,
73,
true,
Some(End::Forced {
ret: true,
throw: true,
infinite_loop: false
})
); });
}
#[test]
fn switch_3() {
let src = r#"
switch (foo) {
case 1:
break;
default: {
return 0;
}
}
throw err;
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Continue)); assert_flow!(flow, 18, false, Some(End::Break)); assert_flow!(flow, 30, false, Some(End::Break)); assert_flow!(flow, 39, false, Some(End::forced_return())); assert_flow!(flow, 48, false, Some(End::forced_return())); assert_flow!(flow, 54, false, Some(End::forced_return())); assert_flow!(flow, 70, false, Some(End::forced_throw())); });
}
#[test]
fn issue_823() {
let src = r#"
switch (foo) {
case 1:
switch (bar) {
case 1:
break;
}
return 0;
default: {
return 0;
}
}"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::forced_return())); assert_flow!(flow, 18, false, Some(End::forced_return())); assert_flow!(flow, 30, false, Some(End::Continue)); assert_flow!(flow, 51, false, Some(End::Break)); assert_flow!(flow, 67, false, Some(End::Break)); assert_flow!(flow, 84, false, Some(End::forced_return())); assert_flow!(flow, 96, false, Some(End::forced_return())); assert_flow!(flow, 111, false, Some(End::forced_return())); });
}
#[test]
fn issue_644() {
let src = r#"
function foo() {
break;
}
function bar() {
continue;
}
"#;
analyze_flow(src, |_flow| {});
}
#[test]
fn issue_716() {
let src = r#"
function foo() {
if (bool) {} else {}
try {
bar();
return 42;
} catch (err) {
console.error(err);
}
}
"#;
analyze_flow(src, |flow| {
assert_flow!(flow, 1, false, Some(End::Continue)); assert_flow!(flow, 16, false, Some(End::Continue)); assert_flow!(flow, 20, false, Some(End::Continue)); assert_flow!(flow, 30, false, Some(End::Continue)); assert_flow!(flow, 38, false, Some(End::Continue)); assert_flow!(flow, 43, false, Some(End::Continue)); assert_flow!(flow, 47, false, Some(End::forced_return())); assert_flow!(flow, 53, false, None); assert_flow!(flow, 64, false, Some(End::forced_return())); assert_flow!(flow, 79, false, Some(End::Continue)); assert_flow!(flow, 91, false, Some(End::Continue)); assert_flow!(flow, 97, false, None); });
}