rubocop 0.59.1 → 0.59.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c194b5622ecc91385098a2b68a32fefb80dfe3c1
4
- data.tar.gz: 6b77f223ef06685ee4af7543faed16456c38d7cb
3
+ metadata.gz: 28eeb813f59ba82f401cfac05daa5a118a1014af
4
+ data.tar.gz: 63b3201d008b9f790f93cc5a724c1a596bd75dd4
5
5
  SHA512:
6
- metadata.gz: 14a31079c85a1f77781219df27ffaeb0756ef006ca737c92a4690ff50f3cff68e407520b3d389bb03cdba4853eddcb07fa375955478547d4bfd219f8dc1e84dc
7
- data.tar.gz: cee68c85b7430c9fb32e11206d9e8b8b1f87dd3cc29b0a878daf62b91e26cbd54793e4ead915b4894e1082c5f6d7dcd8584bf5f0a96e4123df32009ca11ff438
6
+ metadata.gz: c79b61631cdc0e7fd9f0a8b17b0d04956827286239a7f65948e5a9cfec8f4a286dc1e6f062d9d0f5f2bdeaab06ff6dd2d2e10ddcd962dc6446f46ddd64bdbb5a
7
+ data.tar.gz: b348fe1df3db7a302023b32fd4fafa3ab2a4b09edd99efdfc55f3d8dba27ac18dd118eeeb008702467cd188c96104309b99800a428224de17758a6cd4d95c3eb
data/README.md CHANGED
@@ -53,7 +53,7 @@ haven't reached version 1.0 yet). To prevent an unwanted RuboCop update you
53
53
  might want to use a conservative version locking in your `Gemfile`:
54
54
 
55
55
  ```rb
56
- gem 'rubocop', '~> 0.59.1', require: false
56
+ gem 'rubocop', '~> 0.59.2', require: false
57
57
  ```
58
58
 
59
59
  ## Quickstart
@@ -67,7 +67,7 @@ $ rubocop
67
67
 
68
68
  ## Official manual
69
69
 
