Skip to content

Commit

Permalink
Preserve multiline semantics on Style/SymbolArray and `Style/WordAr…
Browse files Browse the repository at this point in the history
…ray`

To fix #10455.

Note that this commit also changes the offense message like this:

```ruby
\# Before
%w(
^^^ Use `[
  'foo',
  'bar'
]` for an array of words.
  foo
  bar
)

\# After
%w(
^^^ Use `[...]` for an array of words.
  foo
  bar
)
```

This means that the auto-corrected code no longer appears in the offense message.
I think this is a reasonable change because the message to enforce the percent styles (`'Use `%w` or `%W` for an array of words.'`) is already using this format.
  • Loading branch information
r7kamura authored and bbatsov committed Jul 7, 2022
1 parent 0ad09e7 commit d1cb20e
Show file tree
Hide file tree
Showing 6 changed files with 102 additions and 7 deletions.
1 change: 1 addition & 0 deletions changelog/change_preserve_multiline_on_stylesymbolarray.md
@@ -0,0 +1 @@
* [#10784](https://github.com/rubocop/rubocop/pull/10784): Preserve multiline semantics on `Style/SymbolArray` and `Style/WordArray`. ([@r7kamura][])
62 changes: 61 additions & 1 deletion lib/rubocop/cop/mixin/percent_array.rb
Expand Up @@ -44,13 +44,26 @@ def check_percent_array(node)
no_acceptable_style! if brackets_required

bracketed_array = build_bracketed_array(node)
message = format(self.class::ARRAY_MSG, prefer: bracketed_array)
message = build_message_for_bracketed_array(bracketed_array)

add_offense(node, message: message) do |corrector|
corrector.replace(node, bracketed_array)
end
end

# @param [String] preferred_array_code
# @return [String]
def build_message_for_bracketed_array(preferred_array_code)
format(
self.class::ARRAY_MSG,
prefer: if preferred_array_code.include?("\n")
'an array literal `[...]`'
else
"`#{preferred_array_code}`"
end
)
end

def check_bracketed_array(node, literal_prefix)
return if allowed_bracket_array?(node)

Expand All @@ -63,6 +76,53 @@ def check_bracketed_array(node, literal_prefix)
percent_literal_corrector.correct(corrector, node, literal_prefix)
end
end

# @param [RuboCop::AST::ArrayNode] node
# @param [Array<String>] elements
# @return [String]
def build_bracketed_array_with_appropriate_whitespace(elements:, node:)
[
'[',
whitespace_leading(node),
elements.join(",#{whitespace_between(node)}"),
whitespace_trailing(node),
']'
].join
end

# Provides whitespace between elements for building a bracketed array.
# %w[ a b c ]
# ^^^
# @param [RuboCop::AST::ArrayNode] node
# @return [String]
def whitespace_between(node)
if node.children.length >= 2
node.source[
node.children[0].location.expression.end_pos...
node.children[1].location.expression.begin_pos
]
else
' '
end
end

# Provides leading whitespace for building a bracketed array.
# %w[ a b c ]
# ^^
# @param [RuboCop::AST::ArrayNode] node
# @return [String]
def whitespace_leading(node)
node.source[node.location.begin.end_pos...node.children[0].location.expression.begin_pos]
end

# Provides trailing whitespace for building a bracketed array.
# %w[ a b c ]
# ^^^^
# @param [RuboCop::AST::ArrayNode] node
# @return [String]
def whitespace_trailing(node)
node.source[node.children[-1].location.expression.end_pos...node.location.end.begin_pos]
end
end
end
end
5 changes: 2 additions & 3 deletions lib/rubocop/cop/style/symbol_array.rb
Expand Up @@ -39,7 +39,7 @@ class SymbolArray < Base
minimum_target_ruby_version 2.0

PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'
ARRAY_MSG = 'Use `%<prefer>s` for an array of symbols.'
ARRAY_MSG = 'Use %<prefer>s for an array of symbols.'

class << self
attr_accessor :largest_brackets
Expand Down Expand Up @@ -74,8 +74,7 @@ def build_bracketed_array(node)
to_symbol_literal(c.value.to_s)
end
end

"[#{syms.join(', ')}]"
build_bracketed_array_with_appropriate_whitespace(elements: syms, node: node)
end

def to_symbol_literal(string)
Expand Down
5 changes: 2 additions & 3 deletions lib/rubocop/cop/style/word_array.rb
Expand Up @@ -44,7 +44,7 @@ class WordArray < Base
extend AutoCorrector

PERCENT_MSG = 'Use `%w` or `%W` for an array of words.'
ARRAY_MSG = 'Use `%<prefer>s` for an array of words.'
ARRAY_MSG = 'Use %<prefer>s for an array of words.'

class << self
attr_accessor :largest_brackets
Expand Down Expand Up @@ -92,8 +92,7 @@ def build_bracketed_array(node)
to_string_literal(word.children[0])
end
end

"[#{words.join(', ')}]"
build_bracketed_array_with_appropriate_whitespace(elements: words, node: node)
end
end
end
Expand Down
19 changes: 19 additions & 0 deletions spec/rubocop/cop/style/symbol_array_spec.rb
Expand Up @@ -220,6 +220,25 @@
RUBY
end

it 'autocorrects multiline %i array' do
expect_offense(<<~RUBY)
%i(
^^^ Use an array literal `[...]` for an array of symbols.
one
two
three
)
RUBY

expect_correction(<<~RUBY)
[
:one,
:two,
:three
]
RUBY
end

it 'autocorrects an array has interpolations' do
expect_offense(<<~'RUBY')
%I(#{foo} #{foo}bar foo#{bar} foo)
Expand Down
17 changes: 17 additions & 0 deletions spec/rubocop/cop/style/word_array_spec.rb
Expand Up @@ -416,6 +416,23 @@
RUBY
end

it 'autocorrects multiline %w() array' do
expect_offense(<<~'RUBY')
%w(
^^^ Use an array literal `[...]` for an array of words.
foo
bar
)
RUBY

expect_correction(<<~'RUBY')
[
'foo',
'bar'
]
RUBY
end

it 'autocorrects a %W() array which uses string with hyphen' do
expect_offense(<<~'RUBY')
%W(foo bar #{foo}-bar)
Expand Down

0 comments on commit d1cb20e

Please sign in to comment.