diff --git a/changelog/fix_fix_styleredundantparentheses_to_be_able.md b/changelog/fix_fix_styleredundantparentheses_to_be_able.md new file mode 100644 index 00000000000..1aa7bf032ec --- /dev/null +++ b/changelog/fix_fix_styleredundantparentheses_to_be_able.md @@ -0,0 +1 @@ +* [#10883](https://github.com/rubocop/rubocop/issues/10883): Fix `Style/RedundantParentheses` to be able to detect offenses and properly correct when the end parentheses and comma are on their own line. ([@dvandersluis][]) diff --git a/lib/rubocop/cop/correctors/parentheses_corrector.rb b/lib/rubocop/cop/correctors/parentheses_corrector.rb index ac618af34c9..41c5200945f 100644 --- a/lib/rubocop/cop/correctors/parentheses_corrector.rb +++ b/lib/rubocop/cop/correctors/parentheses_corrector.rb @@ -5,9 +5,13 @@ module Cop # This autocorrects parentheses class ParenthesesCorrector class << self + include RangeHelp + def correct(corrector, node) corrector.remove(node.loc.begin) corrector.remove(node.loc.end) + handle_orphaned_comma(corrector, node) + return unless ternary_condition?(node) && next_char_is_question_mark?(node) corrector.insert_after(node.loc.end, ' ') @@ -22,6 +26,30 @@ def ternary_condition?(node) def next_char_is_question_mark?(node) node.loc.last_column == node.parent.loc.question.column end + + def only_closing_paren_before_comma?(node) + source_buffer = node.source_range.source_buffer + line_range = source_buffer.line_range(node.loc.end.line) + + line_range.source.start_with?(/\s*\)\s*,/) + end + + # If removing parentheses leaves a comma on its own line, remove all the whitespace + # preceding it to prevent a syntax error. + def handle_orphaned_comma(corrector, node) + return unless only_closing_paren_before_comma?(node) + + range = range_with_surrounding_space( + range: node.loc.end, + buffer: node.source_range.source_buffer, + side: :left, + newlines: true, + whitespace: true, + continuations: true + ) + + corrector.remove(range) + end end end end diff --git a/lib/rubocop/cop/mixin/range_help.rb b/lib/rubocop/cop/mixin/range_help.rb index 6c29eae57cc..756c34215bf 100644 --- a/lib/rubocop/cop/mixin/range_help.rb +++ b/lib/rubocop/cop/mixin/range_help.rb @@ -54,11 +54,11 @@ def range_with_surrounding_comma(range, side = :both) NOT_GIVEN = Module.new def range_with_surrounding_space(range_positional = NOT_GIVEN, # rubocop:disable Metrics/ParameterLists range: NOT_GIVEN, side: :both, newlines: true, - whitespace: false, continuations: false) + whitespace: false, continuations: false, + buffer: @processed_source.buffer) range = range_positional unless range_positional == NOT_GIVEN - buffer = @processed_source.buffer src = buffer.source go_left, go_right = directions(side) diff --git a/lib/rubocop/cop/style/redundant_parentheses.rb b/lib/rubocop/cop/style/redundant_parentheses.rb index 8551c69e0be..184b3ab6eaf 100644 --- a/lib/rubocop/cop/style/redundant_parentheses.rb +++ b/lib/rubocop/cop/style/redundant_parentheses.rb @@ -57,7 +57,6 @@ def ignore_syntax?(node) def allowed_expression?(node) allowed_ancestor?(node) || allowed_method_call?(node) || - allowed_array_or_hash_element?(node) || allowed_multiple_expression?(node) || allowed_ternary?(node) end @@ -167,26 +166,6 @@ def keyword_ancestor?(node) node.parent&.keyword? end - def allowed_array_or_hash_element?(node) - # Don't flag - # ``` - # { a: (1 - # ), } - # ``` - hash_or_array_element?(node) && only_closing_paren_before_comma?(node) - end - - def hash_or_array_element?(node) - node.each_ancestor(:array, :hash).any? - end - - def only_closing_paren_before_comma?(node) - source_buffer = node.source_range.source_buffer - line_range = source_buffer.line_range(node.loc.end.line) - - /^\s*\)\s*,/.match?(line_range.source) - end - def disallowed_literal?(begin_node, node) node.literal? && !node.range_type? && !raised_to_power_negative_numeric?(begin_node, node) end diff --git a/spec/rubocop/cop/style/redundant_parentheses_spec.rb b/spec/rubocop/cop/style/redundant_parentheses_spec.rb index a0827e41b91..ad268b8b5ee 100644 --- a/spec/rubocop/cop/style/redundant_parentheses_spec.rb +++ b/spec/rubocop/cop/style/redundant_parentheses_spec.rb @@ -218,25 +218,80 @@ RUBY end - it_behaves_like 'plausible', "[(1\n),]" - - it_behaves_like 'plausible', <<~RUBY - [ - ( - 1 - ), - 2 - ] - RUBY - - it_behaves_like 'plausible', <<~RUBY - [ - x = ( - 1 - ), - y = 2 - ] - RUBY + context 'literals in an array' do + context 'when there is a comma on the same line as the closing parentheses' do + it 'registers an offense and corrects when there is no subsequent item' do + expect_offense(<<~RUBY) + [ + ( + ^ Don't use parentheses around a literal. + 1 + ) + ] + RUBY + + expect_correction(<<~RUBY) + [ + #{trailing_whitespace * 2} + 1 + #{trailing_whitespace * 2} + ] + RUBY + end + + it 'registers an offense and corrects when there is a trailing comma' do + expect_offense(<<~RUBY) + [(1 + ^^ Don't use parentheses around a literal. + ),] + RUBY + + expect_correction(<<~RUBY) + [1,] + RUBY + end + + it 'registers an offense and corrects when there is a subsequent item' do + expect_offense(<<~RUBY) + [ + ( + ^ Don't use parentheses around a literal. + 1 + ), + 2 + ] + RUBY + + expect_correction(<<~RUBY) + [ + #{trailing_whitespace * 2} + 1, + 2 + ] + RUBY + end + + it 'registers an offense and corrects when there is assignment' do + expect_offense(<<~RUBY) + [ + x = ( + ^ Don't use parentheses around a literal. + 1 + ), + y = 2 + ] + RUBY + + expect_correction(<<~RUBY) + [ + x =#{trailing_whitespace} + 1, + y = 2 + ] + RUBY + end + end + end it 'registers an offense for parens around a literal hash value' do expect_offense(<<~RUBY) @@ -262,7 +317,18 @@ RUBY end - it_behaves_like 'plausible', "{a: (1\n),}" + it 'registers an offense and corrects for a parenthesized item in a hash where ' \ + 'the comma is on a line with the closing parens' do + expect_offense(<<~RUBY) + { a: (1 + ^^ Don't use parentheses around a literal. + ),} + RUBY + + expect_correction(<<~RUBY) + { a: 1,} + RUBY + end it 'registers an offense for parens around an integer exponentiation base' do expect_offense(<<~RUBY) @@ -507,4 +573,24 @@ def x }) RUBY end + + it 'registers an offense and corrects when method arguments are unnecessarily parenthesized' do + expect_offense(<<~RUBY) + foo( + ( + ^ Don't use parentheses around a literal. + 1 + ), + 2 + ) + RUBY + + expect_correction(<<~RUBY) + foo( + #{trailing_whitespace * 2} + 1, + 2 + ) + RUBY + end end