Add tests for desugaring and formatting

This commit is contained in:
Sam Mohr 2025-01-17 18:55:58 -08:00
parent a292e070d4
commit 1a9668e83a
No known key found for this signature in database
GPG key ID: EA41D161A3C1BC99
2 changed files with 174 additions and 51 deletions

View file

@ -972,7 +972,59 @@ mod test_can {
}
#[test]
fn try_desugar_double_question_binop() {
fn try_desugar_works_elsewhere() {
let src = indoc!(
r#"
when Foo 123 is
Foo try -> try
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we don't treat `try` as a keyword here
// by desugaring to:
//
// when Foo 123 is
// Foo try -> try
let (cond_expr, branches) = assert_when_expr(&out.loc_expr.value);
match cond_expr {
Expr::Tag {
name, arguments, ..
} => {
assert_eq!(name.0.to_string(), "Foo");
assert_eq!(arguments.len(), 1);
assert_num_value(&arguments[0].1.value, 123);
}
_ => panic!("cond_expr was not a Tag: {:?}", cond_expr),
}
assert_eq!(branches.len(), 1);
assert_eq!(branches[0].patterns.len(), 1);
assert!(!branches[0].patterns[0].degenerate);
match &branches[0].patterns[0].pattern.value {
Pattern::AppliedTag {
tag_name,
arguments,
..
} => {
assert_eq!(tag_name.0.to_string(), "Foo");
assert_eq!(arguments.len(), 1);
assert_pattern_name(&arguments[0].1.value, "try", &out.interns);
}
other => panic!("First argument was not an applied tag: {:?}", other),
}
assert_var_usage(&branches[0].value.value, "try", &out.interns);
assert!(&branches[0].guard.is_none());
}
#[test]
fn desugar_double_question_binop() {
let src = indoc!(
r#"
Str.to_u64("123") ?? Num.max_u64
@ -989,7 +1041,7 @@ mod test_can {
// Ok(#double_question_ok_0_17) -> Ok(#double_question_ok_0_17)
// Err(_) -> Num.max_u64
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
let (cond_expr, branches) = assert_when_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1);
@ -1016,7 +1068,7 @@ mod test_can {
}
#[test]
fn try_desugar_single_question_binop() {
fn desugar_single_question_binop() {
let src = indoc!(
r#"
Str.to_u64("123") ? FailedToConvert
@ -1033,7 +1085,7 @@ mod test_can {
// Ok(#single_question_ok_0_17) -> #single_question_ok_0_17
// Err(#single_question_err_0_17) -> return Err(FailedToConvert(#single_question_err_0_17))
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
let (cond_expr, branches) = assert_when_expr(&out.loc_expr.value);
let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1);
@ -1075,11 +1127,13 @@ mod test_can {
}
#[test]
fn try_desugar_works_elsewhere() {
fn desugar_and_operator() {
let src = indoc!(
r#"
when Foo 123 is
Foo try -> try
left = Bool.true
right = Bool.false
left and right
"#
);
let arena = Bump::new();
@ -1087,43 +1141,49 @@ mod test_can {
assert_eq!(out.problems, Vec::new());
// Assert that we don't treat `try` as a keyword here
// by desugaring to:
// Assert that we desugar to:
//
// when Foo 123 is
// Foo try -> try
// if left then right else Bool.false
let (cond_expr, branches) = assert_when(&out.loc_expr.value);
match cond_expr {
Expr::Tag {
name, arguments, ..
} => {
assert_eq!(name.0.to_string(), "Foo");
assert_eq!(arguments.len(), 1);
assert_num_value(&arguments[0].1.value, 123);
}
_ => panic!("cond_expr was not a Tag: {:?}", cond_expr),
}
let continuation1 = assert_let_expr(&out.loc_expr.value);
let continuation2 = assert_let_expr(&continuation1.value);
let (branches, final_else) = assert_if_expr(&continuation2.value);
assert_eq!(branches.len(), 1);
assert_eq!(branches[0].patterns.len(), 1);
assert!(!branches[0].patterns[0].degenerate);
match &branches[0].patterns[0].pattern.value {
Pattern::AppliedTag {
tag_name,
arguments,
..
} => {
assert_eq!(tag_name.0.to_string(), "Foo");
assert_eq!(arguments.len(), 1);
assert_pattern_name(&arguments[0].1.value, "try", &out.interns);
}
other => panic!("First argument was not an applied tag: {:?}", other),
}
assert_var_usage(&branches[0].0.value, "left", &out.interns);
assert_var_usage(&branches[0].1.value, "right", &out.interns);
assert_var_usage(&final_else.value, "false", &out.interns);
}
assert_var_usage(&branches[0].value.value, "try", &out.interns);
assert!(&branches[0].guard.is_none());
#[test]
fn desugar_or_operator() {
let src = indoc!(
r#"
left = Bool.true
right = Bool.false
left or right
"#
);
let arena = Bump::new();
let out = can_expr_with(&arena, test_home(), src);
assert_eq!(out.problems, Vec::new());
// Assert that we desugar to:
//
// if left then Bool.true else right
let continuation1 = assert_let_expr(&out.loc_expr.value);
let continuation2 = assert_let_expr(&continuation1.value);
let (branches, final_else) = assert_if_expr(&continuation2.value);
assert_eq!(branches.len(), 1);
assert_var_usage(&branches[0].0.value, "left", &out.interns);
assert_var_usage(&branches[0].1.value, "true", &out.interns);
assert_var_usage(&final_else.value, "right", &out.interns);
}
fn assert_num_value(expr: &Expr, num: usize) {
@ -1238,7 +1298,14 @@ mod test_can {
}
}
fn assert_when(expr: &Expr) -> (&Expr, &Vec<WhenBranch>) {
fn assert_let_expr(expr: &Expr) -> &Loc<Expr> {
match expr {
Expr::LetNonRec(_, continuation) | Expr::LetRec(_, continuation, _) => continuation,
_ => panic!("Expr was not a Let(Non)?Rec: {expr:?}",),
}
}
fn assert_when_expr(expr: &Expr) -> (&Expr, &Vec<WhenBranch>) {
match expr {
Expr::When {
loc_cond, branches, ..
@ -1247,6 +1314,17 @@ mod test_can {
}
}
fn assert_if_expr(expr: &Expr) -> (&[(Loc<Expr>, Loc<Expr>)], &Loc<Expr>) {
match expr {
Expr::If {
branches,
final_else,
..
} => (&branches, &**final_else),
_ => panic!("Expr was not a When: {:?}", expr),
}
}
fn assert_try_expr(expr: &Expr) -> &Expr {
match expr {
Expr::Try { result_expr, .. } => &result_expr.value,

View file

@ -5022,8 +5022,8 @@ mod test_fmt {
r#"
if
(1 == 1)
&& (2 == 1)
&& (3 == 2)
and (2 == 1)
and (3 == 2)
then
"true"
else
@ -5317,18 +5317,63 @@ mod test_fmt {
}
#[test]
fn multi_line_binary_op_1() {
fn multi_line_binary_op_and() {
expr_formats_to(
indoc!(
r"
is_last
&& is_empty
&& is_loaded
"
),
indoc!(
r"
is_last
and is_empty
and is_loaded
"
),
);
expr_formats_same(indoc!(
r"
isLast
&& isEmpty
&& isLoaded
is_last
and is_empty
and is_loaded
"
));
}
#[test]
fn multi_line_binary_op_2() {
fn multi_line_binary_op_or() {
expr_formats_to(
indoc!(
r"
is_last
|| is_empty
|| is_loaded
"
),
indoc!(
r"
is_last
or is_empty
or is_loaded
"
),
);
expr_formats_same(indoc!(
r"
is_last
or is_empty
or is_loaded
"
));
}
#[test]
fn multi_line_binary_op_symbol() {
expr_formats_to(
indoc!(
r"
@ -5398,15 +5443,15 @@ mod test_fmt {
expr_formats_to(
indoc!(
r"
isGreenLight
&& isRedLight && isYellowLight
is_green_light
&& is_red_light && is_yellow_light
"
),
indoc!(
r"
isGreenLight
&& isRedLight
&& isYellowLight
is_green_light
and is_red_light
and is_yellow_light
"
),
);