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] #[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!( let src = indoc!(
r#" r#"
Str.to_u64("123") ?? Num.max_u64 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) // Ok(#double_question_ok_0_17) -> Ok(#double_question_ok_0_17)
// Err(_) -> Num.max_u64 // 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); let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1); assert_eq!(cond_args.len(), 1);
@ -1016,7 +1068,7 @@ mod test_can {
} }
#[test] #[test]
fn try_desugar_single_question_binop() { fn desugar_single_question_binop() {
let src = indoc!( let src = indoc!(
r#" r#"
Str.to_u64("123") ? FailedToConvert Str.to_u64("123") ? FailedToConvert
@ -1033,7 +1085,7 @@ mod test_can {
// Ok(#single_question_ok_0_17) -> #single_question_ok_0_17 // 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)) // 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); let cond_args = assert_func_call(cond_expr, "to_u64", CalledVia::Space, &out.interns);
assert_eq!(cond_args.len(), 1); assert_eq!(cond_args.len(), 1);
@ -1075,11 +1127,13 @@ mod test_can {
} }
#[test] #[test]
fn try_desugar_works_elsewhere() { fn desugar_and_operator() {
let src = indoc!( let src = indoc!(
r#" r#"
when Foo 123 is left = Bool.true
Foo try -> try right = Bool.false
left and right
"# "#
); );
let arena = Bump::new(); let arena = Bump::new();
@ -1087,43 +1141,49 @@ mod test_can {
assert_eq!(out.problems, Vec::new()); assert_eq!(out.problems, Vec::new());
// Assert that we don't treat `try` as a keyword here // Assert that we desugar to:
// by desugaring to:
// //
// when Foo 123 is // if left then right else Bool.false
// Foo try -> try
let (cond_expr, branches) = assert_when(&out.loc_expr.value); let continuation1 = assert_let_expr(&out.loc_expr.value);
match cond_expr { let continuation2 = assert_let_expr(&continuation1.value);
Expr::Tag { let (branches, final_else) = assert_if_expr(&continuation2.value);
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.len(), 1);
assert_eq!(branches[0].patterns.len(), 1);
assert!(!branches[0].patterns[0].degenerate);
match &branches[0].patterns[0].pattern.value { assert_var_usage(&branches[0].0.value, "left", &out.interns);
Pattern::AppliedTag { assert_var_usage(&branches[0].1.value, "right", &out.interns);
tag_name, assert_var_usage(&final_else.value, "false", &out.interns);
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); #[test]
assert!(&branches[0].guard.is_none()); 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) { 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 { match expr {
Expr::When { Expr::When {
loc_cond, branches, .. 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 { fn assert_try_expr(expr: &Expr) -> &Expr {
match expr { match expr {
Expr::Try { result_expr, .. } => &result_expr.value, Expr::Try { result_expr, .. } => &result_expr.value,

View file

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