Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions parser/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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.")
Expand All @@ -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(
Expand Down
8 changes: 4 additions & 4 deletions parser/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
34 changes: 34 additions & 0 deletions tests/by-util/test_awk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
Loading