diff --git a/changelog/change_update_stylewordarray_to_register_an.md b/changelog/change_update_stylewordarray_to_register_an.md new file mode 100644 index 00000000000..aa2d22dc76e --- /dev/null +++ b/changelog/change_update_stylewordarray_to_register_an.md @@ -0,0 +1 @@ +* [#9962](https://github.com/rubocop/rubocop/issues/9962): Update `Style/WordArray` to register an offense in `percent` style if any values contain spaces. ([@dvandersluis][]) diff --git a/config/default.yml b/config/default.yml index 300f5479581..0963ae99322 100644 --- a/config/default.yml +++ b/config/default.yml @@ -4936,7 +4936,7 @@ Style/WordArray: StyleGuide: '#percent-w' Enabled: true VersionAdded: '0.9' - VersionChanged: '0.36' + VersionChanged: '<>' EnforcedStyle: percent SupportedStyles: # percent style: %w(word1 word2) diff --git a/lib/rubocop/cop/mixin/percent_array.rb b/lib/rubocop/cop/mixin/percent_array.rb index b817632bdbb..63c055811a8 100644 --- a/lib/rubocop/cop/mixin/percent_array.rb +++ b/lib/rubocop/cop/mixin/percent_array.rb @@ -18,15 +18,16 @@ def invalid_percent_array_context?(node) !parent.parenthesized? && parent&.block_literal? end + # Override to determine values that are invalid in a percent array + def invalid_percent_array_contents?(_node) + false + end + def allowed_bracket_array?(node) comments_in_array?(node) || below_array_length?(node) || invalid_percent_array_context?(node) end - def message(_node) - style == :percent ? self.class::PERCENT_MSG : self.class::ARRAY_MSG - end - def comments_in_array?(node) line_span = node.source_range.first_line...node.source_range.last_line processed_source.each_comment_in_lines(line_span).any? @@ -35,9 +36,11 @@ def comments_in_array?(node) def check_percent_array(node) array_style_detected(:percent, node.values.size) - return unless style == :brackets + return unless style == :brackets || invalid_percent_array_contents?(node) - add_offense(node) { |corrector| correct_bracketed(corrector, node) } + add_offense(node, message: self.class::ARRAY_MSG) do |corrector| + correct_bracketed(corrector, node) + end end def check_bracketed_array(node, literal_prefix) @@ -47,7 +50,7 @@ def check_bracketed_array(node, literal_prefix) return unless style == :percent - add_offense(node) do |corrector| + add_offense(node, message: self.class::PERCENT_MSG) do |corrector| percent_literal_corrector = PercentLiteralCorrector.new(@config, @preferred_delimiters) percent_literal_corrector.correct(corrector, node, literal_prefix) end diff --git a/lib/rubocop/cop/style/word_array.rb b/lib/rubocop/cop/style/word_array.rb index 2c2ab2acd86..c461045fa49 100644 --- a/lib/rubocop/cop/style/word_array.rb +++ b/lib/rubocop/cop/style/word_array.rb @@ -9,6 +9,9 @@ module Style # Alternatively, it can check for uses of the %w() syntax, in projects # which do not want to include that syntax. # + # NOTE: When using the `percent` style, %w() arrays containing a space + # will be registered as offenses. + # # Configuration option: MinSize # If set, arrays with fewer elements than this value will not trigger the # cop. For example, a `MinSize` of `3` will not enforce a style on an @@ -21,12 +24,18 @@ module Style # # bad # ['foo', 'bar', 'baz'] # + # # bad (contains spaces) + # %w[foo\ bar baz\ quux] + # # @example EnforcedStyle: brackets # # good # ['foo', 'bar', 'baz'] # # # bad # %w[foo bar baz] + # + # # good (contains spaces) + # ['foo bar', 'baz quux'] class WordArray < Base include ArrayMinSize include ArraySyntax @@ -53,13 +62,22 @@ def on_array(node) private - def complex_content?(strings) + def complex_content?(strings, complex_regex: word_regex) strings.any? do |s| + next unless s.str_content + string = s.str_content.dup.force_encoding(::Encoding::UTF_8) - !string.valid_encoding? || !word_regex.match?(string) || / /.match?(string) + !string.valid_encoding? || + (complex_regex && !complex_regex.match?(string)) || + / /.match?(string) end end + def invalid_percent_array_contents?(node) + # Disallow %w() arrays that contain invalid encoding or spaces + complex_content?(node.values, complex_regex: false) + end + def word_regex Regexp.new(cop_config['WordRegex']) end diff --git a/spec/rubocop/cop/style/word_array_spec.rb b/spec/rubocop/cop/style/word_array_spec.rb index 3912e94cef7..b731b6744f4 100644 --- a/spec/rubocop/cop/style/word_array_spec.rb +++ b/spec/rubocop/cop/style/word_array_spec.rb @@ -317,6 +317,34 @@ _investigate(cop2, parse_source('%w(a b c)')) expect(cop2.config_to_allow_offenses).to eq('EnforcedStyle' => 'percent', 'MinSize' => 3) end + + it 'registers an offense for a %w() array containing spaces' do + expect_offense(<<~'RUBY') + %w(one\ two three\ four) + ^^^^^^^^^^^^^^^^^^^^^^^^ Use `[]` for an array of words. + RUBY + + expect_correction(<<~RUBY) + ['one two', 'three four'] + RUBY + end + + it 'does not register an offense for a %w() array containing non word characters' do + expect_no_offenses(<<~RUBY) + %w(% %i %I %q %Q %r %s %w %W %x) + RUBY + end + + it 'corrects properly when there is an extra trailing comma' do + expect_offense(<<~RUBY) + A = ["one", "two",] + ^^^^^^^^^^^^^^^ Use `%w` or `%W` for an array of words. + RUBY + + expect_correction(<<~RUBY) + A = %w(one two) + RUBY + end end context 'when EnforcedStyle is array' do @@ -338,6 +366,10 @@ expect_no_offenses("['foo', 'bar', 'foo-bar']") end + it 'does not register an offense for arrays of strings with spaces' do + expect_no_offenses("['foo bar', 'baz quux']") + end + it 'registers an offense for a %w() array' do expect_offense(<<~RUBY) %w(one two three)