Skip to content

Commit

Permalink
Parenthesized forwarded args in Style/MethodCallWithArgsParentheses
Browse files Browse the repository at this point in the history
The new-style arguments delegation included in Ruby 2.7 and expanded in
Ruby 3.0 is required to be parenthesized, since the syntax is clashing
with the endless `start...` range, which can be started with `start ...`
as well. Currently, the most iffy cop of them all is issuing offenses
for:

```ruby
def delegated_call(...)
  @proxy.call(...)
             ^^^^^ Omit parentheses for method calls with arguments.
end
```

Removing the parens will start an endless range with the beginning
being the result of the method call without arguments. See the parse
trees below for more details:

```bash
-> ruby-parse -e "def foo(...); bar(...); end"
(def :foo
  (args
    (forward-arg))
  (send nil :bar
    (forwarded-args)))

-> ruby-parse -e "def foo(...); bar ...; end"
(def :foo
  (args
    (forward-arg))
  (erange
    (send nil :bar) nil))

-> ruby-parse -e "def foo(...); bar...; end"
(def :foo
  (args
    (forward-arg))
  (erange
    (send nil :bar) nil))
```

We don't want that so allow the parens and leave this gotcha to the poor
developer that's gonna run into it.
  • Loading branch information
gsamokovarov authored and bbatsov committed Apr 4, 2021
1 parent c8c3358 commit 93ecd7e
Show file tree
Hide file tree
Showing 3 changed files with 24 additions and 5 deletions.
@@ -0,0 +1 @@
* [#9637](https://github.com/rubocop/rubocop/issues/9637): Allow parentheses for forwarded args in `Style/MethodCallWithArgsParentheses`'s `omit_parentheses` style to avoid endless range ambiguity. ([@gsamokovarov][])
Expand Up @@ -5,15 +5,14 @@ module Cop
module Style
class MethodCallWithArgsParentheses
# Style omit_parentheses
# rubocop:disable Metrics/ModuleLength
# rubocop:disable Metrics/ModuleLength, Metrics/CyclomaticComplexity
module OmitParentheses
TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
OMIT_MSG = 'Omit parentheses for method calls with arguments.'
private_constant :OMIT_MSG

private

# rubocop:disable Metrics/CyclomaticComplexity
def omit_parentheses(node)
return unless node.parenthesized?
return if inside_endless_method_def?(node)
Expand All @@ -27,7 +26,6 @@ def omit_parentheses(node)
auto_correct(corrector, node)
end
end
# rubocop:enable Metrics/CyclomaticComplexity

def auto_correct(corrector, node)
if parentheses_at_the_end_of_multiline_call?(node)
Expand Down Expand Up @@ -114,7 +112,7 @@ def call_with_ambiguous_arguments?(node)
call_as_argument_or_chain?(node) ||
hash_literal_in_arguments?(node) ||
node.descendants.any? do |n|
ambigious_literal?(n) || logical_operator?(n) ||
n.forwarded_args_type? || ambigious_literal?(n) || logical_operator?(n) ||
call_with_braced_block?(n)
end
end
Expand Down Expand Up @@ -190,7 +188,7 @@ def inside_string_interpolation?(node)
node.ancestors.drop_while { |a| !a.begin_type? }.any?(&:dstr_type?)
end
end
# rubocop:enable Metrics/ModuleLength
# rubocop:enable Metrics/ModuleLength, Metrics/CyclomaticComplexity
end
end
end
Expand Down
20 changes: 20 additions & 0 deletions spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb
Expand Up @@ -361,6 +361,26 @@ module Foo

it_behaves_like 'endless methods', omit: true

context 'forwarded arguments in 2.7', :ruby27 do
it 'accepts parens for forwarded arguments' do
expect_no_offenses(<<~RUBY)
def delegated_call(...)
@proxy.call(...)
end
RUBY
end
end

context 'forwarded arguments in 3.0', :ruby30 do
it 'accepts parens for forwarded arguments' do
expect_no_offenses(<<~RUBY)
def method_missing(name, ...)
@proxy.call(name, ...)
end
RUBY
end
end

it 'register an offense for parens in method call without args' do
trailing_whitespace = ' '

Expand Down

0 comments on commit 93ecd7e

Please sign in to comment.