[Bug #20468] Fix safe navigation in for variable

This commit is contained in:
Nobuyoshi Nakada 2024-05-16 16:22:17 +09:00
parent 9d69619623
commit 2dd46bb82f
No known key found for this signature in database
GPG Key ID: 3582D74E1FEE4465
2 changed files with 29 additions and 6 deletions

View File

@ -5380,12 +5380,17 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
CHECK(COMPILE_POPPED(pre, "masgn lhs (NODE_ATTRASGN)", node));
bool safenav_call = false;
LINK_ELEMENT *insn_element = LAST_ELEMENT(pre);
iobj = (INSN *)get_prev_insn((INSN *)insn_element); /* send insn */
ASSUME(iobj);
ELEM_REMOVE(LAST_ELEMENT(pre));
ELEM_REMOVE((LINK_ELEMENT *)iobj);
pre->last = iobj->link.prev;
ELEM_REMOVE(insn_element);
if (!IS_INSN_ID(iobj, send)) {
safenav_call = true;
iobj = (INSN *)get_prev_insn(iobj);
ELEM_INSERT_NEXT(&iobj->link, insn_element);
}
(pre->last = iobj->link.prev)->next = 0;
const struct rb_callinfo *ci = (struct rb_callinfo *)OPERAND_AT(iobj, 0);
int argc = vm_ci_argc(ci) + 1;
@ -5404,7 +5409,9 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
return COMPILE_NG;
}
ADD_ELEM(lhs, (LINK_ELEMENT *)iobj);
iobj->link.prev = lhs->last;
lhs->last->next = &iobj->link;
for (lhs->last = &iobj->link; lhs->last->next; lhs->last = lhs->last->next);
if (vm_ci_flag(ci) & VM_CALL_ARGS_SPLAT) {
int argc = vm_ci_argc(ci);
bool dupsplat = false;
@ -5437,10 +5444,12 @@ compile_massign_lhs(rb_iseq_t *iseq, LINK_ANCHOR *const pre, LINK_ANCHOR *const
}
INSERT_BEFORE_INSN1(iobj, line_no, node_id, pushtoarray, INT2FIX(1));
}
if (!safenav_call) {
ADD_INSN(lhs, line_node, pop);
if (argc != 1) {
ADD_INSN(lhs, line_node, pop);
}
}
for (int i=0; i < argc; i++) {
ADD_INSN(post, line_node, pop);
}

View File

@ -1248,6 +1248,20 @@ eom
assert_syntax_error("a&.x,=0", /multiple assignment destination/)
end
def test_safe_call_in_for_variable
assert_valid_syntax("for x&.bar in []; end")
assert_separately([], "#{<<~"begin;"}\n#{<<~'end;'}")
begin;
foo = nil
for foo&.bar in [1]; end
assert_nil(foo)
foo = Struct.new(:bar).new
for foo&.bar in [1]; end
assert_equal(1, foo.bar)
end;
end
def test_no_warning_logop_literal
assert_warning("") do
eval("true||raise;nil")