From 3359e5d2e9bcbf19d1652636c8e448a6889302ae Mon Sep 17 00:00:00 2001 From: Fabian Homborg Date: Wed, 21 Jul 2021 22:33:39 +0200 Subject: [PATCH] Let "return" exit a script (#8148) Currently, if a "return" is given outside of a function, we'd just throw an error. That always struck me as a bit weird, given that scripts can also return a value. So simply let "return" outside also exit the script, kinda like "exit" does. However, unlike "exit" it doesn't quit an interactive shell - it seems weird to have "return" do that as well. It sets $status, so it can be used to quickly set that, in case you want to test something. --- doc_src/cmds/return.rst | 1 + src/builtin_return.cpp | 9 ++++++--- src/parse_constants.h | 3 --- src/parse_util.cpp | 19 ------------------- tests/checks/return.fish | 22 ++++++++++++++++++++++ 5 files changed, 29 insertions(+), 25 deletions(-) create mode 100644 tests/checks/return.fish diff --git a/doc_src/cmds/return.rst b/doc_src/cmds/return.rst index 22823ca61..0867a52be 100644 --- a/doc_src/cmds/return.rst +++ b/doc_src/cmds/return.rst @@ -17,6 +17,7 @@ Description It is usually added inside of a conditional block such as an :ref:`if ` statement or a :ref:`switch ` statement to conditionally stop the executing function and return to the caller, but it can also be used to specify the exit status of a function. +If run at the top level of a script, it exits that script and returns the given status, like :ref:`exit `. If run at the top level in an interactive session, it will set ``$status``, but not exit the shell. Example ------- diff --git a/src/builtin_return.cpp b/src/builtin_return.cpp index 181711eb9..9f6ea45d3 100644 --- a/src/builtin_return.cpp +++ b/src/builtin_return.cpp @@ -97,10 +97,13 @@ maybe_t builtin_return(parser_t &parser, io_streams_t &streams, const wchar } } + // If we're not in a function, exit the current script, + // but not an interactive shell. if (!has_function_block) { - streams.err.append_format(_(L"%ls: Not inside of function\n"), cmd); - builtin_print_error_trailer(parser, streams.err, cmd); - return STATUS_CMD_ERROR; + if (!parser.libdata().is_interactive) { + parser.libdata().exit_current_script = true; + } + return retval; } // Mark a return in the libdata. diff --git a/src/parse_constants.h b/src/parse_constants.h index 65031e8f1..5d5eb9502 100644 --- a/src/parse_constants.h +++ b/src/parse_constants.h @@ -235,9 +235,6 @@ enum class pipeline_position_t { /// Error when using continue outside of loop. #define INVALID_CONTINUE_ERR_MSG _(L"'continue' while not inside of loop") -/// Error when using return builtin outside of function definition. -#define INVALID_RETURN_ERR_MSG _(L"'return' outside of function definition") - // Error messages. The number is a reminder of how many format specifiers are contained. /// Error for $^. diff --git a/src/parse_util.cpp b/src/parse_util.cpp index 828c685fb..a0a41f350 100644 --- a/src/parse_util.cpp +++ b/src/parse_util.cpp @@ -1141,25 +1141,6 @@ static bool detect_errors_in_decorated_statement(const wcstring &buff_src, append_syntax_error(parse_errors, source_start, EXEC_ERR_MSG, command.c_str()); } - // Check that we don't return from outside a function. But we allow it if it's - // 'return --help'. - if (!errored && command == L"return" && !first_arg_is_help) { - // See if we are in a function. - bool found_function = false; - for (const node_t *cursor = &dst; cursor != nullptr; cursor = cursor->parent) { - if (const auto *bs = cursor->try_as()) { - if (bs->header->type == type_t::function_header) { - found_function = true; - break; - } - } - } - - if (!found_function) { - errored = append_syntax_error(parse_errors, source_start, INVALID_RETURN_ERR_MSG); - } - } - // Check that we don't break or continue from outside a loop. if (!errored && (command == L"break" || command == L"continue") && !first_arg_is_help) { // Walk up until we hit a 'for' or 'while' loop. If we hit a function first, diff --git a/tests/checks/return.fish b/tests/checks/return.fish new file mode 100644 index 000000000..b63e336e4 --- /dev/null +++ b/tests/checks/return.fish @@ -0,0 +1,22 @@ +#RUN: %fish -C 'set -l fish %fish' %s +# Some tests of the "return" builtin. + +$fish -c 'return 5' +echo $status +# CHECK: 5 + +$fish -c 'exit 5' +echo $status +# CHECK: 5 + +$fish -c 'echo foo; return 2; echo bar' +# CHECK: foo +# but not bar +echo $status +# CHECK: 2 + +$fish -ic 'echo interactive-foo; return 69; echo interactive-bar' +# CHECK: interactive-foo +# but not bar +echo $status +# CHECK: 69