rubocop 0.57.2 → 0.58.0

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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +12 -9
  3. data/bin/setup +7 -0
  4. data/config/default.yml +18 -2
  5. data/config/disabled.yml +4 -0
  6. data/lib/rubocop.rb +1 -0
  7. data/lib/rubocop/ast/node.rb +5 -0
  8. data/lib/rubocop/ast/node/str_node.rb +2 -0
  9. data/lib/rubocop/cli.rb +4 -7
  10. data/lib/rubocop/config.rb +4 -4
  11. data/lib/rubocop/config_loader.rb +4 -8
  12. data/lib/rubocop/cop/corrector.rb +25 -0
  13. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +3 -7
  14. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  15. data/lib/rubocop/cop/layout/indentation_width.rb +9 -1
  16. data/lib/rubocop/cop/layout/leading_blank_lines.rb +1 -1
  17. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +28 -51
  18. data/lib/rubocop/cop/lint/redundant_with_object.rb +1 -1
  19. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -3
  20. data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +1 -1
  21. data/lib/rubocop/cop/lint/useless_access_modifier.rb +17 -4
  22. data/lib/rubocop/cop/metrics/line_length.rb +28 -6
  23. data/lib/rubocop/cop/mixin/check_assignment.rb +0 -2
  24. data/lib/rubocop/cop/mixin/statement_modifier.rb +6 -1
  25. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +79 -4
  26. data/lib/rubocop/cop/performance/inefficient_hash_search.rb +9 -5
  27. data/lib/rubocop/cop/performance/range_include.rb +9 -3
  28. data/lib/rubocop/cop/performance/sample.rb +6 -4
  29. data/lib/rubocop/cop/rails/bulk_change_table.rb +11 -7
  30. data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +3 -1
  31. data/lib/rubocop/cop/registry.rb +11 -2
  32. data/lib/rubocop/cop/style/encoding.rb +5 -0
  33. data/lib/rubocop/cop/style/end_block.rb +8 -0
  34. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -1
  35. data/lib/rubocop/cop/style/ip_addresses.rb +76 -0
  36. data/lib/rubocop/cop/style/multiple_comparison.rb +16 -2
  37. data/lib/rubocop/cop/style/symbol_proc.rb +4 -2
  38. data/lib/rubocop/cop/style/unneeded_condition.rb +19 -2
  39. data/lib/rubocop/formatter/disabled_config_formatter.rb +3 -3
  40. data/lib/rubocop/options.rb +20 -12
  41. data/lib/rubocop/processed_source.rb +2 -5
  42. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  43. data/lib/rubocop/rspec/shared_contexts.rb +0 -4
  44. data/lib/rubocop/rspec/shared_examples.rb +0 -23
  45. data/lib/rubocop/version.rb +1 -1
  46. metadata +7 -11
@@ -36,7 +36,7 @@ module RuboCop
36
36
  def_node_matcher :redundant_with_object?, <<-PATTERN
37
37
  (block
38
38
  $(send _ {:each_with_object :with_object}
39
- (_))
39
+ _)
40
40
  (args
41
41
  (arg _))
42
42
  ...)
@@ -77,9 +77,7 @@ module RuboCop
77
77
  next if references.any? do |reference|
78
78
  next true if !reference.explicit? && ignore_implicit_references?
79
79
 
80
- reference_pos = reference.node.source_range.begin_pos
81
-
82
- reference_pos <= assignment_without_usage_pos
80
+ reference_pos(reference.node) <= assignment_without_usage_pos
83
81
  end
84
82
 
85
83
  yield location_known ? node : argument.declaration_node
@@ -115,6 +113,12 @@ module RuboCop
115
113
  end
116
114
  end
117
115
 
116
+ def reference_pos(node)
117
+ node = node.parent.masgn_type? ? node.parent : node
118
+
119
+ node.source_range.begin_pos
120
+ end
121
+
118
122
  # Check whether the given node is nested into block or conditional.
119
123
  #
120
124
  def node_within_block_or_conditional?(node, stop_search_node)
@@ -44,7 +44,7 @@ module RuboCop
44
44
  # end
45
45
  #
46
46
  # case foo
47
- # when *[1, 2, 3]
47
+ # when 1, 2, 3
48
48
  # bar
49
49
  # else
50
50
  # baz
@@ -134,11 +134,16 @@ module RuboCop
134
134
 
135
135
  if node.begin_type?
136
136
  check_scope(node)
