0001-required-keyword-arguments.patch

Nobuyoshi Nakada, 02/16/2013 01:18 AM

Download (7.82 KB)

View differences:

compile.c
1183 1183
	if (args->kw_args) {
1184 1184
	    NODE *node = args->kw_args;
1185 1185
	    VALUE keywords = rb_ary_tmp_new(1);
1186
	    int i = 0, j;
1186
	    VALUE required = 0;
1187
	    int i = 0, j, r = 0;
1187 1188

  
1188 1189
	    iseq->arg_keyword = get_dyna_var_idx_at_raw(iseq, args->kw_rest_arg->nd_vid);
1189 1190
	    COMPILE(optargs, "kwarg", args->kw_rest_arg);
1190 1191
	    while (node) {
1191
		rb_ary_push(keywords, INT2FIX(node->nd_body->nd_vid));
1192
		VALUE list = keywords;
1193
		if (node->nd_body->nd_value == (NODE *)-1) {
1194
		    ++r;
1195
		    if (!required) required = rb_ary_tmp_new(1);
1196
		    list = required;
1197
		}
1198
		rb_ary_push(list, INT2FIX(node->nd_body->nd_vid));
1192 1199
		COMPILE_POPED(optargs, "kwarg", node); /* nd_type(node) == NODE_KW_ARG */
1193 1200
		node = node->nd_next;
1194 1201
		i += 1;
1195 1202
	    }
1196 1203
	    iseq->arg_keyword_check = (args->kw_rest_arg->nd_vid & ID_SCOPE_MASK) == ID_JUNK;
1197 1204
	    iseq->arg_keywords = i;
1205
	    iseq->arg_keyword_required = r;
1198 1206
	    iseq->arg_keyword_table = ALLOC_N(ID, i);
1207
	    if (r) {
1208
		rb_ary_concat(required, keywords);
1209
		keywords = required;
1210
	    }
1199 1211
	    for (j = 0; j < i; j++) {
1200 1212
		iseq->arg_keyword_table[j] = FIX2INT(RARRAY_PTR(keywords)[j]);
1201 1213
	    }
......
5179 5191
      }
5180 5192
      case NODE_KW_ARG:{
5181 5193
	LABEL *default_label = NEW_LABEL(nd_line(node));
5182
	LABEL *end_label = NEW_LABEL(nd_line(node));
5194
	LABEL *end_label = 0;
5183 5195
	int idx, lv, ls;
5184 5196
	ID id = node->nd_body->nd_vid;
5185 5197

  
......
5203 5215
	  default:
5204 5216
	    rb_bug("iseq_compile_each (NODE_KW_ARG): unknown node: %s", ruby_node_name(nd_type(node->nd_body)));
5205 5217
	}
5206
	ADD_INSNL(ret, nd_line(node), jump, end_label);
5218
	if (node->nd_body->nd_value != (NODE *)-1) {
5219
	    end_label = NEW_LABEL(nd_line(node));
5220
	    ADD_INSNL(ret, nd_line(node), jump, end_label);
5221
	}
5207 5222
	ADD_LABEL(ret, default_label);
5208
	COMPILE_POPED(ret, "keyword default argument", node->nd_body);
5209
	ADD_LABEL(ret, end_label);
5223
	if (node->nd_body->nd_value != (NODE *)-1) {
5224
	    COMPILE_POPED(ret, "keyword default argument", node->nd_body);
5225
	    ADD_LABEL(ret, end_label);
5226
	}
5210 5227
	break;
5211 5228
      }
5212 5229
      case NODE_DSYM:{
parse.y
4613 4613
			$$ = rb_assoc_new($$, $2);
4614 4614
		    %*/
4615 4615
		    }
4616
		| tLABEL
4617
		    {
4618
			arg_var(formal_argument(get_id($1)));
4619
			$$ = assignable($1, (NODE *)-1);
4620
		    /*%%%*/
4621
			$$ = NEW_KW_ARG(0, $$);
4622
		    /*%
4623
			$$ = rb_assoc_new($$, 0);
4624
		    %*/
4625
		    }
4616 4626
		;
4617 4627

  
4618 4628
f_block_kw	: tLABEL primary_value
......
4625 4635
			$$ = rb_assoc_new($$, $2);
4626 4636
		    %*/
4627 4637
		    }
4638
		| tLABEL
4639
		    {
4640
			arg_var(formal_argument(get_id($1)));
4641
			$$ = assignable($1, (NODE *)-1);
4642
		    /*%%%*/
4643
			$$ = NEW_KW_ARG(0, $$);
4644
		    /*%
4645
			$$ = rb_assoc_new($$, 0);
4646
		    %*/
4647
		    }
4628 4648
		;
4629 4649

  
4630 4650
f_block_kwarg	: f_block_kw
test/ruby/test_keyword.rb
274 274
    assert_valid_syntax("def bug7662(*, **) end")