70
- You can read a ton more about RuboCop in its [official manual](http://docs.rubocop.org).
70
+ You can read a ton more about RuboCop in its [official manual](https://docs.rubocop.org).
71
71
 
72
72
  ## Compatibility
73
73
 
@@ -1597,6 +1597,7 @@ Metrics/LineLength:
1597
1597
  Metrics/MethodLength:
1598
1598
  CountComments: false # count full line comments?
1599
1599
  Max: 10
1600
+ ExcludedMethods: []
1600
1601
 
1601
1602
  Metrics/ModuleLength:
1602
1603
  CountComments: false # count full line comments?
@@ -130,6 +130,7 @@ require_relative 'rubocop/cop/mixin/parser_diagnostic'
130
130
  require_relative 'rubocop/cop/mixin/percent_array'
131
131
  require_relative 'rubocop/cop/mixin/percent_literal'
132
132
  require_relative 'rubocop/cop/mixin/preceding_following_alignment'
133
+ require_relative 'rubocop/cop/mixin/preferred_delimiters'
133
134
  require_relative 'rubocop/cop/mixin/rescue_node'
134
135
  require_relative 'rubocop/cop/mixin/safe_assignment'
135
136
  require_relative 'rubocop/cop/mixin/safe_mode'
@@ -154,6 +155,7 @@ require_relative 'rubocop/cop/correctors/line_break_corrector'
154
155
  require_relative 'rubocop/cop/correctors/multiline_literal_brace_corrector'
155
156
  require_relative 'rubocop/cop/correctors/ordered_gem_corrector'
156
157
  require_relative 'rubocop/cop/correctors/parentheses_corrector'
158
+ require_relative 'rubocop/cop/correctors/percent_literal_corrector'
157
159
  require_relative 'rubocop/cop/correctors/punctuation_corrector'
158
160
  require_relative 'rubocop/cop/correctors/space_corrector'
159
161
  require_relative 'rubocop/cop/correctors/string_literal_corrector'
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # This auto-corrects percent literals
6
+ class PercentLiteralCorrector
7
+ include Util
8
+
9
+ attr_reader :config, :preferred_delimiters
10
+
11
+ def initialize(config, preferred_delimiters)
12
+ @config = config
13
+ @preferred_delimiters = preferred_delimiters
14
+ end
15
+
16
+ def correct(node, char)
17
+ escape = escape_words?(node)
18
+ char = char.upcase if escape
19
+ delimiters = delimiters_for("%#{char}")
20
+ contents = new_contents(node, escape, delimiters)
21
+ wrap_contents(node, contents, char, delimiters)
22
+ end
23
+
24
+ private
25
+
26
+ def wrap_contents(node, contents, char, delimiters)
27
+ lambda do |corrector|
28
+ corrector.replace(
29
+ node.source_range,
30
+ "%#{char}#{delimiters[0]}#{contents}#{delimiters[1]}"
31
+ )
32
+ end
33
+ end
34
+
35
+ def escape_words?(node)
36
+ node.children.any? { |w| needs_escaping?(w.children[0]) }
37
+ end
38
+
39
+ def delimiters_for(type)
40
+ PreferredDelimiters
41
+ .new(type, config, preferred_delimiters)
42
+ .delimiters
43
+ end
44
+
45
+ def new_contents(node, escape, delimiters)
46
+ if node.multiline?
47
+ autocorrect_multiline_words(node, escape, delimiters)
48
+ else
49
+ autocorrect_words(node, escape, delimiters)
50
+ end
51
+ end
52
+
53
+ def autocorrect_multiline_words(node, escape, delimiters)
54
+ contents = process_multiline_words(node, escape, delimiters)
55
+ contents << end_content(node.source)
56
+ contents.join('')
57
+ end
58
+
59
+ def autocorrect_words(node, escape, delimiters)
60
+ node.children.map do |word_node|
61
+ fix_escaped_content(word_node, escape, delimiters)
62
+ end.join(' ')
63
+ end
64
+
65
+ def process_multiline_words(node, escape, delimiters)
66
+ base_line_num = node.first_line
67
+ prev_line_num = base_line_num
68
+ node.children.map.with_index do |word_node, index|
69
+ line_breaks = line_breaks(word_node,
70
+ node.source,
71
+ prev_line_num,
72
+ base_line_num,
73
+ index)
74
+ prev_line_num = word_node.first_line
75
+ content = fix_escaped_content(word_node, escape, delimiters)
76
+ line_breaks + content
77
+ end
78
+ end
79
+
80
+ def line_breaks(node, source, previous_line_num, base_line_num, node_indx)
81
+ source_in_lines = source.split("\n")
82
+ if first_line?(node, previous_line_num)
83
+ node_indx.zero? && node.first_line == base_line_num ? '' : ' '
84
+ else
85
+ process_lines(node, previous_line_num, base_line_num, source_in_lines)
86
+ end
87
+ end
88
+
89
+ def first_line?(node, previous_line_num)
90
+ node.first_line == previous_line_num
91
+ end
92
+
93
+ def process_lines(node, previous_line_num, base_line_num, source_in_lines)
94
+ begin_line_num = previous_line_num - base_line_num + 1
95
+ end_line_num = node.first_line - base_line_num + 1
96
+ lines = source_in_lines[begin_line_num...end_line_num]
97
+ "\n" + lines.join("\n").split(node.source).first
98
+ end
99
+
100
+ def fix_escaped_content(word_node, escape, delimiters)
101
+ content = word_node.children.first.to_s
102
+ content = escape_string(content) if escape
103
+ substitute_escaped_delimiters(content, delimiters)
104
+ content
105
+ end
106
+
107
+ def substitute_escaped_delimiters(content, delimiters)
108
+ delimiters.each { |delim| content.gsub!(delim, "\\#{delim}") }
109
+ end
110
+
111
+ def end_content(source)
112
+ result = /\A(\s*)\]/.match(source.split("\n").last)
113
+ ("\n" + result[1]) if result
114
+ end
115
+ end
116
+ end
117
+ end
@@ -44,22 +44,16 @@ module RuboCop
44
44
  def on_if(node)
45
45
  return if correct_style?(node)
46
46
 
47
- if last_argument_is_heredoc?(node)
47
+ if node.modifier_form? && last_argument_is_heredoc?(node)
48
48
  heredoc_node = last_argument(node)
49
49
 
50
- heredoc_body = heredoc_node.loc.heredoc_body
51
- num_of_heredoc_lines =
52
- heredoc_body.last_line - heredoc_body.first_line
53
-
54
- line = node.last_line + num_of_heredoc_lines + END_OF_HEREDOC_LINE
55
-
56
- return if next_line_empty?(line)
50
+ return if next_line_empty?(heredoc_line(node, heredoc_node))
57
51
 
58
52
  add_offense(heredoc_node, location: :heredoc_end)
59
53
  else
60
54
  return if next_line_empty?(node.last_line)
61
55
 
62
- add_offense(node)
56
+ add_offense(node, location: offense_location(node))
63
57
  end
64
58
  end
65
59
 
@@ -126,6 +120,22 @@ module RuboCop
126
120
  def last_argument(node)
127
121
  node.if_branch.children.last
128
122
  end
123
+
124
+ def heredoc_line(node, heredoc_node)
125
+ heredoc_body = heredoc_node.loc.heredoc_body
126
+ num_of_heredoc_lines =
127
+ heredoc_body.last_line - heredoc_body.first_line
128
+
129
+ node.last_line + num_of_heredoc_lines + END_OF_HEREDOC_LINE
130
+ end
131
+
132
+ def offense_location(node)
133
+ if node.loc && node.loc.end
134
+ :end
135
+ else
136
+ :expression
137
+ end
138
+ end
129
139
  end
130
140
  end
131
141
  end
@@ -29,6 +29,26 @@ module RuboCop
29
29
  MSG_BEFORE_AND_AFTER = 'Keep a blank line before and after ' \
30
30
  '`%<modifier>s`.'.freeze
31
31
 
32
+ def on_class(node)
33
+ _name, superclass, _body = *node
34
+
35
+ @class_or_module_def_line = if superclass
36
+ superclass.first_line
37
+ else
38
+ node.source_range.first_line
39
+ end
40
+ end
41
+
42
+ def on_module(node)
43
+ @class_or_module_def_line = node.source_range.first_line
44
+ end
45
+
46
+ def on_sclass(node)
47
+ self_node, _body = *node
48
+
49
+ @class_or_module_def_line = self_node.source_range.first_line
50
+ end
51
+
32
52
  def on_send(node)
33
53
  return unless node.bare_access_modifier?
34
54
 
@@ -40,11 +60,10 @@ module RuboCop
40
60
  def autocorrect(node)
41
61
  lambda do |corrector|
42
62
  send_line = node.first_line
43
- previous_line = processed_source[send_line - 2]
44
63
  next_line = processed_source[send_line]
45
64
  line = range_by_whole_lines(node.source_range)
46
65
 
47
- unless previous_line_empty?(previous_line)
66
+ unless previous_line_empty?(send_line)
48
67
  corrector.insert_before(line, "\n")
49
68
  end
50
69
 
@@ -62,9 +81,12 @@ module RuboCop
62
81
  end
63
82
  end
64
83
 
65
- def previous_line_empty?(previous_line)
84
+ def previous_line_empty?(send_line)
85
+ previous_line = previous_line_ignoring_comments(processed_source,
86
+ send_line)
87
+
66
88
  block_start?(previous_line) ||
67
- class_def?(previous_line) ||
89
+ class_def?(send_line) ||
68
90
  previous_line.blank?
69
91
  end
70
92
 
@@ -74,15 +96,16 @@ module RuboCop
74
96
 
75
97
  def empty_lines_around?(node)
76
98
  send_line = node.first_line
77
- previous_line = previous_line_ignoring_comments(processed_source,
78
- send_line)
99
+
79
100
  next_line = processed_source[send_line]
80
101
 
81
- previous_line_empty?(previous_line) && next_line_empty?(next_line)
102
+ previous_line_empty?(send_line) && next_line_empty?(next_line)
82
103
  end
83
104
 
84
105
  def class_def?(line)
85
- line =~ /^\s*(class|module)/
106
+ return false unless @class_or_module_def_line
107
+
108
+ line == @class_or_module_def_line + 1
86
109
  end
87
110
 
88
111
  def block_start?(line)
@@ -94,10 +117,11 @@ module RuboCop
94
117
  end
95
118
 
96
119
  def message(node)
97
- previous_line = processed_source[node.first_line - 2]
120
+ send_line = node.first_line
121
+ previous_line = processed_source[send_line - 2]
98
122
 
99
123
  if block_start?(previous_line) ||
100
- class_def?(previous_line)
124
+ class_def?(send_line)
101
125
  format(MSG_AFTER, modifier: node.loc.selector.source)
102
126
  else
103
127
  format(MSG_BEFORE_AND_AFTER, modifier: node.loc.selector.source)
@@ -24,12 +24,13 @@ module RuboCop
24
24
  # The array of regular expressions representing percent literals that,
25
25
  # if found within a percent literal expression, will cause a
26
26
  # NestedPercentLiteral violation to be emitted.
27
- REGEXES = PercentLiteral::PERCENT_LITERAL_TYPES.map do |percent_literal|
27
+ PERCENT_LITERAL_TYPES = PreferredDelimiters::PERCENT_LITERAL_TYPES
28
+ REGEXES = PERCENT_LITERAL_TYPES.map do |percent_literal|
28
29
  /\A#{percent_literal}\W/
29
30
  end.freeze
30
31
 
31
32
  def on_array(node)
32
- process(node, *PercentLiteral::PERCENT_LITERAL_TYPES)
33
+ process(node, *PERCENT_LITERAL_TYPES)
33
34
  end
34
35
 
35
36
  def on_percent_literal(node)
@@ -14,6 +14,7 @@ module RuboCop
14
14
 
15
15
  def on_block(node)
16
16
  return if excluded_method?(node)
17
+ return if node.class_constructor?
17
18
 
18
19
  check_code_length(node)
19
20
  end
@@ -13,8 +13,18 @@ module RuboCop
13
13
  check_code_length(node)
14
14
  end
15
15
 
16
+ def on_casgn(node)
17
+ class_definition?(node) do
18
+ check_code_length(node)
19
+ end
20
+ end
21
+
16
22
  private
17
23
 
24
+ def_node_matcher :class_definition?, <<-PATTERN
25
+ (casgn nil? _ (block (send (const nil? :Class) :new) ...))
26
+ PATTERN
27
+
18
28
  def message(length, max_length)
19
29
  format('Class has too many lines. [%<length>d/%<max>d]',
20
30
  length: length,
@@ -123,8 +123,8 @@ module RuboCop
123
123
 
124
124
  def allowed_uri_position?(line, uri_range)
125
125
  uri_range.begin < max &&
126
- (uri_range.end == line_length(line) ||
127
- uri_range.end == line_length(line) - 1)
126
+ (uri_range.end == line.length ||
127
+ uri_range.end == line.length - 1)
128
128
  end
129
129
 
130
130
  def find_excessive_uri_range(line)
@@ -12,6 +12,9 @@ module RuboCop
12
12
  LABEL = 'Method'.freeze
13
13
 
14
14
  def on_def(node)
15
+ excluded_methods = cop_config['ExcludedMethods']
16
+ return if excluded_methods.include?(String(node.method_name))
17
+
15
18
  check_code_length(node)
16
19
  end
17
20
  alias on_defs on_def
@@ -13,8 +13,18 @@ module RuboCop
13
13
  check_code_length(node)
14
14
  end
15
15
 
16
+ def on_casgn(node)
17
+ module_definition?(node) do
18
+ check_code_length(node)
19
+ end
20
+ end
21
+
16
22
  private
17
23
 
24
+ def_node_matcher :module_definition?, <<-PATTERN
25
+ (casgn nil? _ (block (send (const nil? :Module) :new) ...))
26
+ PATTERN
27
+
18
28
  def message(length, max_length)
19
29
  format('Module has too many lines. [%<length>d/%<max>d]',
20
30
  length: length,
@@ -18,9 +18,13 @@ module RuboCop
18
18
 
19
19
  def check_code_length(node)
20
20
  length = code_length(node)
21
+
21
22
  return unless length > max_length
22
23
 
23
- add_offense(node, message: message(length, max_length)) do
24
+ location = node.casgn_type? ? :name : :expression
25
+
26
+ add_offense(node, location: location,
27
+ message: message(length, max_length)) do
24
28
  self.max = length
25
29
  end
26
30
  end
@@ -6,8 +6,6 @@ module RuboCop
6
6
  module PercentLiteral
7
7
  include RangeHelp
8
8
 
9
- PERCENT_LITERAL_TYPES = %w[% %i %I %q %Q %r %s %w %W %x].freeze
10
-
11
9
  private
12
10
 
13
11
  def percent_literal?(node)
@@ -35,117 +33,6 @@ module RuboCop
35
33
  def contents_range(node)
36
34
  range_between(node.loc.begin.end_pos, node.loc.end.begin_pos)
37
35
  end
38
-
39
- # ['a', 'b', 'c'] => %w(a b c)
40
- def correct_percent(node, char)
41
- words = node.children
42
- escape = words.any? { |w| needs_escaping?(w.children[0]) }
43
- char = char.upcase if escape
44
- delimiters = preferred_delimiters_for("%#{char}")
45
- contents = new_contents(node, escape, delimiters)
46
-
47
- lambda do |corrector|
48
- corrector.replace(
49
- node.source_range,
50
- "%#{char}#{delimiters[0]}#{contents}#{delimiters[1]}"
51
- )
52
- end
53
- end
54
-
55
- def new_contents(node, escape, delimiters)
56
- if node.multiline?
57
- autocorrect_multiline_words(node, escape, delimiters)
58
- else
59
- autocorrect_words(node, escape, delimiters)
60
- end
61
- end
62
-
63
- def autocorrect_multiline_words(node, escape, delimiters)
64
- base_line_number = node.first_line
65
- previous_line_number = base_line_number
66
- contents = node.children.map.with_index do |word_node, index|
67
- line_breaks = line_breaks(word_node,
68
- node.source,
69
- previous_line_number,
70
- base_line_number,
71
- index)
72
- previous_line_number = word_node.first_line
73
- content = escaped_content(word_node, escape, delimiters)
74
- line_breaks + content
75
- end
76
- contents << end_content(node.source)
77
- contents.join('')
78
- end
79
-
80
- def autocorrect_words(node, escape, delimiters)
81
- node.children.map do |word_node|
82
- content = word_node.children.first.to_s
83
- content = escape ? escape_string(content) : content
84
- delimiters.each do |delimiter|
85
- content.gsub!(delimiter, "\\#{delimiter}")
86
- end
87
- content
88
- end.join(' ')
89
- end
90
-
91
- def line_breaks(node, source, previous_line_num, base_line_num, node_idx)
92
- source_in_lines = source.split("\n")
93
- if node.first_line == previous_line_num
94
- node_idx.zero? && node.first_line == base_line_num ? '' : ' '
95
- else
96
- begin_line_num = previous_line_num - base_line_num + 1
97
- end_line_num = node.first_line - base_line_num + 1
98
- lines = source_in_lines[begin_line_num...end_line_num]
99
- "\n" + lines.join("\n").split(node.source).first
100
- end
101
- end
102
-
103
- def escaped_content(word_node, escape, delimiters)
104
- content = word_node.children.first.to_s
105
- content = escape_string(content) if escape
106
- delimiters.each do |delimiter|
107
- content.gsub!(delimiter, "\\#{delimiter}")
108
- end
109
- content
110
- end
111
-
112
- def end_content(source)
113
- result = /\A(\s*)\]/.match(source.split("\n").last)
114
- ("\n" + result[1]) if result
115
- end
116
-
117
- def ensure_valid_preferred_delimiters
118
- invalid = preferred_delimiters_config.keys -
119
- (PERCENT_LITERAL_TYPES + %w[default])
120
- return if invalid.empty?
121
-
122
- raise ArgumentError,
123
- "Invalid preferred delimiter config key: #{invalid.join(', ')}"
124
- end
125
-
126
- def preferred_delimiters
127
- @preferred_delimiters ||=
128
- begin
129
- ensure_valid_preferred_delimiters
130
-
131
- if preferred_delimiters_config.key?('default')
132
- Hash[PERCENT_LITERAL_TYPES.map do |type|
133
- [type, preferred_delimiters_config[type] ||
134
- preferred_delimiters_config['default']]
135
- end]
136
- else
137
- preferred_delimiters_config
138
- end
139
- end
140
- end
141
-
142
- def preferred_delimiters_config
143
- @config.for_cop('Style/PercentLiteralDelimiters')['PreferredDelimiters']
144
- end
145
-
146
- def preferred_delimiters_for(type)
147
- preferred_delimiters[type].split(//)
148
- end
149
36
  end
150
37
  end
151
38
  end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for handling percent literal delimiters.
6
+ class PreferredDelimiters
7
+ attr_reader :type, :config
8
+
9
+ PERCENT_LITERAL_TYPES = %w[% %i %I %q %Q %r %s %w %W %x].freeze
10
+
11
+ def initialize(type, config, preferred_delimiters)
12
+ @type = type
13
+ @config = config
14
+ @preferred_delimiters = preferred_delimiters
15
+ end
16
+
17
+ def delimiters
18
+ preferred_delimiters[type].split(//)
19
+ end
20
+
21
+ private
22
+
23
+ def ensure_valid_preferred_delimiters
24
+ invalid = preferred_delimiters_config.keys -
25
+ (PERCENT_LITERAL_TYPES + %w[default])
26
+ return if invalid.empty?
27
+
28
+ raise ArgumentError,
29
+ "Invalid preferred delimiter config key: #{invalid.join(', ')}"
30
+ end
31
+
32
+ def preferred_delimiters
33
+ @preferred_delimiters ||=
34
+ begin
35
+ ensure_valid_preferred_delimiters
36
+
37
+ if preferred_delimiters_config.key?('default')
38
+ Hash[PERCENT_LITERAL_TYPES.map do |type|
39
+ [type, preferred_delimiters_config[type] ||
40
+ preferred_delimiters_config['default']]
41
+ end]
42
+ else
43
+ preferred_delimiters_config
44
+ end
45
+ end
46
+ end
47
+
48
+ def preferred_delimiters_config
49
+ config.for_cop('Style/PercentLiteralDelimiters')['PreferredDelimiters']
50
+ end
51
+ end
52
+ end
53
+ end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # @foo ||= calculate_expensive_thing(helper_variable)
44
44
  # end
45
45
  #
46
- # @example EnforcedStyleForLeadingUnderscores :required
46
+ # @example EnforcedStyleForLeadingUnderscores: required
47
47
  # # bad
48
48
  # def foo
49
49
  # @something ||= calculate_expensive_thing
@@ -35,7 +35,7 @@ module RuboCop
35
35
  # foo.bar
36
36
  # end
37
37
  #
38
- # # EnforceForPrefixed: true
38
+ # @example EnforceForPrefixed: true (default)
39
39
  # # bad
40
40
  # def foo_bar
41
41
  # foo.bar
@@ -44,7 +44,7 @@ module RuboCop
44
44
  # # good
45
45
  # delegate :bar, to: :foo, prefix: true
46
46
  #
47
- # # EnforceForPrefixed: false
47
+ # @example EnforceForPrefixed: false
48
48
  # # good
49
49
  # def foo_bar
50
50
  # foo.bar
@@ -23,6 +23,10 @@ module RuboCop
23
23
  class HasManyOrHasOneDependent < Cop
24
24
  MSG = 'Specify a `:dependent` option.'.freeze
25
25
 
26
+ def_node_search :active_resource_class?, <<-PATTERN
27
+ (const (const nil? :ActiveResource) :Base)
28
+ PATTERN
29
+
26
30
  def_node_matcher :association_without_options?, <<-PATTERN
27
31
  (send nil? {:has_many :has_one} _)
28
32
  PATTERN
@@ -47,6 +51,8 @@ module RuboCop
47
51
  PATTERN
48
52
 
49
53
  def on_send(node)
54
+ return if active_resource?(node.parent)
55
+
50
56
  unless association_without_options?(node)
51
57
  return if valid_options?(association_with_options?(node))
52
58
  end
@@ -88,6 +94,12 @@ module RuboCop
88
94
 
89
95
  false
90
96
  end
97
+
98
+ def active_resource?(node)
99
+ return false if node.nil?
100
+
101
+ active_resource_class?(node)
102
+ end
91
103
  end
92
104
  end
93
105
  end
@@ -99,12 +99,37 @@ module RuboCop
99
99
 
100
100
  def autocorrect_to_each(node)
101
101
  item, enumerable = deconstruct_for(node)
102
- replacement_range = replacement_range(node, node.loc.begin.end_pos)
103
- correction = "#{enumerable.source}.each do |#{item.source}|"
104
102
 
103
+ end_pos = end_position(node, enumerable)
104
+
105
+ replacement_range = replacement_range(node, end_pos)
106
+
107
+ enum_source = enumerable_source(enumerable)
108
+
109
+ correction = "#{enum_source}.each do |#{item.source}|"
105
110
  ->(corrector) { corrector.replace(replacement_range, correction) }
106
111
  end
107
112
 
113
+ def end_position(node, enumerable)
114
+ if node.do?
115
+ node.loc.begin.end_pos
116
+ elsif enumerable.begin_type?
117
+ enumerable.loc.end.end_pos
118
+ else
119
+ enumerable.loc.expression.end.end_pos
120
+ end
121
+ end
122
+
123
+ def enumerable_source(enumerable)
124
+ return "(#{enumerable.source})" if wrap_into_parentheses?(enumerable)
125
+
126
+ enumerable.source
127
+ end
128
+
129
+ def wrap_into_parentheses?(enumerable)
130
+ enumerable.irange_type? || enumerable.erange_type?
131
+ end
132
+
108
133
  def autocorrect_to_for(node)
109
134
  enumerable, items = deconstruct_each(node)
110
135
  variables = extract_variables(items)
@@ -31,8 +31,11 @@ module RuboCop
31
31
  ASSIGNMENT_TYPES = %i[lvasgn casgn cvasgn
32
32
  gvasgn ivasgn masgn].freeze
33
33
 
34
+ NAMED_CAPTURE = /\?<.+>/
35
+
34
36
  def on_if(node)
35
37
  return unless eligible_node?(node)
38
+ return if named_capture_in_condition?(node)
36
39
 
37
40
  add_offense(node, location: :keyword,
38
41
  message: format(MSG, keyword: node.keyword))
@@ -46,6 +49,10 @@ module RuboCop
46
49
 
47
50
  private
48
51
 
52
+ def named_capture_in_condition?(node)
53
+ node.condition.match_with_lvasgn_type?
54
+ end
55
+
49
56
  def eligible_node?(node)
50
57
  !non_eligible_if?(node) && !node.chained? &&
51
58
  !node.nested_conditional? && single_line_as_modifier?(node)
@@ -77,6 +77,12 @@ module RuboCop
77
77
  add_offense(node)
78
78
  end
79
79
 
80
+ def preferred_delimiters_for(type)
81
+ PreferredDelimiters
82
+ .new(type, @config, @preferred_delimiters)
83
+ .delimiters
84
+ end
85
+
80
86
  def uses_preferred_delimiter?(node, type)
81
87
  preferred_delimiters_for(type)[0] == begin_source(node)[-1]
82
88
  end
@@ -39,14 +39,18 @@ module RuboCop
39
39
  exprs_lines = exprs.map { |e| e.source_range.line }
40
40
  lines = exprs_lines.group_by { |i| i }
41
41
 
42
- # every line with more than 1 expression on it is an offense
43
42
  lines.each do |line, expr_on_line|
43
+ # Every line with more than one expression on it is a
44
+ # potential offense
44
45
  next unless expr_on_line.size > 1
45
46
 
46
47
  # TODO: Find the correct position of the semicolon. We don't know
47
48
  # if the first semicolon on the line is a separator of
48
49
  # expressions. It's just a guess.
49
50
  column = @processed_source[line - 1].index(';')
51
+
52
+ next unless column
53
+
50
54
  convention_on(line, column, false)
51
55
  end
52
56
  end
@@ -31,7 +31,6 @@ module RuboCop
31
31
  include ArrayMinSize
32
32
  include ArraySyntax
33
33
  include ConfigurableEnforcedStyle
34
- include PercentLiteral
35
34
  include PercentArray
36
35
 
37
36
  PERCENT_MSG = 'Use `%i` or `%I` for an array of symbols.'.freeze
@@ -53,7 +52,9 @@ module RuboCop
53
52
 
54
53
  def autocorrect(node)
55
54
  if style == :percent
56
- correct_percent(node, 'i')
55
+ PercentLiteralCorrector
56
+ .new(@config, @preferred_delimiters)
57
+ .correct(node, 'i')
57
58
  else
58
59
  correct_bracketed(node)
59
60
  end
@@ -32,7 +32,6 @@ module RuboCop
32
32
  include ArraySyntax
33
33
  include ConfigurableEnforcedStyle
34
34
  include PercentArray
35
- include PercentLiteral
36
35
 
37
36
  PERCENT_MSG = 'Use `%w` or `%W` for an array of words.'.freeze
38
37
  ARRAY_MSG = 'Use `[]` for an array of words.'.freeze
@@ -53,7 +52,9 @@ module RuboCop
53
52
 
54
53
  def autocorrect(node)
55
54
  if style == :percent
56
- correct_percent(node, 'w')
55
+ PercentLiteralCorrector
56
+ .new(@config, @preferred_delimiters)
57
+ .correct(node, 'w')
57
58
  else
58
59
  correct_bracketed(node)
59
60
  end
@@ -50,6 +50,8 @@ module RuboCop
50
50
 
51
51
  lhs, opr, rhs = zero_length_predicate
52
52
 
53
+ return if non_polymorphic_collection?(node)
54
+
53
55
  add_offense(
54
56
  node,
55
57
  message: format(ZERO_MSG, lhs: lhs, opr: opr, rhs: rhs)
@@ -63,6 +65,8 @@ module RuboCop
63
65
 
64
66
  lhs, opr, rhs = nonzero_length_predicate
65
67
 
68
+ return if non_polymorphic_collection?(node)
69
+
66
70
  add_offense(
67
71
  node,
68
72
  message: format(NONZERO_MSG, lhs: lhs, opr: opr, rhs: rhs)
@@ -99,6 +103,14 @@ module RuboCop
99
103
  {(send (send $_ _) _ _)
100
104
  (send _ _ (send $_ _))}
101
105
  PATTERN
106
+
107
+ # Some collection like objects in the Ruby standard library
108
+ # implement `#size`, but not `#empty`. We ignore those to
109
+ # reduce false positives.
110
+ def_node_matcher :non_polymorphic_collection?, <<-PATTERN
111
+ {(send (send (send (const nil? :File) :stat _) ...) ...)
112
+ (send (send (send (const nil? {:Tempfile :StringIO}) {:new :open} ...) ...) ...)}
113
+ PATTERN
102
114
  end
103
115
  end
104
116
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '0.59.1'.freeze
6
+ STRING = '0.59.2'.freeze
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, running on ' \
9
9
  '%<ruby_engine>s %<ruby_version>s %<ruby_platform>s)'.freeze
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.59.1
4
+ version: 0.59.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2018-09-15 00:00:00.000000000 Z
13
+ date: 2018-09-24 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: jaro_winkler
@@ -239,6 +239,7 @@ files:
239
239
  - lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb
240
240
  - lib/rubocop/cop/correctors/ordered_gem_corrector.rb
241
241
  - lib/rubocop/cop/correctors/parentheses_corrector.rb
242
+ - lib/rubocop/cop/correctors/percent_literal_corrector.rb
242
243
  - lib/rubocop/cop/correctors/punctuation_corrector.rb
243
244
  - lib/rubocop/cop/correctors/space_corrector.rb
244
245
  - lib/rubocop/cop/correctors/string_literal_corrector.rb
@@ -466,6 +467,7 @@ files:
466
467
  - lib/rubocop/cop/mixin/percent_array.rb
467
468
  - lib/rubocop/cop/mixin/percent_literal.rb
468
469
  - lib/rubocop/cop/mixin/preceding_following_alignment.rb
470
+ - lib/rubocop/cop/mixin/preferred_delimiters.rb
469
471
  - lib/rubocop/cop/mixin/range_help.rb
470
472
  - lib/rubocop/cop/mixin/rescue_node.rb
471
473
  - lib/rubocop/cop/mixin/safe_assignment.rb
@@ -799,10 +801,10 @@ homepage: https://github.com/rubocop-hq/rubocop
799
801
  licenses:
800
802
  - MIT
801
803
  metadata:
802
- homepage_uri: http://www.rubocop.org/
804
+ homepage_uri: https://www.rubocop.org/
803
805
  changelog_uri: https://github.com/rubocop-hq/rubocop/blob/master/CHANGELOG.md
804
806
  source_code_uri: https://github.com/rubocop-hq/rubocop/
805
- documentation_uri: http://docs.rubocop.org/
807
+ documentation_uri: https://docs.rubocop.org/
806
808
  bug_tracker_uri: https://github.com/rubocop-hq/rubocop/issues
807
809
  post_install_message:
808
810
  rdoc_options: []