137
- elsif node.send_type? && node.bare_access_modifier?
137
+ elsif node.send_type? && access_modifier?(node)
138
138
  add_offense(node, message: format(MSG, current: node.method_name))
139
139
  end
140
140
  end
141
141
 
142
+ def access_modifier?(node)
143
+ node.bare_access_modifier? ||
144
+ node.method_name == :private_class_method
145
+ end
146
+
142
147
  def check_scope(node)
143
148
  cur_vis, unused = check_child_nodes(node, nil, :public)
144
149
 
@@ -147,9 +152,8 @@ module RuboCop
147
152
 
148
153
  def check_child_nodes(node, unused, cur_vis)
149
154
  node.child_nodes.each do |child|
150
- if child.send_type? && child.bare_access_modifier?
151
- cur_vis, unused =
152
- check_new_visibility(child, unused, child.method_name, cur_vis)
155
+ if child.send_type? && access_modifier?(child)
156
+ cur_vis, unused = check_send_node(child, cur_vis, unused)
153
157
  elsif method_definition?(child)
154
158
  unused = nil
155
159
  elsif start_of_new_scope?(child)
@@ -162,6 +166,15 @@ module RuboCop
162
166
  [cur_vis, unused]
163
167
  end
164
168
 
169
+ def check_send_node(node, cur_vis, unused)
170
+ if node.bare_access_modifier?
171
+ check_new_visibility(node, unused, node.method_name, cur_vis)
172
+ elsif node.method_name == :private_class_method && !node.arguments?
173
+ add_offense(node, message: format(MSG, current: node.method_name))
174
+ [cur_vis, unused]
175
+ end
176
+ end
177
+
165
178
  def check_new_visibility(node, unused, new_vis, cur_vis)
166
179
  # does this modifier just repeat the existing visibility?
167
180
  if new_vis == cur_vis
@@ -7,6 +7,8 @@ module RuboCop
7
7
  module Metrics
8
8
  # This cop checks the length of lines in the source code.
9
9
  # The maximum length is configurable.
10
+ # The tab size is configured in the `IndentationWidth`
11
+ # of the `Layout/Tab` cop.
10
12
  class LineLength < Cop
11
13
  include ConfigurableMax
12
14
  include IgnoredPattern
@@ -23,8 +25,24 @@ module RuboCop
23
25
 
24
26
  private
25
27
 
28
+ def tab_indentation_width
29
+ config.for_cop('Layout/Tab')['IndentationWidth']
30
+ end
31
+
32
+ def line_length(line)
33
+ if tab_indentation_width
34
+ line = line.gsub("\t", ' ' * tab_indentation_width)
35
+ end
36
+ line.length
37
+ end
38
+
39
+ def highligh_start(line)
40
+ return max unless tab_indentation_width
41
+ max - (tab_indentation_width - 1) * line.count("\t")
42
+ end
43
+
26
44
  def check_line(line, index, heredocs)
27
- return if line.length <= max
45
+ return if line_length(line) <= max
28
46
  return if ignored_line?(line, index, heredocs)
29
47
 
30
48
  if ignore_cop_directives? && directive_on_source_line?(index)
@@ -33,7 +51,10 @@ module RuboCop
33
51
  return check_uri_line(line, index) if allow_uri?
34
52
 
35
53
  register_offense(
36
- source_range(processed_source.buffer, index + 1, 0...line.length),
54
+ source_range(
55
+ processed_source.buffer, index,
56
+ highligh_start(line)...line_length(line)
57
+ ),
37
58
  line
38
59
  )
39
60
  end
@@ -44,10 +65,10 @@ module RuboCop
44
65
  end
45
66
 
46
67
  def register_offense(loc, line)
47
- message = format(MSG, length: line.length, max: max)
68
+ message = format(MSG, length: line_length(line), max: max)
48
69
 
49
70
  add_offense(nil, location: loc, message: message) do
50
- self.max = line.length
71
+ self.max = line_length(line)
51
72
  end
52
73
  end
53
74
 
@@ -59,7 +80,7 @@ module RuboCop
59
80
  end
60
81
 
61
82
  source_range(processed_source.buffer, index + 1,
62
- excessive_position...(line.length))
83
+ excessive_position...(line_length(line)))
63
84
  end
64
85
 
65
86
  def max
@@ -100,7 +121,8 @@ module RuboCop
100
121
 
101
122
  def allowed_uri_position?(line, uri_range)
