rubocop 0.59.1 → 0.59.2

Sign up to get free protection for your applications and to get access to all the features.
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: []