Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 4adb012

Browse files
committed
Anonymous block forwarding allows a method to forward a passed
block to another method without having to provide a name for the block parameter. Implements [Feature #11256] Co-authored-by: Yusuke Endoh [email protected] Co-authored-by: Nobuyoshi Nakada [email protected]
1 parent ea02b93 commit 4adb012

File tree

6 files changed

+70
-2
lines changed

6 files changed

+70
-2
lines changed

NEWS.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@ Note that each entry is kept to a minimum, see links for details.
77

88
## Language changes
99

10+
* The block arguments can be now be anonymous, if the block will
11+
only be passed to another method. [[Feature #11256]]
12+
13+
```ruby
14+
def foo(&)
15+
bar(&)
16+
end
17+
```
18+
1019
* Pin operator now takes an expression. [[Feature #17411]]
1120

1221
```ruby
@@ -412,6 +421,7 @@ See [the repository](https://github.com/ruby/error_highlight) in detail.
412421

413422
[Bug #4443]: https://bugs.ruby-lang.org/issues/4443
414423
[Feature #6210]: https://bugs.ruby-lang.org/issues/6210
424+
[Feature #11256]: https://bugs.ruby-lang.org/issues/11256
415425
[Feature #12194]: https://bugs.ruby-lang.org/issues/12194
416426
[Feature #12495]: https://bugs.ruby-lang.org/issues/12495
417427
[Feature #14256]: https://bugs.ruby-lang.org/issues/14256

doc/syntax/methods.rdoc

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -515,8 +515,15 @@ Most frequently the block argument is used to pass a block to another method:
515515
@items.each(&block)
516516
end
517517

518+
You are not required to give a name to the block if you will just be passing
519+
it to another method:
520+
521+
def each_item(&)
522+
@items.each(&)
523+
end
524+
518525
If you are only going to call the block and will not otherwise manipulate it
519-
or send it to another method using <code>yield</code> without an explicit
526+
or send it to another method, using <code>yield</code> without an explicit
520527
block parameter is preferred. This method is equivalent to the first method
521528
in this section:
522529

parse.y

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -427,6 +427,8 @@ static void token_info_drop(struct parser_params *p, const char *token, rb_code_
427427

428428
#define lambda_beginning_p() (p->lex.lpar_beg == p->lex.paren_nest)
429429

430+
#define ANON_BLOCK_ID '&'
431+
430432
static enum yytokentype yylex(YYSTYPE*, YYLTYPE*, struct parser_params*);
431433

432434
#ifndef RIPPER
@@ -2846,6 +2848,17 @@ block_arg : tAMPER arg_value
28462848
/*% %*/
28472849
/*% ripper: $2 %*/
28482850
}
2851+
| tAMPER
2852+
{
2853+
/*%%%*/
2854+
if (!local_id(p, ANON_BLOCK_ID)) {
2855+
compile_error(p, "no anonymous block parameter");
2856+
}
2857+
$$ = NEW_BLOCK_PASS(NEW_LVAR(ANON_BLOCK_ID, &@1), &@$);
2858+
/*%
2859+
$$ = Qnil;
2860+
%*/
2861+
}
28492862
;
28502863

28512864
opt_block_arg : ',' block_arg
@@ -5541,6 +5554,14 @@ f_block_arg : blkarg_mark tIDENTIFIER
55415554
/*% %*/
55425555
/*% ripper: blockarg!($2) %*/
55435556
}
5557+
| blkarg_mark
5558+
{
5559+
/*%%%*/
5560+
arg_var(p, shadowing_lvar(p, get_id(ANON_BLOCK_ID)));
5561+
/*%
5562+
$$ = dispatch1(blockarg, Qnil);
5563+
%*/
5564+
}
55445565
;
55455566

55465567
opt_f_block_arg : ',' f_block_arg

test/ruby/test_iseq.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,16 @@ def test_lambda_with_ractor_roundtrip
125125
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
126126
end
127127

128+
def test_super_with_anonymous_block
129+
iseq = compile(<<~EOF)
130+
def touch3(&block) # :nodoc:
131+
foo { super }
132+
end
133+
42
134+
EOF
135+
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
136+
end
137+
128138
def test_ractor_unshareable_outer_variable
129139
name = "\u{2603 26a1}"
130140
y = eval("proc {#{name} = nil; proc {|x| #{name} = x}}").call
@@ -373,6 +383,14 @@ def test_anon_param_in_disasm
373383
assert_equal [2], param_names
374384
end
375385

386+
def anon_block(&); end
387+
388+
def test_anon_block_param_in_disasm
389+
iseq = RubyVM::InstructionSequence.of(method(:anon_block))
390+
param_names = iseq.to_a[iseq.to_a.index(:method) + 1]
391+
assert_equal [:&], param_names
392+
end
393+
376394
def strip_lineno(source)
377395
source.gsub(/^.*?: /, "")
378396
end

test/ruby/test_parse.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1044,7 +1044,7 @@ def test_error_def_in_argument
10441044
end;
10451045

10461046
assert_syntax_error("def\nf(000)end", /^ \^~~/)
1047-
assert_syntax_error("def\nf(&)end", /^ \^/)
1047+
assert_syntax_error("def\nf(&0)end", /^ \^/)
10481048
end
10491049

10501050
def test_method_location_in_rescue

test/ruby/test_syntax.rb

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,18 @@ def test_script_lines
6666
f&.close!
6767
end
6868

69+
def test_anonymous_block_forwarding
70+
assert_syntax_error("def b; c(&); end", /no anonymous block parameter/)
71+
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
72+
begin;
73+
def b(&); c(&) end
74+
def c(&); yield 1 end
75+
a = nil
76+
b{|c| a = c}
77+
assert_equal(1, a)
78+
end;
79+
end
80+
6981
def test_newline_in_block_parameters
7082
bug = '[ruby-dev:45292]'
7183
["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params|

0 commit comments

Comments
 (0)