102
123
  uri_range.begin < max &&
103
- (uri_range.end == line.length || uri_range.end == line.length - 1)
124
+ (uri_range.end == line_length(line) ||
125
+ uri_range.end == line_length(line) - 1)
104
126
  end
105
127
 
106
128
  def find_excessive_uri_range(line)
@@ -11,8 +11,6 @@ module RuboCop
11
11
  end
12
12
 
13
13
  def on_send(node)
14
- return unless node.setter_method?
15
-
16
14
  rhs = extract_rhs(node)
17
15
 
18
16
  return unless rhs
@@ -39,7 +39,7 @@ module RuboCop
39
39
  end
40
40
 
41
41
  def length_in_modifier_form(node, cond, body_length)
42
- indentation = node.loc.keyword.column
42
+ indentation = node.loc.keyword.column * indentation_multiplier
43
43
  kw_length = node.loc.keyword.size
44
44
  cond_length = cond.source_range.size
45
45
  space = 1
@@ -49,6 +49,11 @@ module RuboCop
49
49
  def max_line_length
50
50
  config.for_cop('Metrics/LineLength')['Max']
51
51
  end
52
+
53
+ def indentation_multiplier
54
+ return 1 if config.for_cop('Layout/Tab')['Enabled']
55
+ config.for_cop('Layout/Tab')['IndentationWidth']
56
+ end
52
57
  end
53
58
  end
54
59
  end
@@ -6,7 +6,13 @@ module RuboCop
6
6
  # This cop checks for memoized methods whose instance variable name
7
7
  # does not match the method name.
8
8
  #
9
- # @example
9
+ # This cop can be configured with the EnforcedStyleForLeadingUnderscores
10
+ # directive. It can be configured to allow for memoized instance variables
11
+ # prefixed with an underscore. Prefixing ivars with an undersscore is a
12
+ # convention that is used to implicitly indicate that an ivar should not
13
+ # be set or referencd outside of the memoization method.
14
+ #
15
+ # @example EnforcedStyleForLeadingUnderscores: disallowed (default)
10
16
  # # bad
11
17
  # # Method foo is memoized using an instance variable that is
12
18
  # # not `@foo`. This can cause confusion and bugs.
@@ -32,9 +38,44 @@ module RuboCop
32
38
  # @foo ||= calculate_expensive_thing(helper_variable)
33
39
  # end
34
40
  #
41
+ # @example EnforcedStyleForLeadingUnderscores :required
42
+ # # bad
43
+ # def foo
44
+ # @something ||= calculate_expensive_thing
45
+ # end
46
+ #
47
+ # # bad
48
+ # def foo
49
+ # @foo ||= calculate_expensive_thing
50
+ # end
51
+ #
52
+ # # good
53
+ # def foo
54
+ # @_foo ||= calculate_expensive_thing
55
+ # end
56
+ #
57
+ # @example EnforcedStyleForLeadingUnderscores :optional
58
+ # # bad
59
+ # def foo
60
+ # @something ||= calculate_expensive_thing
61
+ # end
62
+ #
63
+ # # good
64
+ # def foo
65
+ # @foo ||= calculate_expensive_thing
66
+ # end
67
+ #
68
+ # # good
69
+ # def foo
70
+ # @_foo ||= calculate_expensive_thing
71
+ # end
35
72
  class MemoizedInstanceVariableName < Cop
73
+ include ConfigurableEnforcedStyle
74
+
36
75
  MSG = 'Memoized variable `%<var>s` does not match ' \
37
76
  'method name `%<method>s`. Use `@%<suggested_var>s` instead.'.freeze
77
+ UNDERSCORE_REQUIRED = 'Memoized variable `%<var>s` does not start ' \
78
+ 'with `_`. Use `@%<suggested_var>s` instead.'.freeze
38
79
 
39
80
  def self.node_pattern
40
81
  memo_assign = '(or_asgn $(ivasgn _) _)'
@@ -52,10 +93,11 @@ module RuboCop
52
93
  def on_def(node)
53
94
  (method_name, ivar_assign) = memoized?(node)
54
95
  return if matches?(method_name, ivar_assign)
96
+
55
97
  msg = format(
56
- MSG,
98
+ message(ivar_assign.children.first.to_s),
57
99
  var: ivar_assign.children.first.to_s,
58
- suggested_var: method_name.to_s.delete('!?'),
100
+ suggested_var: suggested_var(method_name),
59
101
  method: method_name
60
102
  )
