diff --git a/changelog/fix_fix_an_infinite_loop_for_layout_space_inside_block_braces.md b/changelog/fix_fix_an_infinite_loop_for_layout_space_inside_block_braces.md new file mode 100644 index 00000000000..5513d996103 --- /dev/null +++ b/changelog/fix_fix_an_infinite_loop_for_layout_space_inside_block_braces.md @@ -0,0 +1 @@ +* [#10958](https://github.com/rubocop/rubocop/issues/10958): Fix an infinite loop for `Layout/SpaceInsideBlockBraces` when `EnforcedStyle` is `no_space` and using multiline block. ([@ydah][]) diff --git a/lib/rubocop/cop/layout/space_inside_block_braces.rb b/lib/rubocop/cop/layout/space_inside_block_braces.rb index b42696a5ccb..99d75d0ae67 100644 --- a/lib/rubocop/cop/layout/space_inside_block_braces.rb +++ b/lib/rubocop/cop/layout/space_inside_block_braces.rb @@ -131,7 +131,7 @@ def braces_with_contents_inside(node, inner) args_delimiter = node.arguments.loc.begin if node.block_type? # Can be ( | or nil. check_left_brace(inner, node.loc.begin, args_delimiter) - check_right_brace(inner, node.loc.begin, node.loc.end, node.single_line?) + check_right_brace(node, inner, node.loc.begin, node.loc.end, node.single_line?) end def check_left_brace(inner, left_brace, args_delimiter) @@ -142,14 +142,15 @@ def check_left_brace(inner, left_brace, args_delimiter) end end - def check_right_brace(inner, left_brace, right_brace, single_line) + def check_right_brace(node, inner, left_brace, right_brace, single_line) if single_line && /\S$/.match?(inner) no_space(right_brace.begin_pos, right_brace.end_pos, 'Space missing inside }.') else + column = node.loc.expression.column return if multiline_block?(left_brace, right_brace) && - aligned_braces?(left_brace, right_brace) + aligned_braces?(inner, right_brace, column) - space_inside_right_brace(right_brace) + space_inside_right_brace(inner, right_brace, column) end end @@ -157,8 +158,12 @@ def multiline_block?(left_brace, right_brace) left_brace.first_line != right_brace.first_line end - def aligned_braces?(left_brace, right_brace) - left_brace.first_line == right_brace.last_column + def aligned_braces?(inner, right_brace, column) + column == right_brace.column || column == inner_last_space_count(inner) + end + + def inner_last_space_count(inner) + inner.split("\n").last.count(' ') end def no_space_inside_left_brace(left_brace, args_delimiter) @@ -197,10 +202,21 @@ def pipe?(args_delimiter) args_delimiter&.is?('|') end - def space_inside_right_brace(right_brace) + def space_inside_right_brace(inner, right_brace, column) brace_with_space = range_with_surrounding_space(right_brace, side: :left) - space(brace_with_space.begin_pos, brace_with_space.end_pos - 1, - 'Space inside } detected.') + begin_pos = brace_with_space.begin_pos + end_pos = brace_with_space.end_pos - 1 + + if brace_with_space.source.match?(/\R/) + begin_pos = end_pos - (right_brace.column - column) + end + + if inner.end_with?(']') + end_pos -= 1 + begin_pos = end_pos - (inner_last_space_count(inner) - column) + end + + space(begin_pos, end_pos, 'Space inside } detected.') end def no_space(begin_pos, end_pos, msg) diff --git a/spec/rubocop/cop/layout/space_inside_block_braces_spec.rb b/spec/rubocop/cop/layout/space_inside_block_braces_spec.rb index 735fbc30392..ecc4b68353b 100644 --- a/spec/rubocop/cop/layout/space_inside_block_braces_spec.rb +++ b/spec/rubocop/cop/layout/space_inside_block_braces_spec.rb @@ -387,8 +387,37 @@ expect_offense(<<~RUBY) items.map {|item| item.do_something - ^{} Space inside } detected. } + ^^ Space inside } detected. + RUBY + + expect_correction(<<~RUBY) + items.map {|item| + item.do_something + } + RUBY + end + + it 'accepts when braces are aligned in multiline block with bracket' do + expect_no_offenses(<<~RUBY) + foo {[ + bar + ]} + RUBY + end + + it 'registers an offense when braces are not aligned in multiline block with bracket' do + expect_offense(<<~RUBY) + foo {[ + bar + ]} + ^^ Space inside } detected. + RUBY + + expect_correction(<<~RUBY) + foo {[ + bar + ]} RUBY end end