diff --git a/src/uu/tr/src/operation.rs b/src/uu/tr/src/operation.rs index e0ed37e2e..3ea96ab18 100644 --- a/src/uu/tr/src/operation.rs +++ b/src/uu/tr/src/operation.rs @@ -319,8 +319,7 @@ impl Sequence { // GNU applies -t before complementing a character class. // That means we must first truncate the expanded, non-complemented // source set, then complement the truncated prefix to recover the - // final translation domain. Complementing first would incorrectly - // shrink the complemented domain to the prefix length. + // final translation domain. let truncated_set1: Vec<_> = set1 .iter() .flat_map(Self::flatten) @@ -329,6 +328,11 @@ impl Sequence { set1_solved = (0..=u8::MAX) .filter(|x| !truncated_set1.contains(x)) .collect(); + // After expansion the complemented domain may be larger than set2. + // Re-check the complement validity constraint. + if set2_uniques.len() > 1 || set1_solved.len() > set2_solved.len() { + return Err(BadSequence::ComplementMoreThanOneUniqueInSet2); + } } else { set1_solved.truncate(set2_solved.len()); } diff --git a/tests/by-util/test_tr.rs b/tests/by-util/test_tr.rs index e07d876dc..41e815deb 100644 --- a/tests/by-util/test_tr.rs +++ b/tests/by-util/test_tr.rs @@ -386,9 +386,10 @@ fn test_truncate_applies_before_complement_with_class() { new_ucmd!() .args(&["-ct", "[:digit:]", "X"]) .pipe_in("A") - .succeeds() - .stdout_is("X"); + .fails() + .stderr_contains("when translating with complemented character classes,\nstring2 must map all characters in the domain to one"); } + #[test] fn missing_args_fails() { let (_, mut ucmd) = at_and_ucmd!();