diff --git a/src/builtins/string.rs b/src/builtins/string.rs index 2445d332a..6cb1090e2 100644 --- a/src/builtins/string.rs +++ b/src/builtins/string.rs @@ -34,11 +34,6 @@ macro_rules! string_error { } use string_error; -fn string_unknown_option(parser: &Parser, streams: &mut IoStreams, subcmd: &wstr, opt: &wstr) { - string_error!(streams, BUILTIN_ERR_UNKNOWN, subcmd, opt); - builtin_print_error_trailer(parser, streams.err, L!("string")); -} - trait StringSubCommand<'args> { const SHORT_OPTIONS: &'static wstr; const LONG_OPTIONS: &'static [WOption<'static>]; @@ -87,13 +82,19 @@ fn parse_opts( return Err(STATUS_INVALID_ARGS); } '?' => { - string_unknown_option(parser, streams, cmd, args_read[w.wopt_index - 1]); + string_error!( + streams, + BUILTIN_ERR_UNKNOWN, + cmd, + args_read[w.wopt_index - 1] + ); + builtin_print_error_trailer(parser, streams.err, L!("string")); return Err(STATUS_INVALID_ARGS); } c => { let retval = self.parse_opt(cmd, c, w.woptarg); if let Err(e) = retval { - e.print_error(&args_read, parser, streams, w.woptarg, w.wopt_index); + e.print_error(&args_read, streams, w.woptarg, w.wopt_index); return Err(e.retval()); } } @@ -236,22 +237,26 @@ impl StringError { fn print_error( &self, args: &[&wstr], - parser: &Parser, streams: &mut IoStreams, optarg: Option<&wstr>, optind: usize, ) { - let cmd = args[0]; + let subcmd = args[0]; use StringError::*; match self { InvalidArgs(msg) => { streams.err.appendln("string ".chars().chain(msg.chars())); } NotANumber => { - string_error!(streams, BUILTIN_ERR_NOT_NUMBER, cmd, optarg.unwrap()); + string_error!(streams, BUILTIN_ERR_NOT_NUMBER, subcmd, optarg.unwrap()); } UnknownOption => { - string_unknown_option(parser, streams, cmd, args[optind - 1]); + // This would mean the subcmd's XXX_OPTIONS does not match + // the list in its `parse_opt()` implementation + unreachable!( + "unexpected option '{}' for 'string {subcmd}'", + args[optind - 1] + ) } } } @@ -368,7 +373,7 @@ pub fn string(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B _ => { streams .err - .appendln(&wgettext_fmt!(BUILTIN_ERR_INVALID_SUBCMD, cmd, args[0])); + .appendln(&wgettext_fmt!(BUILTIN_ERR_INVALID_SUBCMD, cmd, subcmd_name)); builtin_print_error_trailer(parser, streams.err, cmd); Err(STATUS_INVALID_ARGS) } diff --git a/src/builtins/string/replace.rs b/src/builtins/string/replace.rs index 5575f52ef..ed2210aaf 100644 --- a/src/builtins/string/replace.rs +++ b/src/builtins/string/replace.rs @@ -194,7 +194,7 @@ fn new( replacement.to_owned() } else { Self::interpret_escape(replacement) - .ok_or_else(|| RegexError::InvalidEscape(pattern.to_owned()))? + .ok_or_else(|| RegexError::InvalidEscape(replacement.to_owned()))? }; Self::Regex { replacement, diff --git a/tests/checks/string.fish b/tests/checks/string.fish index 3af9c0b2a..aab6aaa65 100644 --- a/tests/checks/string.fish +++ b/tests/checks/string.fish @@ -1,4 +1,4 @@ -#RUN: %fish %s +#RUN: fish=%fish %fish %s # Tests for string builtin. Mostly taken from man page examples. string match -r -v "c.*" dog can cat diz; and echo "exit 0" @@ -1209,3 +1209,6 @@ printf "dog\ncat\nbat\n" | string replace -rf --max-matches 1 'at$' aught printf "dog\ncat\nbat\n" | string replace -r --max-matches 1 '^c' h # CHECK: dog # CHECK: hat + +$fish --features="no-regex-easyesc" -c "string replace -r o '\c' -- foo" +# CHECKERR: string replace: Invalid escape sequence in pattern "\c"