Handle hash and splat nodes in defined?

This supports the nodes in both in the parse.y and prism compilers.

Fixes [Bug #20043]

Co-authored-by: Kevin Newton <kddnewton@gmail.com>
This commit is contained in:
Jeremy Evans 2024-06-24 11:32:58 -07:00 коммит произвёл GitHub
Родитель e428ee7bbe
Коммит ae0c7faa79
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
3 изменённых файлов: 130 добавлений и 19 удалений

Просмотреть файл

@ -5859,17 +5859,22 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
expr_type = DEFINED_FALSE;
break;
case NODE_HASH:
case NODE_LIST:{
const NODE *vals = node;
const NODE *vals = (nd_type(node) == NODE_HASH) ? RNODE_HASH(node)->nd_head : node;
do {
defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false);
if (vals) {
do {
if (RNODE_LIST(vals)->nd_head) {
defined_expr0(iseq, ret, RNODE_LIST(vals)->nd_head, lfinish, Qfalse, false);
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
}
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
} while ((vals = RNODE_LIST(vals)->nd_next) != NULL);
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
}
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
}
} while ((vals = RNODE_LIST(vals)->nd_next) != NULL);
}
}
/* fall through */
case NODE_STR:
@ -5889,6 +5894,15 @@ defined_expr0(rb_iseq_t *iseq, LINK_ANCHOR *const ret,
expr_type = DEFINED_EXPR;
break;
case NODE_SPLAT:
defined_expr0(iseq, ret, RNODE_LIST(node)->nd_head, lfinish, Qfalse, false);
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(line);
}
ADD_INSNL(ret, line_node, branchunless, lfinish[1]);
expr_type = DEFINED_EXPR;
break;
/* variables */
case NODE_LVAR:
case NODE_DVAR:

Просмотреть файл