61
103
  add_offense(node, location: ivar_assign.source_range, message: msg)
@@ -64,12 +106,45 @@ module RuboCop
64
106
 
65
107
  private
66
108
 
109
+ def style_parameter_name
110
+ 'EnforcedStyleForLeadingUnderscores'
111
+ end
112
+
67
113
  def matches?(method_name, ivar_assign)
68
114
  return true if ivar_assign.nil? || method_name == :initialize
69
115
  method_name = method_name.to_s.delete('!?')
70
116
  variable = ivar_assign.children.first
71
117
  variable_name = variable.to_s.sub('@', '')
72
- variable_name == method_name
118
+
119
+ return false unless valid_leading_underscore?(variable_name)
120
+
121
+ variable_name.sub(/\A_/, '') == method_name
122
+ end
123
+
124
+ def message(variable)
125
+ variable_name = variable.to_s.sub('@', '')
126
+
127
+ return UNDERSCORE_REQUIRED if style == :required &&
128
+ !variable_name.start_with?('_')
129
+
130
+ MSG
131
+ end
132
+
133
+ def suggested_var(method_name)
134
+ suggestion = method_name.to_s.delete('!?')
135
+
136
+ style == :required ? "_#{suggestion}" : suggestion
137
+ end
138
+
139
+ def valid_leading_underscore?(variable_name)
140
+ case style
141
+ when :required
142
+ variable_name.start_with?('_')
143
+ when :disallowed
144
+ !variable_name.start_with?('_')
145
+ else
146
+ true
147
+ end
73
148
  end
74
149
  end
75
150
  end
@@ -38,11 +38,15 @@ module RuboCop
38
38
  #
39
39
  class InefficientHashSearch < Cop
40
40
  def_node_matcher :inefficient_include?, <<-PATTERN
41
- (send $(send _ ${:keys :values}) :include? $_)
41
+ (send (send $_ {:keys :values}) :include? _)
42
42
  PATTERN
43
43
 
44
44
  def on_send(node)
45
- add_offense(node, message: msg(node)) if inefficient_include?(node)
45
+ inefficient_include?(node) do |receiver|
46
+ return if receiver.nil?
47
+
48
+ add_offense(node)
49
+ end
46
50
  end
47
51
 
48
52
  def autocorrect(node)
@@ -59,7 +63,7 @@ module RuboCop
59
63
 
60
64
  private
61
65
 
62
- def msg(node)
66
+ def message(node)
63
67
  "Use `##{autocorrect_method(node)}` instead of "\
64
68
  "`##{current_method(node)}.include?`."
65
69
  end
@@ -72,7 +76,7 @@ module RuboCop
72
76
  end
73
77
 
74
78
  def current_method(node)
75
- node.children[0].method_name
79
+ node.receiver.method_name
76
80
  end
77
81
 
78
82
  def use_long_method
@@ -87,7 +91,7 @@ module RuboCop
87
91
  end
88
92
 
89
93
  def autocorrect_hash_expression(node)
90
- node.children[0].children[0].loc.expression.source
94
+ node.receiver.receiver.source
91
95
  end
92
96
  end
93
97
  end
@@ -9,11 +9,17 @@ module RuboCop
9
9
  # end points of the `Range`. In a great majority of cases, this is what
10
10
  # is wanted.
11
11
  #
12
- # Here is an example of a case where `Range#cover?` may not provide the
13
- # desired result:
12
+ # @example
13
+ # # bad
14
+ # ('a'..'z').include?('b') # => true
14
15
  #
15
- # ('a'..'z').cover?('yellow') # => true
16
+ # # good
17
+ # ('a'..'z').cover?('b') # => true
16
18
  #
19
+ # # Example of a case where `Range#cover?` may not provide
20
+ # # the desired result:
21
+ #
22
+ # ('a'..'z').cover?('yellow') # => true
17
23
  class RangeInclude < Cop
18
24
  MSG = 'Use `Range#cover?` instead of `Range#include?`.'.freeze
19
25
 
@@ -11,6 +11,8 @@ module RuboCop
11
11
  # [1, 2, 3].shuffle.first
12
12
  # [1, 2, 3].shuffle.first(2)
13
13
  # [1, 2, 3].shuffle.last
14
+ # [2, 1, 3].shuffle.at(0)
15
+ # [2, 1, 3].shuffle.slice(0)
14
16
  # [1, 2, 3].shuffle[2]