275 275
    assert_valid_syntax("def bug7662(a, **) end")
276 276
  end
277

  
278
  def test_required_keyword
279
    feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
280
    o = Object.new
281
    assert_nothing_raised(SyntaxError, feature7701) do
282
      eval("def o.foo(a:) a; end")
283
    end
284
    assert_raise(ArgumentError, feature7701) {o.foo}
285
    assert_equal(42, o.foo(a: 42), feature7701)
286
  end
287

  
288
  def test_block_required_keyword
289
    feature7701 = '[ruby-core:51454] [Feature #7701] required keyword argument'
290
    b = assert_nothing_raised(SyntaxError, feature7701) do
291
      break eval("proc {|a:| a}")
292
    end
293
    assert_raise(ArgumentError, feature7701) {b.call}
294
    assert_equal(42, b.call(a: 42), feature7701)
295
  end
277 296
end
vm_core.h
273 273
    int arg_keyword;
274 274
    int arg_keyword_check; /* if this is true, raise an ArgumentError when unknown keyword argument is passed */
275 275
    int arg_keywords;
276
    int arg_keyword_required;
276 277
    ID *arg_keyword_table;
277 278

  
278 279
    size_t stack_max; /* for stack overflow check */
vm_insnhelper.c
143 143
    rb_exc_raise(exc);
144 144
}
145 145

  
146
NORETURN(static void keyword_error(const char *error, VALUE keys));
147
static void
148
keyword_error(const char *error, VALUE keys)
149
{
150
    const char *msg = RARRAY_LEN(keys) == 1 ? "" : "s";
151
    keys = rb_ary_join(keys, rb_usascii_str_new2(", "));
152
    rb_raise(rb_eArgError, "%s keyword%s: %"PRIsVALUE, error, msg, keys);
153
}
154

  
146 155
NORETURN(static void unknown_keyword_error(const rb_iseq_t *iseq, VALUE hash));
147 156
static void
148 157
unknown_keyword_error(const rb_iseq_t *iseq, VALUE hash)
149 158
{
150
    VALUE sep = rb_usascii_str_new2(", "), keys;
151
    const char *msg;
159
    VALUE keys;
152 160
    int i;
153 161
    for (i = 0; i < iseq->arg_keywords; i++) {
154 162
	rb_hash_delete(hash, ID2SYM(iseq->arg_keyword_table[i]));
155 163
    }
156 164
    keys = rb_funcall(hash, rb_intern("keys"), 0, 0);
157 165
    if (!RB_TYPE_P(keys, T_ARRAY)) rb_raise(rb_eArgError, "unknown keyword");
158
    msg = RARRAY_LEN(keys) == 1 ? "" : "s";
159
    keys = rb_funcall(keys, rb_intern("join"), 1, sep);
160
    rb_raise(rb_eArgError, "unknown keyword%s: %"PRIsVALUE, msg, keys);
166
    keyword_error("unknown", keys);
161 167
}
162 168

  
163 169
void
......
1075 1081
	argc--;
1076 1082
	keyword_hash = rb_hash_dup(keyword_hash);
1077 1083
	if (iseq->arg_keyword_check) {
1078
	    for (i = j = 0; i < iseq->arg_keywords; i++) {
1084
	    VALUE missing = Qnil;
1085
	    for (i = 0; i < iseq->arg_keyword_required; i++) {
1086
		if (st_lookup(RHASH_TBL(keyword_hash), ID2SYM(iseq->arg_keyword_table[i]), 0))
1087
		    continue;
1088
		if (NIL_P(missing)) missing = rb_ary_tmp_new(1);
1089
		rb_ary_push(missing, ID2SYM(iseq->arg_keyword_table[i]));
1090
	    }
1091
	    if (!NIL_P(missing)) {
1092
		keyword_error("missing", missing);
1093
	    }
1094
	    for (j = i; i < iseq->arg_keywords; i++) {
1079 1095
		if (st_lookup(RHASH_TBL(keyword_hash), ID2SYM(iseq->arg_keyword_table[i]), 0)) j++;
1080 1096
	    }
1081 1097
	    if (RHASH_TBL(keyword_hash)->num_entries > (unsigned int) j) {
......
1083 1099
	    }
1084 1100
	}
1085 1101
    }
1102
    else if (iseq->arg_keyword_check && iseq->arg_keyword_required) {
1103
	VALUE missing = rb_ary_tmp_new(iseq->arg_keyword_required);
1104
	for (i = 0; i < iseq->arg_keyword_required; i++) {
1105
	    rb_ary_push(missing, ID2SYM(iseq->arg_keyword_table[i]));
1106
	}
1107
	keyword_error("missing", missing);
1108
    }
1086 1109
    else {
1087 1110
	keyword_hash = rb_hash_new();
1088 1111
    }
1089
-