@ -3443,19 +3443,83 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_c
dtype = DEFINED_FALSE;
break;
case PM_ARRAY_NODE: {
const pm_array_node_t *cast = (const pm_array_node_t *) node;
const pm_array_node_t *cast = (const pm_array_node_t *) node;
if (!PM_NODE_FLAG_P(cast, PM_ARRAY_NODE_FLAGS_CONTAINS_SPLAT)) {
for (size_t index = 0; index < cast->elements.size; index++) {
pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
if (cast->elements.size > 0 && !lfinish[1]) {
lfinish[1] = NEW_LABEL(location.line);
}
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(location.line);
}
for (size_t index = 0; index < cast->elements.size; index++) {
pm_compile_defined_expr0(iseq, cast->elements.nodes[index], node_location, ret, popped, scope_node, true, lfinish, false);
PUSH_INSNL(ret, location, branchunless, lfinish[1]);
}
PUSH_INSNL(ret, location, branchunless, lfinish[1]);
dtype = DEFINED_EXPR;
break;
}
case PM_HASH_NODE:
case PM_KEYWORD_HASH_NODE: {
const pm_node_list_t *elements;
if (PM_NODE_TYPE_P(node, PM_HASH_NODE)) {
elements = &((const pm_hash_node_t *) node)->elements;
}
else {
elements = &((const pm_keyword_hash_node_t *) node)->elements;
}
if (elements->size > 0 && !lfinish[1]) {
lfinish[1] = NEW_LABEL(location.line);
}
for (size_t index = 0; index < elements->size; index++) {
const pm_node_t *element = elements->nodes[index];
switch (PM_NODE_TYPE(element)) {
case PM_ASSOC_NODE: {
const pm_assoc_node_t *assoc = (const pm_assoc_node_t *) element;
pm_compile_defined_expr0(iseq, assoc->key, node_location, ret, popped, scope_node, true, lfinish, false);
PUSH_INSNL(ret, location, branchunless, lfinish[1]);
pm_compile_defined_expr0(iseq, assoc->value, node_location, ret, popped, scope_node, true, lfinish, false);
PUSH_INSNL(ret, location, branchunless, lfinish[1]);
break;
}
}
case PM_ASSOC_SPLAT_NODE: {
const pm_assoc_splat_node_t *assoc_splat = (const pm_assoc_splat_node_t *) element;
pm_compile_defined_expr0(iseq, assoc_splat->value, node_location, ret, popped, scope_node, true, lfinish, false);
PUSH_INSNL(ret, location, branchunless, lfinish[1]);
break;
}
default:
rb_bug("unexpected node type in hash node: %s", pm_node_type_to_str(PM_NODE_TYPE(element)));
break;
}
}
dtype = DEFINED_EXPR;
break;
}
case PM_SPLAT_NODE: {
const pm_splat_node_t *cast = (const pm_splat_node_t *) node;
pm_compile_defined_expr0(iseq, cast->expression, node_location, ret, popped, scope_node, in_condition, lfinish, false);
if (!lfinish[1]) {
lfinish[1] = NEW_LABEL(location.line);
}
PUSH_INSNL(ret, location, branchunless, lfinish[1]);
dtype = DEFINED_EXPR;
break;
}
case PM_IMPLICIT_NODE: {
const pm_implicit_node_t *cast = (const pm_implicit_node_t *) node;
pm_compile_defined_expr0(iseq, cast->value, node_location, ret, popped, scope_node, in_condition, lfinish, explicit_receiver);
return;
}
case PM_AND_NODE:
case PM_BEGIN_NODE:
@ -3467,7 +3531,6 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_c
case PM_DEFINED_NODE:
case PM_FLOAT_NODE:
case PM_FOR_NODE:
case PM_HASH_NODE:
case PM_IF_NODE:
case PM_IMAGINARY_NODE:
case PM_INTEGER_NODE:
@ -3475,7 +3538,6 @@ pm_compile_defined_expr0(rb_iseq_t *iseq, const pm_node_t *node, const pm_line_c
case PM_INTERPOLATED_STRING_NODE:
case PM_INTERPOLATED_SYMBOL_NODE:
case PM_INTERPOLATED_X_STRING_NODE:
case PM_KEYWORD_HASH_NODE:
case PM_LAMBDA_NODE:
case PM_MATCH_PREDICATE_NODE:
case PM_MATCH_REQUIRED_NODE:

Просмотреть файл

@ -139,6 +139,41 @@ class TestDefined < Test::Unit::TestCase
assert_equal("assignment", eval('defined?(A::B &&= 1)'))
end
def test_defined_splat
assert_nil(defined?([*a]))
assert_nil(defined?(itself(*a)))
assert_equal("expression", defined?([*itself]))
assert_equal("method", defined?(itself(*itself)))
end
def test_defined_hash
assert_nil(defined?({a: a}))
assert_nil(defined?({a => 1}))
assert_nil(defined?({a => a}))
assert_nil(defined?({**a}))
assert_nil(defined?(itself(a: a)))
assert_nil(defined?(itself(a => 1)))
assert_nil(defined?(itself(a => a)))
assert_nil(defined?(itself(**a)))
assert_nil(defined?(itself({a: a})))
assert_nil(defined?(itself({a => 1})))
assert_nil(defined?(itself({a => a})))
assert_nil(defined?(itself({**a})))
assert_equal("expression", defined?({a: itself}))
assert_equal("expression", defined?({itself => 1}))
assert_equal("expression", defined?({itself => itself}))
assert_equal("expression", defined?({**itself}))
assert_equal("method", defined?(itself(a: itself)))
assert_equal("method", defined?(itself(itself => 1)))
assert_equal("method", defined?(itself(itself => itself)))
assert_equal("method", defined?(itself(**itself)))
assert_equal("method", defined?(itself({a: itself})))
assert_equal("method", defined?(itself({itself => 1})))
assert_equal("method", defined?(itself({itself => itself})))
assert_equal("method", defined?(itself({**itself})))
end
def test_defined_literal
assert_equal("nil", defined?(nil))
assert_equal("true", defined?(true))