test: reject a -a/-o that ends the expression - #14395
Open
Socialpranker wants to merge 1 commit into
Open
Conversation
`test x -a` and `test x -o` exited 0 instead of failing with the usual missing-operand diagnostic: the parser turned any BOOLOP found at the end of the token stream into a literal string. That fallback is only right when the BOOLOP has no operand on either side, as in the one-argument `test -a`; once an expression has been parsed the trailing BOOLOP joins it to nothing, which is an error.
|
GNU testsuite comparison: |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A
-aor-oat the end of an expression was silently swallowed:That is the same diagnostic uutils already produces for the other operators
that run out of an operand (
test x =,test 1 -gt), and the exit status2 is what
--helpreserves for a malformed expression:Returning 0 here is the worst possible answer: a truncated
-achain — anunquoted empty variable is the usual way to get one — reads as true.
The fix
maybe_boolopturned a BOOLOP found at the end of the token stream into aliteral string. That is correct only when the BOOLOP has no operand on
either side, which is the one-argument
test -a(a string-length test of"-a") and the same case nested inside another BOOLOP,test -o -o. Oncean expression has been parsed the trailing BOOLOP joins it to nothing, so
maybe_boolopnow takes whether a left operand exists and reportsMissingArgumentin that case.exprpasses what it knows; the recursivecall and the one in
bangpasstrue, since both have just parsed a term.No new error kind, no new message:
ParseErrorKind::MissingArgumentandits diagnostics entry already exist and are what the other operators use.
This also un-ignores
test_string_length_and_nothing, which was marked#[ignore = "GNU considers this an error"]for exactly this gap.Not in scope
test -a x -aandtest ! -a xstill differ from GNU in which token themessage names (
'x': binary operator expectedvs. the one printed here).That is issue #6203 — the parse stack does not record enough to tell those
inputs apart — and is untouched by this change: they exited 0/1 before and
still do not match, except that the first now at least exits 2.
How the GNU behavior was established
By running the installed GNU coreutils 9.11 binary (Homebrew,
gtest) as ablack box over 26 combinations of
-a/-oin leading, medial and trailingposition, with and without
!, parentheses, unary operators and emptyoperands, and diffing exit status and stderr against uutils. I did not read
GNU coreutils source.
Testing
tests/by-util/test_test.rs:test_boolop_without_right_operand(both operators, empty left operand,and after a chain, a parenthesized group and
!) andtest_lone_boolop_is_a_string(guards the literal fallback that muststay:
test -a,test ! -a,test -n -a), plus the un-ignoredtest_string_length_and_nothing. Mutation-checked: revertingparser.rsalone makes two of them fail.cargo test --features test --test tests test_test: 107 passed, 0failed, 3 ignored (105/0/4 before).
cargo clippy -p uu_test --all-targets -- -D warnings: clean.cargo fmt --check: clean.28 utilities: mismatches 79 -> 78, the
testbucket 1 -> 0, no otherbucket moved.
Disclosure
Prepared with AI assistance (Claude Opus 5, via Claude Code), per the AI
policy in CONTRIBUTING.md. Every GNU behavior quoted above came from
running the installed binary, not from reading GPL source. All testing was
run locally.