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