Skip to content

Block ampersand association #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Apr 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a

## [Unreleased]

### Changed

- [#45](https://github.com/ruby-syntax-tree/syntax_tree/issues/45) - Fix parsing expressions like `foo.instance_exec(&T.must(block))`, where there are two `args_add_block` calls with a single `&`. Previously it was associating the `&` with the wrong block.

## [2.1.0] - 2022-04-12

### Added
Expand Down
20 changes: 10 additions & 10 deletions lib/syntax_tree/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -414,12 +414,19 @@ def on_args_add(arguments, argument)
# (false | untyped) block
# ) -> Args
def on_args_add_block(arguments, block)
# First, see if there is an & operator that could potentially be
# associated with the block part of this args_add_block. If there is not,
# then just return the arguments.
operator = find_token(Op, "&", consume: false)

# If we can't find the & operator, then there's no block to add to the
# list, so we're just going to return the arguments as-is.
return arguments unless operator

# If there are any arguments and the operator we found from the list is
# not after them, then we're going to return the arguments as-is because
# we're looking at an & that occurs before the arguments are done.
if arguments.parts.any? && operator.location.start_char < arguments.location.end_char
return arguments
end

# Now we know we have an & operator, so we're going to delete it from the
# list of tokens to make sure it doesn't get confused with anything else.
tokens.delete(operator)
Expand All @@ -428,13 +435,6 @@ def on_args_add_block(arguments, block)
location = operator.location
location = operator.location.to(block.location) if block

# If there are any arguments and the operator we found from the list is
# not after them, then we're going to return the arguments as-is because
# we're looking at an & that occurs before the arguments are done.
if arguments.parts.any? && location.start_char < arguments.location.end_char
return arguments
end

# Otherwise, we're looking at an actual block argument (with or without a
# block, which could be missing because it could be a bare & since 3.1.0).
arg_block = ArgBlock.new(value: block, location: location)
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/arg_block.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,5 @@
def foo(&)
bar(&)
end
% # https://github.com/ruby-syntax-tree/syntax_tree/issues/45
foo.instance_exec(&T.must(block))