From 2dde243a65de51bddf505c14e9d57c8724610380 Mon Sep 17 00:00:00 2001 From: Kevin Newton Date: Wed, 13 Apr 2022 15:45:48 -0400 Subject: [PATCH] Block ampersand association 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. --- CHANGELOG.md | 4 ++++ lib/syntax_tree/parser.rb | 20 ++++++++++---------- test/fixtures/arg_block.rb | 2 ++ 3 files changed, 16 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 189509c0..1c793645 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/lib/syntax_tree/parser.rb b/lib/syntax_tree/parser.rb index 60923b57..77660f48 100644 --- a/lib/syntax_tree/parser.rb +++ b/lib/syntax_tree/parser.rb @@ -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) @@ -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) diff --git a/test/fixtures/arg_block.rb b/test/fixtures/arg_block.rb index d423efa8..3e8524ce 100644 --- a/test/fixtures/arg_block.rb +++ b/test/fixtures/arg_block.rb @@ -18,3 +18,5 @@ def foo(&) bar(&) end +% # https://github.com/ruby-syntax-tree/syntax_tree/issues/45 +foo.instance_exec(&T.must(block))