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.
This commit is contained in:
Fabian Homborg 2021-07-21 22:33:39 +02:00 committed by GitHub
parent a32248277f
commit 3359e5d2e9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 29 additions and 25 deletions

View file

@ -17,6 +17,7 @@ Description
It is usually added inside of a conditional block such as an :ref:`if <cmd-if>` statement or a :ref:`switch <cmd-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 <cmd-exit>`. If run at the top level in an interactive session, it will set ``$status``, but not exit the shell.
Example
-------

View file

@ -97,10 +97,13 @@ maybe_t<int> 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.

View file

@ -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 $^.

View file

@ -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<block_statement_t>()) {
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,

22
tests/checks/return.fish Normal file
View file

@ -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