KCL: Fix cryptic error on unexpected tokens in fn call (#7295)

This program:
```kcl
1
|> extrude(
  length=depth,
})
```

was giving this bad error:

```
unexpected token |>
```

Now it gives

```kcl
There was an unexpected }. Try removing it.
```

and it correctly puts the diagnostic on the extra }.

Fixes https://github.com/KittyCAD/modeling-app/issues/6126
This commit is contained in:
Adam Chalmers
2025-05-30 13:57:05 -05:00
committed by GitHub
parent a506f7f698
commit 5fccaad0e7
2 changed files with 42 additions and 10 deletions

View File

@ -3141,7 +3141,7 @@ fn binding_name(i: &mut TokenSlice) -> ModalResult<Node<Identifier>> {
/// Either a positional or keyword function call.
fn fn_call_pos_or_kw(i: &mut TokenSlice) -> ModalResult<Expr> {
alt((fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),)).parse_next(i)
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw).parse_next(i)
}
fn labelled_fn_call(i: &mut TokenSlice) -> ModalResult<Expr> {
@ -3270,7 +3270,22 @@ fn fn_call_kw(i: &mut TokenSlice) -> ModalResult<Node<CallExpressionKw>> {
)?;
ignore_whitespace(i);
opt(comma_sep).parse_next(i)?;
let end = close_paren.parse_next(i)?.end;
let end = match close_paren.parse_next(i) {
Ok(tok) => tok.end,
Err(e) => {
if let Some(tok) = i.next_token() {
return Err(ErrMode::Cut(
CompilationError::fatal(
SourceRange::from(&tok),
format!("There was an unexpected {}. Try removing it.", tok.value),
)
.into(),
));
} else {
return Err(e);
}
}
};
// Validate there aren't any duplicate labels.
let mut counted_labels = IndexMap::with_capacity(args.len());
@ -3391,8 +3406,7 @@ mod tests {
fn kw_call_as_operand() {
let tokens = crate::parsing::token::lex("f(x = 1)", ModuleId::default()).unwrap();
let tokens = tokens.as_slice();
let op = operand.parse(tokens).unwrap();
println!("{op:#?}");
operand.parse(tokens).unwrap();
}
#[test]
@ -4432,8 +4446,8 @@ z(-[["#,
assert_err(
r#"z
(--#"#,
"Unexpected token: (",
[2, 3],
"There was an unexpected -. Try removing it.",
[3, 4],
);
}
@ -5188,14 +5202,32 @@ bar = 1
}
}
#[test]
fn test_sensible_error_when_unexpected_token_in_fn_call() {
let program_source = "1
|> extrude(
length=depth,
})";
let expected_src_start = program_source.find("}").expect("Program should have an extraneous }");
let tokens = crate::parsing::token::lex(program_source, ModuleId::default()).unwrap();
ParseContext::init();
let err = program.parse(tokens.as_slice()).unwrap_err();
let cause = err
.inner()
.cause
.as_ref()
.expect("Found an error, but there was no cause. Add a cause.");
assert_eq!(cause.message, "There was an unexpected }. Try removing it.",);
assert_eq!(cause.source_range.start(), expected_src_start);
}
#[test]
fn test_sensible_error_when_using_keyword_as_arg_label() {
for (i, program) in ["pow(2, fn = 8)"].into_iter().enumerate() {
let tokens = crate::parsing::token::lex(program, ModuleId::default()).unwrap();
let err = match fn_call_kw.parse(tokens.as_slice()) {
Err(e) => e,
Ok(ast) => {
eprintln!("{ast:#?}");
Ok(_ast) => {
panic!("Expected this to error but it didn't");
}
};