From c8e705823f544fe88e2e112cc1a5a27731dfd105 Mon Sep 17 00:00:00 2001 From: Genadi Samokovarov Date: Thu, 25 Mar 2021 14:10:22 +0200 Subject: [PATCH] Parenthesized forwarded args in Style/MethodCallWithArgsParentheses 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. --- ...style_method_call_with_args_parentheses.md | 1 + .../omit_parentheses.rb | 8 +++----- .../method_call_with_args_parentheses_spec.rb | 20 +++++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 changelog/fix_parentheses_in_forwarded_args_for_style_method_call_with_args_parentheses.md diff --git a/changelog/fix_parentheses_in_forwarded_args_for_style_method_call_with_args_parentheses.md b/changelog/fix_parentheses_in_forwarded_args_for_style_method_call_with_args_parentheses.md new file mode 100644 index 00000000000..535227f8023 --- /dev/null +++ b/changelog/fix_parentheses_in_forwarded_args_for_style_method_call_with_args_parentheses.md @@ -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][]) diff --git a/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb b/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb index 42e8dd863c9..b13e9da9536 100644 --- a/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +++ b/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb @@ -5,7 +5,7 @@ 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.' @@ -13,7 +13,6 @@ module OmitParentheses private - # rubocop:disable Metrics/CyclomaticComplexity def omit_parentheses(node) return unless node.parenthesized? return if inside_endless_method_def?(node) @@ -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) @@ -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 @@ -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 diff --git a/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb b/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb index 1f099178d9f..47424de2783 100644 --- a/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb +++ b/spec/rubocop/cop/style/method_call_with_args_parentheses_spec.rb @@ -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 = ' '