mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00
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 mame@ruby-lang.org Co-authored-by: Nobuyoshi Nakada nobu@ruby-lang.org
This commit is contained in:
parent
ea02b93bb9
commit
4adb012926
Notes:
git
2021-11-19 07:18:18 +09:00
6 changed files with 70 additions and 2 deletions
10
NEWS.md
10
NEWS.md
|
@ -7,6 +7,15 @@ Note that each entry is kept to a minimum, see links for details.
|
||||||
|
|
||||||
## Language changes
|
## Language changes
|
||||||
|
|
||||||
|
* The block arguments can be now be anonymous, if the block will
|
||||||
|
only be passed to another method. [[Feature #11256]]
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
def foo(&)
|
||||||
|
bar(&)
|
||||||
|
end
|
||||||
|
```
|
||||||
|
|
||||||
* Pin operator now takes an expression. [[Feature #17411]]
|
* Pin operator now takes an expression. [[Feature #17411]]
|
||||||
|
|
||||||
```ruby
|
```ruby
|
||||||
|
@ -412,6 +421,7 @@ See [the repository](https://github.com/ruby/error_highlight) in detail.
|
||||||
|
|
||||||
[Bug #4443]: https://bugs.ruby-lang.org/issues/4443
|
[Bug #4443]: https://bugs.ruby-lang.org/issues/4443
|
||||||
[Feature #6210]: https://bugs.ruby-lang.org/issues/6210
|
[Feature #6210]: https://bugs.ruby-lang.org/issues/6210
|
||||||
|
[Feature #11256]: https://bugs.ruby-lang.org/issues/11256
|
||||||
[Feature #12194]: https://bugs.ruby-lang.org/issues/12194
|
[Feature #12194]: https://bugs.ruby-lang.org/issues/12194
|
||||||
[Feature #12495]: https://bugs.ruby-lang.org/issues/12495
|
[Feature #12495]: https://bugs.ruby-lang.org/issues/12495
|
||||||
[Feature #14256]: https://bugs.ruby-lang.org/issues/14256
|
[Feature #14256]: https://bugs.ruby-lang.org/issues/14256
|
||||||
|
|
|
@ -515,8 +515,15 @@ Most frequently the block argument is used to pass a block to another method:
|
||||||
@items.each(&block)
|
@items.each(&block)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
You are not required to give a name to the block if you will just be passing
|
||||||
|
it to another method:
|
||||||
|
|
||||||
|
def each_item(&)
|
||||||
|
@items.each(&)
|
||||||
|
end
|
||||||
|
|
||||||
If you are only going to call the block and will not otherwise manipulate it
|
If you are only going to call the block and will not otherwise manipulate it
|
||||||
or send it to another method using <code>yield</code> without an explicit
|
or send it to another method, using <code>yield</code> without an explicit
|
||||||
block parameter is preferred. This method is equivalent to the first method
|
block parameter is preferred. This method is equivalent to the first method
|
||||||
in this section:
|
in this section:
|
||||||
|
|
||||||
|
|
21
parse.y
21
parse.y
|
@ -427,6 +427,8 @@ static void token_info_drop(struct parser_params *p, const char *token, rb_code_
|
||||||
|
|
||||||
#define lambda_beginning_p() (p->lex.lpar_beg == p->lex.paren_nest)
|
#define lambda_beginning_p() (p->lex.lpar_beg == p->lex.paren_nest)
|
||||||
|
|
||||||
|
#define ANON_BLOCK_ID '&'
|
||||||
|
|
||||||
static enum yytokentype yylex(YYSTYPE*, YYLTYPE*, struct parser_params*);
|
static enum yytokentype yylex(YYSTYPE*, YYLTYPE*, struct parser_params*);
|
||||||
|
|
||||||
#ifndef RIPPER
|
#ifndef RIPPER
|
||||||
|
@ -2846,6 +2848,17 @@ block_arg : tAMPER arg_value
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper: $2 %*/
|
/*% ripper: $2 %*/
|
||||||
}
|
}
|
||||||
|
| tAMPER
|
||||||
|
{
|
||||||
|
/*%%%*/
|
||||||
|
if (!local_id(p, ANON_BLOCK_ID)) {
|
||||||
|
compile_error(p, "no anonymous block parameter");
|
||||||
|
}
|
||||||
|
$$ = NEW_BLOCK_PASS(NEW_LVAR(ANON_BLOCK_ID, &@1), &@$);
|
||||||
|
/*%
|
||||||
|
$$ = Qnil;
|
||||||
|
%*/
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_block_arg : ',' block_arg
|
opt_block_arg : ',' block_arg
|
||||||
|
@ -5541,6 +5554,14 @@ f_block_arg : blkarg_mark tIDENTIFIER
|
||||||
/*% %*/
|
/*% %*/
|
||||||
/*% ripper: blockarg!($2) %*/
|
/*% ripper: blockarg!($2) %*/
|
||||||
}
|
}
|
||||||
|
| blkarg_mark
|
||||||
|
{
|
||||||
|
/*%%%*/
|
||||||
|
arg_var(p, shadowing_lvar(p, get_id(ANON_BLOCK_ID)));
|
||||||
|
/*%
|
||||||
|
$$ = dispatch1(blockarg, Qnil);
|
||||||
|
%*/
|
||||||
|
}
|
||||||
;
|
;
|
||||||
|
|
||||||
opt_f_block_arg : ',' f_block_arg
|
opt_f_block_arg : ',' f_block_arg
|
||||||
|
|
|
@ -125,6 +125,16 @@ class TestISeq < Test::Unit::TestCase
|
||||||
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
|
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_super_with_anonymous_block
|
||||||
|
iseq = compile(<<~EOF)
|
||||||
|
def touch3(&block) # :nodoc:
|
||||||
|
foo { super }
|
||||||
|
end
|
||||||
|
42
|
||||||
|
EOF
|
||||||
|
assert_equal(42, ISeq.load_from_binary(iseq.to_binary).eval)
|
||||||
|
end
|
||||||
|
|
||||||
def test_ractor_unshareable_outer_variable
|
def test_ractor_unshareable_outer_variable
|
||||||
name = "\u{2603 26a1}"
|
name = "\u{2603 26a1}"
|
||||||
y = eval("proc {#{name} = nil; proc {|x| #{name} = x}}").call
|
y = eval("proc {#{name} = nil; proc {|x| #{name} = x}}").call
|
||||||
|
@ -373,6 +383,14 @@ class TestISeq < Test::Unit::TestCase
|
||||||
assert_equal [2], param_names
|
assert_equal [2], param_names
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def anon_block(&); end
|
||||||
|
|
||||||
|
def test_anon_block_param_in_disasm
|
||||||
|
iseq = RubyVM::InstructionSequence.of(method(:anon_block))
|
||||||
|
param_names = iseq.to_a[iseq.to_a.index(:method) + 1]
|
||||||
|
assert_equal [:&], param_names
|
||||||
|
end
|
||||||
|
|
||||||
def strip_lineno(source)
|
def strip_lineno(source)
|
||||||
source.gsub(/^.*?: /, "")
|
source.gsub(/^.*?: /, "")
|
||||||
end
|
end
|
||||||
|
|
|
@ -1044,7 +1044,7 @@ x = __ENCODING__
|
||||||
end;
|
end;
|
||||||
|
|
||||||
assert_syntax_error("def\nf(000)end", /^ \^~~/)
|
assert_syntax_error("def\nf(000)end", /^ \^~~/)
|
||||||
assert_syntax_error("def\nf(&)end", /^ \^/)
|
assert_syntax_error("def\nf(&0)end", /^ \^/)
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_method_location_in_rescue
|
def test_method_location_in_rescue
|
||||||
|
|
|
@ -66,6 +66,18 @@ class TestSyntax < Test::Unit::TestCase
|
||||||
f&.close!
|
f&.close!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def test_anonymous_block_forwarding
|
||||||
|
assert_syntax_error("def b; c(&); end", /no anonymous block parameter/)
|
||||||
|
assert_separately([], "#{<<-"begin;"}\n#{<<-'end;'}")
|
||||||
|
begin;
|
||||||
|
def b(&); c(&) end
|
||||||
|
def c(&); yield 1 end
|
||||||
|
a = nil
|
||||||
|
b{|c| a = c}
|
||||||
|
assert_equal(1, a)
|
||||||
|
end;
|
||||||
|
end
|
||||||
|
|
||||||
def test_newline_in_block_parameters
|
def test_newline_in_block_parameters
|
||||||
bug = '[ruby-dev:45292]'
|
bug = '[ruby-dev:45292]'
|
||||||
["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params|
|
["", "a", "a, b"].product(["", ";x", [";", "x"]]) do |params|
|
||||||
|
|
Loading…
Add table
Reference in a new issue