Hoist for loop control var to enclosing scope (#4376)

* Hoist `for` loop control var to enclosing scope

It should be possible to reference the last value assigned to a `for`
loop control var when the loop terminates. This makes it easier to detect
if we broke out of the loop among other things.  This change makes fish
`for` loops behave like most other shells.

Fixes #1935

* Remove redundant line
This commit is contained in:
Kurtis Rader
2017-09-08 21:14:26 -07:00
committed by ridiculousfish
parent 527e102746
commit 905766fca2
11 changed files with 166 additions and 45 deletions

View File

@@ -33,6 +33,7 @@
#include "expand.h"
#include "function.h"
#include "io.h"
#include "maybe.h"
#include "parse_constants.h"
#include "parse_execution.h"
#include "parse_tree.h"
@@ -441,6 +442,15 @@ parse_execution_result_t parse_execution_context_t::run_block_statement(
return ret;
}
/// Return true if the current execution context is within a function block, else false.
bool parse_execution_context_t::is_function_context() const {
const block_t *current = parser->block_at_index(0);
const block_t *parent = parser->block_at_index(1);
bool is_within_function_call =
(current && parent && current->type() == TOP && parent->type() == FUNCTION_CALL);
return is_within_function_call;
}
parse_execution_result_t parse_execution_context_t::run_for_statement(
const parse_node_t &header, const parse_node_t &block_contents) {
assert(header.type == symbol_for_header);
@@ -462,6 +472,17 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(
return ret;
}
auto var = env_get(for_var_name, ENV_LOCAL);
if (!var && !is_function_context()) var = env_get(for_var_name, ENV_DEFAULT);
if (!var || var->read_only()) {
int retval = env_set_empty(for_var_name, ENV_LOCAL | ENV_USER);
if (retval != ENV_OK) {
report_error(var_name_node, L"You cannot use read-only variable '%ls' in a for loop",
for_var_name.c_str());
return parse_execution_errored;
}
}
for_block_t *fb = parser->push_block<for_block_t>();
// Now drive the for loop.
@@ -473,13 +494,7 @@ parse_execution_result_t parse_execution_context_t::run_for_statement(
}
const wcstring &val = argument_sequence.at(i);
int retval = env_set_one(for_var_name, ENV_LOCAL | ENV_USER, val);
if (retval != ENV_OK) {
report_error(var_name_node, L"You cannot use read-only variable '%ls' in a for loop",
for_var_name.c_str());
ret = parse_execution_errored;
break;
}
int retval = env_set_one(for_var_name, ENV_DEFAULT | ENV_USER, val);
fb->loop_status = LOOP_NORMAL;
this->run_job_list(block_contents, fb);