Bug: KCL formatter removes 'fn' from closures: (#4718)
# Problem
Before this PR, our formatter reformats
```
squares_out = reduce(arr, 0, fn (i, squares) {
return 1
})
```
to
```
squares_out = reduce(arr, 0, (i, squares) {
return 1
})
```
i.e. it removes the `fn` keyword from the closure. This keyword is required, so, our formatter turned working code into invalid code.
# Cause
When this closure parameter is formatted, the ExprContext is ::Decl, so `Expr::recast` skips adding the `fn` keyword. The reason it's ::Decl is because the `squares_out = ` declaration sets it, and no subsequent call sets the context to something else.
# Solution
When recasting a call expression, set the context for every argument to `ExprContext::Other`.
This commit is contained in:
@ -166,7 +166,14 @@ pub(crate) enum ExprContext {
|
||||
}
|
||||
|
||||
impl Expr {
|
||||
pub(crate) fn recast(&self, options: &FormatOptions, indentation_level: usize, ctxt: ExprContext) -> String {
|
||||
pub(crate) fn recast(&self, options: &FormatOptions, indentation_level: usize, mut ctxt: ExprContext) -> String {
|
||||
let is_decl = matches!(ctxt, ExprContext::Decl);
|
||||
if is_decl {
|
||||
// Just because this expression is being bound to a variable, doesn't mean that every child
|
||||
// expression is being bound. So, reset the expression context if necessary.
|
||||
// This will still preserve the "::Pipe" context though.
|
||||
ctxt = ExprContext::Other;
|
||||
}
|
||||
match &self {
|
||||
Expr::BinaryExpression(bin_exp) => bin_exp.recast(options),
|
||||
Expr::ArrayExpression(array_exp) => array_exp.recast(options, indentation_level, ctxt),
|
||||
@ -175,11 +182,7 @@ impl Expr {
|
||||
Expr::MemberExpression(mem_exp) => mem_exp.recast(),
|
||||
Expr::Literal(literal) => literal.recast(),
|
||||
Expr::FunctionExpression(func_exp) => {
|
||||
let mut result = if ctxt == ExprContext::Decl {
|
||||
String::new()
|
||||
} else {
|
||||
"fn".to_owned()
|
||||
};
|
||||
let mut result = if is_decl { String::new() } else { "fn".to_owned() };
|
||||
result += &func_exp.recast(options, indentation_level);
|
||||
result
|
||||
}
|
||||
@ -2170,6 +2173,28 @@ sketch002 = startSketchOn({
|
||||
assert_eq!(actual, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unparse_fn_unnamed() {
|
||||
let input = r#"squares_out = reduce(arr, 0, fn(i, squares) {
|
||||
return 1
|
||||
})
|
||||
"#;
|
||||
let ast = crate::parsing::top_level_parse(input).unwrap();
|
||||
let actual = ast.recast(&FormatOptions::new(), 0);
|
||||
assert_eq!(actual, input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unparse_fn_named() {
|
||||
let input = r#"fn f(x) {
|
||||
return 1
|
||||
}
|
||||
"#;
|
||||
let ast = crate::parsing::top_level_parse(input).unwrap();
|
||||
let actual = ast.recast(&FormatOptions::new(), 0);
|
||||
assert_eq!(actual, input);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn recast_objects_with_comments() {
|
||||
use winnow::Parser;
|
||||
|
||||
Reference in New Issue
Block a user