diff --git a/prqlc/prqlc/src/semantic/resolver/expr.rs b/prqlc/prqlc/src/semantic/resolver/expr.rs index 72d9a7d705f8..531c64490c49 100644 --- a/prqlc/prqlc/src/semantic/resolver/expr.rs +++ b/prqlc/prqlc/src/semantic/resolver/expr.rs @@ -161,9 +161,14 @@ impl pl::PlFold for Resolver<'_> { } // special case: handle the syntax !{tuple..} via resolve_column_exclusion - pl::ExprKind::FuncCall(pl::FuncCall { name, args, .. }) - if (name.kind.as_ident()).is_some_and(|i| i.to_string() == "std.not") - && matches!(args[0].kind, pl::ExprKind::Tuple(_)) => + pl::ExprKind::FuncCall(pl::FuncCall { + name, + args, + named_args, + }) if (name.kind.as_ident()).is_some_and(|i| i.to_string() == "std.not") + && args.len() == 1 + && named_args.is_empty() + && matches!(args[0].kind, pl::ExprKind::Tuple(_)) => { let arg = args.into_iter().exactly_one().unwrap(); self.resolve_column_exclusion(arg)? diff --git a/prqlc/prqlc/tests/integration/bad_error_messages.rs b/prqlc/prqlc/tests/integration/bad_error_messages.rs index 2883a620d1a8..6d76259f6432 100644 --- a/prqlc/prqlc/tests/integration/bad_error_messages.rs +++ b/prqlc/prqlc/tests/integration/bad_error_messages.rs @@ -209,6 +209,16 @@ fn nested_groups() { "); } +#[test] +fn not_with_named_arg() { + // A named argument to `std.not` isn't column-exclusion syntax. Both of these + // used to panic — the first on `exactly_one`, the second on indexing + // `args[0]` with no positional args at all. They're errors now, but the + // message has no span and `Debug`-prints the ident. + assert_snapshot!(compile(r"from x | select (std.not {a} b:1)").unwrap_err(), @r#"Error: unknown named argument `b` to closure Some(["std", "not"])"#); + assert_snapshot!(compile(r"from x | select (std.not b:1)").unwrap_err(), @r#"Error: unknown named argument `b` to closure Some(["std", "not"])"#); +} + #[test] fn just_std() { assert_snapshot!(compile(r###" diff --git a/prqlc/prqlc/tests/integration/error_messages.rs b/prqlc/prqlc/tests/integration/error_messages.rs index fb14604e0103..493c72c98f70 100644 --- a/prqlc/prqlc/tests/integration/error_messages.rs +++ b/prqlc/prqlc/tests/integration/error_messages.rs @@ -554,3 +554,29 @@ fn append_by_name_unnamed() { ───╯ "); } + +#[test] +fn not_with_extra_args() { + // A tuple argument to `std.not` is column-exclusion syntax, but only when + // it's the sole argument — extra args used to panic on `exactly_one`. The + // named-argument forms are in `bad_error_messages::not_with_named_arg`, + // since the message they produce still needs improving. + assert_snapshot!(compile(r"from x | select (std.not {a} b)").unwrap_err(), @" + Error: + ╭─[ :1:18 ] + │ + 1 │ from x | select (std.not {a} b) + │ ──────┬────── + │ ╰──────── Too many arguments to function `not` + ───╯ + "); + assert_snapshot!(compile(r"from x | select (std.not {a} {b})").unwrap_err(), @" + Error: + ╭─[ :1:18 ] + │ + 1 │ from x | select (std.not {a} {b}) + │ ───────┬─────── + │ ╰───────── Too many arguments to function `not` + ───╯ + "); +}