15
17
  # [1, 2, 3].shuffle[0, 2] # sample(2) will do the same
16
18
  # [1, 2, 3].shuffle[0..2] # sample(3) will do the same
@@ -28,7 +30,7 @@ module RuboCop
28
30
  MSG = 'Use `%<correct>s` instead of `%<incorrect>s`.'.freeze
29
31
 
30
32
  def_node_matcher :sample_candidate?, <<-PATTERN
31
- (send $(send _ :shuffle $...) ${:first :last :[]} $...)
33
+ (send $(send _ :shuffle $...) ${:first :last :[] :at :slice} $...)
32
34
  PATTERN
33
35
 
34
36
  def on_send(node)
@@ -57,7 +59,7 @@ module RuboCop
57
59
  case method
58
60
  when :first, :last
59
61
  true
60
- when :[]
62
+ when :[], :at, :slice
61
63
  sample_size(method_args) != :unknown
62
64
  else
63
65
  false
@@ -78,7 +80,7 @@ module RuboCop
78
80
  when :erange, :irange
79
81
  range_size(arg)
80
82
  when :int
81
- arg.to_a.first.zero? ? nil : :unknown
83
+ [0, -1].include?(arg.to_a.first) ? nil : :unknown
82
84
  else
83
85
  :unknown
84
86
  end
@@ -126,7 +128,7 @@ module RuboCop
126
128
  case method
127
129
  when :first, :last
128
130
  extract_source(method_args)
129
- when :[]
131
+ when :[], :slice
130
132
  sample_size(method_args)
131
133
  end
132
134
  end
@@ -76,7 +76,7 @@ module RuboCop
76
76
  MYSQL = 'mysql'.freeze
77
77
  POSTGRESQL = 'postgresql'.freeze
78
78
 
79
- MIGRATIION_METHODS = %i[change up down].freeze
79
+ MIGRATION_METHODS = %i[change up down].freeze
80
80
 
81
81
  COMBINABLE_TRANSFORMATIONS = %i[
82
82
  primary_key
@@ -134,7 +134,7 @@ module RuboCop
134
134
 
135
135
  def on_def(node)
136
136
  return unless support_bulk_alter?
137
- return unless MIGRATIION_METHODS.include?(node.method_name)
137
+ return unless MIGRATION_METHODS.include?(node.method_name)
138
138
  return unless node.body
139
139
 
140
140
  recorder = AlterMethodsRecorder.new
@@ -168,7 +168,7 @@ module RuboCop
168
168
 
169
169
  # @param node [RuboCop::AST::SendNode] (send nil? :change_table ...)
170
170
  def include_bulk_options?(node)
171
- # arguments: [(sym :table) (hash (pair (sym :bulk) _))]
171
+ # arguments: [{(sym :table)(str "table")} (hash (pair (sym :bulk) _))]
172
172
  options = node.arguments[1]
173
173
  return false unless options
174
174
  options.hash_type? &&
@@ -196,6 +196,8 @@ module RuboCop
196
196
  config = yaml['development']
197
197
  return nil unless config.is_a?(Hash)
198
198
  config
199
+ rescue Psych::SyntaxError
200
+ nil
199
201
  end
200
202
 
201
203
  def support_bulk_alter?
@@ -231,7 +233,7 @@ module RuboCop
231
233
 
232
234
  # @param node [RuboCop::AST::SendNode]
233
235
  def add_offense_for_alter_methods(node)
234
- # arguments: [(sym :table) ...]
236
+ # arguments: [{(sym :table)(str "table")} ...]
235
237
  table_name = node.arguments[0].value
236
238
  message = format(MSG_FOR_ALTER_METHODS, table: table_name)
237
239
  add_offense(node, message: message)
@@ -251,9 +253,11 @@ module RuboCop
251
253
 
252
254
  # @param new_node [RuboCop::AST::SendNode]
253
255
  def process(new_node)
254
- # arguments: [(sym :table) ...]
255
- table_name = new_node.arguments[0]
256
- flush unless @nodes.all? { |node| node.arguments[0] == table_name }
256
+ # arguments: [{(sym :table)(str "table")} ...]
257
+ table_name = new_node.arguments[0].value.to_s
258
+ flush unless @nodes.all? do |node|
259
+ node.arguments[0].value.to_s == table_name
260
+ end
257
261
  @nodes << new_node
258
262
  end
259
263