From 4839dff6ab1ad6f5d195a3fbd37ec7900fc018cd Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 26 Jul 2026 17:24:36 +0200 Subject: [PATCH 1/4] Chaining error: improve the error message --- parser/src/diagnostics.rs | 2 +- tests/by-util/test_awk.rs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/parser/src/diagnostics.rs b/parser/src/diagnostics.rs index ca9b158..81d6fb9 100644 --- a/parser/src/diagnostics.rs +++ b/parser/src/diagnostics.rs @@ -170,7 +170,7 @@ impl ParsingError { "This is only valid in some contexts, like a right-hand assignment or a function argument.", ), Self::NonAssociativeOperator(_) => Some( - "Some operators can't be chained to avoid logical errors, such as comparison ones.\n\ + "Some operators cannot be chained because doing so could lead to logical errors. Comparison operators are one example.\n\ Example: write `a == b && b == c` instead of `a == b == c`.", ), Self::ExpectedIdentifier(_, _) => Some( diff --git a/tests/by-util/test_awk.rs b/tests/by-util/test_awk.rs index 74a993b..7fda4ae 100644 --- a/tests/by-util/test_awk.rs +++ b/tests/by-util/test_awk.rs @@ -249,6 +249,15 @@ fn write_to_dev_full_does_not_panic() { ); } +// Chaining non-associative operators must report the diagnostic's help text. +#[test] +fn chained_comparison_reports_help_text() { + ucmd() + .arg("BEGIN { p < q < r }") + .run() + .stderr_contains("Some operators cannot be chained"); +} + #[test] fn array_single_index_get_set() { ucmd() From 5070531c8f8de760570ad57ae366fa9bc13fe9d8 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 26 Jul 2026 17:52:00 +0200 Subject: [PATCH 2/4] parser: report the function name on a duplicated argument --- parser/src/lib.rs | 8 ++++---- tests/by-util/test_awk.rs | 9 +++++++++ 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/parser/src/lib.rs b/parser/src/lib.rs index 48d6426..b75a44d 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -715,16 +715,16 @@ impl<'a> Parser<'a> { } loop { - let name = lex.expect_identifier()?.qualify(lex, self.namespace)?; + let arg = lex.expect_identifier()?.qualify(lex, self.namespace)?; // Linear search is fine for the numbers we are working with. - if let Some(arg) = args.iter().find(|&a| a == &name) { + if args.contains(&arg) { return Err(ParsingError::DuplicatedArgument( lex.span(), - format!("{name:?}"), format!("{arg:?}"), + format!("{name:?}"), )); } - args.push(name); + args.push(arg); if !lex.consume(&Token::Comma) { lex.expect( diff --git a/tests/by-util/test_awk.rs b/tests/by-util/test_awk.rs index 7fda4ae..e55c784 100644 --- a/tests/by-util/test_awk.rs +++ b/tests/by-util/test_awk.rs @@ -258,6 +258,15 @@ fn chained_comparison_reports_help_text() { .stderr_contains("Some operators cannot be chained"); } +// The report must name the enclosing function, not repeat the argument. +#[test] +fn duplicated_parameter_names_the_function() { + ucmd() + .arg("function shuffle(seed, seed) { } BEGIN { shuffle(1, 2) }") + .run() + .stderr_contains("Duplicated argument `awk::seed` to function `awk::shuffle`."); +} + #[test] fn array_single_index_get_set() { ucmd() From 9579235c4f82408f269b9b6a841d6313ea284ccb Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 26 Jul 2026 17:52:27 +0200 Subject: [PATCH 3/4] parser: fix wording of the ternary hint --- parser/src/diagnostics.rs | 2 +- tests/by-util/test_awk.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/parser/src/diagnostics.rs b/parser/src/diagnostics.rs index 81d6fb9..53e2d14 100644 --- a/parser/src/diagnostics.rs +++ b/parser/src/diagnostics.rs @@ -158,7 +158,7 @@ impl ParsingError { "This operand must modify the value of a variable. Consider alternatives like `+` or `-`.", ), Self::MissingTernaryOr(_) => Some( - "Ternaries select between to expression based on a condition, like `bool ? foo : bar`.", + "Ternaries select between two expressions based on a condition, like `bool ? foo : bar`.", ), Self::LexingError(LexingError::UnavailableOnPosix(_, _)) => { Some("This item is not available in POSIX-strict or traditional modes.") diff --git a/tests/by-util/test_awk.rs b/tests/by-util/test_awk.rs index e55c784..a76f68e 100644 --- a/tests/by-util/test_awk.rs +++ b/tests/by-util/test_awk.rs @@ -267,6 +267,14 @@ fn duplicated_parameter_names_the_function() { .stderr_contains("Duplicated argument `awk::seed` to function `awk::shuffle`."); } +#[test] +fn incomplete_ternary_reports_help_text() { + ucmd() + .arg("BEGIN { v = 1 ? 2 }") + .run() + .stderr_contains("Ternaries select between two expressions"); +} + #[test] fn array_single_index_get_set() { ucmd() From aa18aff9c7ee1ec6c0207056d23fdca3beb3f243 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Sun, 26 Jul 2026 17:53:48 +0200 Subject: [PATCH 4/4] parser: clarify the spaced function call error --- parser/src/diagnostics.rs | 2 +- tests/by-util/test_awk.rs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/parser/src/diagnostics.rs b/parser/src/diagnostics.rs index 53e2d14..b36fdf7 100644 --- a/parser/src/diagnostics.rs +++ b/parser/src/diagnostics.rs @@ -58,7 +58,7 @@ pub enum ParsingError { MissingTernaryOr(Span), #[error("Missing closing parenthesis in function call.")] FunctionCallMissingParenthesis(Span), - #[error("Functions calls must have their name yuxtaposed to the parenthesis `(`.")] + #[error("Function calls must not have a space between the name and the parenthesis `(`.")] FunctionCallSeparatedIdent(Span), #[error("Missing closing parenthesis `(` in function call to `{}`.", .1)] FunctionCallUnclosed(Span, String), diff --git a/tests/by-util/test_awk.rs b/tests/by-util/test_awk.rs index a76f68e..eea9e71 100644 --- a/tests/by-util/test_awk.rs +++ b/tests/by-util/test_awk.rs @@ -275,6 +275,14 @@ fn incomplete_ternary_reports_help_text() { .stderr_contains("Ternaries select between two expressions"); } +#[test] +fn spaced_function_call_reports_the_space() { + ucmd() + .arg("function scale(n) { return n } BEGIN { scale (3) }") + .run() + .stderr_contains("must not have a space between the name and the parenthesis"); +} + #[test] fn array_single_index_get_set() { ucmd()