diff --git a/parser/src/diagnostics.rs b/parser/src/diagnostics.rs index ca9b158..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), @@ -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.") @@ -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/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 74a993b..eea9e71 100644 --- a/tests/by-util/test_awk.rs +++ b/tests/by-util/test_awk.rs @@ -249,6 +249,40 @@ 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"); +} + +// 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 incomplete_ternary_reports_help_text() { + ucmd() + .arg("BEGIN { v = 1 ? 2 }") + .run() + .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()