rubocop 1.8.1 → 1.9.1

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 (60) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +37 -4
  4. data/lib/rubocop.rb +6 -0
  5. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  6. data/lib/rubocop/config.rb +5 -2
  7. data/lib/rubocop/config_loader.rb +7 -14
  8. data/lib/rubocop/config_store.rb +12 -1
  9. data/lib/rubocop/cop/base.rb +2 -1
  10. data/lib/rubocop/cop/exclude_limit.rb +26 -0
  11. data/lib/rubocop/cop/generator.rb +1 -3
  12. data/lib/rubocop/cop/internal_affairs.rb +5 -1
  13. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  14. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  15. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  16. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  17. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  18. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +37 -17
  19. data/lib/rubocop/cop/layout/first_argument_indentation.rb +16 -2
  20. data/lib/rubocop/cop/layout/line_length.rb +2 -1
  21. data/lib/rubocop/cop/layout/space_before_brackets.rb +9 -4
  22. data/lib/rubocop/cop/lint/deprecated_constants.rb +5 -0
  23. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  24. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  25. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  26. data/lib/rubocop/cop/lint/symbol_conversion.rb +103 -0
  27. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  28. data/lib/rubocop/cop/message_annotator.rb +4 -1
  29. data/lib/rubocop/cop/metrics/block_nesting.rb +2 -2
  30. data/lib/rubocop/cop/metrics/parameter_lists.rb +5 -2
  31. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  32. data/lib/rubocop/cop/mixin/code_length.rb +3 -1
  33. data/lib/rubocop/cop/mixin/comments_help.rb +0 -1
  34. data/lib/rubocop/cop/mixin/configurable_max.rb +1 -0
  35. data/lib/rubocop/cop/mixin/method_complexity.rb +3 -1
  36. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  37. data/lib/rubocop/cop/naming/variable_number.rb +1 -1
  38. data/lib/rubocop/cop/severity.rb +3 -3
  39. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  40. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  41. data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
  42. data/lib/rubocop/cop/style/float_division.rb +3 -0
  43. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  44. data/lib/rubocop/cop/style/if_inside_else.rb +14 -7
  45. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +120 -0
  46. data/lib/rubocop/cop/style/nil_comparison.rb +3 -0
  47. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  48. data/lib/rubocop/cop/style/numeric_literals.rb +6 -9
  49. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  50. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  51. data/lib/rubocop/cop/style/sole_nested_conditional.rb +26 -2
  52. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  53. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  54. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -1
  55. data/lib/rubocop/magic_comment.rb +30 -1
  56. data/lib/rubocop/options.rb +1 -1
  57. data/lib/rubocop/rspec/expect_offense.rb +5 -2
  58. data/lib/rubocop/runner.rb +1 -0
  59. data/lib/rubocop/version.rb +2 -2
  60. metadata +13 -3
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks that RSpec examples that use `expects_offense`
7
+ # or `expects_no_offenses` do not have conflicting
8
+ # descriptions.
9
+ #
10
+ # @example
11
+ # # bad
12
+ # it 'does not register an offense' do
13
+ # expect_offense('...')
14
+ # end
15
+ #
16
+ # it 'registers an offense' do
17
+ # expect_no_offenses('...')
18
+ # end
19
+ #
20
+ # # good
21
+ # it 'registers an offense' do
22
+ # expect_offense('...')
23
+ # end
24
+ #
25
+ # it 'does not register an offense' do
26
+ # expect_no_offenses('...')
27
+ # end
28
+ class ExampleDescription < Base
29
+ class << self
30
+ attr_accessor :descriptions
31
+ end
32
+
33
+ MSG = 'Description does not match use of `%<method_name>s`.'
34
+
35
+ RESTRICT_ON_SEND = %i[
36
+ expect_offense
37
+ expect_no_offenses
38
+ expect_correction
39
+ expect_no_corrections
40
+ ].to_set.freeze
41
+
42
+ EXPECT_NO_OFFENSES_INCORRECT_DESCRIPTIONS = [
43
+ /^(adds|registers|reports|finds) (an? )?offense/,
44
+ /^flags\b/
45
+ ].freeze
46
+
47
+ EXPECT_OFFENSE_INCORRECT_DESCRIPTIONS = [
48
+ /^(does not|doesn't) (register|find|flag|report)/,
49
+ /^(does not|doesn't) add (a|an|any )?offense/
50
+ ].freeze
51
+
52
+ EXPECT_NO_CORRECTIONS_INCORRECT_DESCRIPTIONS = [
53
+ /^(auto[- ]?)?correct/
54
+ ].freeze
55
+
56
+ EXPECT_CORRECTION_INCORRECT_DESCRIPTIONS = [
57
+ /\b(does not|doesn't) (auto[- ]?)?correct/
58
+ ].freeze
59
+
60
+ def_node_matcher :offense_example?, <<~PATTERN
61
+ (block
62
+ (send _ {:it :specify} $_description)
63
+ _args
64
+ `(send nil? %RESTRICT_ON_SEND ...)
65
+ )
66
+ PATTERN
67
+
68
+ def on_send(node)
69
+ parent = node.each_ancestor(:block).first
70
+ return unless parent && (description = offense_example?(parent))
71
+
72
+ method_name = node.method_name
73
+ message = format(MSG, method_name: method_name)
74
+
75
+ regexp_group = self.class.const_get("#{method_name}_incorrect_descriptions".upcase)
76
+ check_description(description, regexp_group, message)
77
+ end
78
+
79
+ private
80
+
81
+ def check_description(description, regexps, message)
82
+ return unless regexps.any? { |regexp| regexp.match?(description.value) }
83
+
84
+ add_offense(description, message: message)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop checks for redundant `subject(:cop) { described_class.new }`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # RSpec.describe RuboCop::Cop::Department::Foo do
11
+ # subject(:cop) { described_class.new(config) }
12
+ # end
13
+ #
14
+ # # good
15
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
16
+ # end
17
+ #
18
+ class RedundantDescribedClassAsSubject < Base
19
+ include RangeHelp
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Remove the redundant `subject`%<additional_message>s.'
23
+
24
+ def_node_matcher :described_class_subject?, <<~PATTERN
25
+ (block
26
+ (send nil? :subject
27
+ (sym :cop))
28
+ (args)
29
+ (send
30
+ (send nil? :described_class) :new
31
+ $...))
32
+ PATTERN
33
+
34
+ def on_block(node)
35
+ return unless (described_class_arguments = described_class_subject?(node))
36
+ return if described_class_arguments.count >= 2
37
+
38
+ describe = find_describe_method_node(node)
39
+
40
+ unless (exist_config = describe.last_argument.source == ':config')
41
+ additional_message = ' and specify `:config` in `describe`'
42
+ end
43
+
44
+ message = format(MSG, additional_message: additional_message)
45
+
46
+ add_offense(node, message: message) do |corrector|
47
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
48
+
49
+ corrector.insert_after(describe.last_argument, ', :config') unless exist_config
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def find_describe_method_node(block_node)
56
+ block_node.ancestors.find { |node| node.block_type? && node.method?(:describe) }.send_node
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # This cop checks that `let` is `RuboCop::Config.new` with no arguments.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
11
+ # let(:config) { RuboCop::Config.new }
12
+ # end
13
+ #
14
+ # # good
15
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
16
+ # end
17
+ #
18
+ # RSpec.describe RuboCop::Cop::Department::Foo, :config do
19
+ # let(:config) { RuboCop::Config.new(argument) }
20
+ # end
21
+ #
22
+ class RedundantLetRuboCopConfigNew < Base
23
+ include RangeHelp
24
+ extend AutoCorrector
25
+
26
+ MSG = 'Remove `let` that is `RuboCop::Config.new` with no arguments%<additional_message>s.'
27
+
28
+ def_node_matcher :let_rubocop_config_new?, <<~PATTERN
29
+ (block
30
+ (send nil? :let
31
+ (sym :config))
32
+ (args)
33
+ (send
34
+ (const
35
+ (const nil? :RuboCop) :Config) :new))
36
+ PATTERN
37
+
38
+ def on_block(node)
39
+ return unless let_rubocop_config_new?(node)
40
+
41
+ describe = find_describe_method_node(node)
42
+
43
+ unless (exist_config = describe.last_argument.source == ':config')
44
+ additional_message = ' and specify `:config` in `describe`'
45
+ end
46
+
47
+ message = format(MSG, additional_message: additional_message)
48
+
49
+ add_offense(node, message: message) do |corrector|
50
+ corrector.remove(range_by_whole_lines(node.source_range, include_final_newline: true))
51
+
52
+ corrector.insert_after(describe.last_argument, ', :config') unless exist_config
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def find_describe_method_node(block_node)
59
+ block_node.ancestors.find { |node| node.block_type? && node.method?(:describe) }.send_node
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -213,7 +213,12 @@ module RuboCop
213
213
  name = node.method_name.to_s
214
214
  category, = categories.find { |_, names| names.include?(name) }
215
215
  key = category || name
216
- visibility_key = "#{node_visibility(node)}_#{key}"
216
+ visibility_key =
217
+ if node.def_modifier?
218
+ "#{name}_methods"
219
+ else
220
+ "#{node_visibility(node)}_#{key}"
221
+ end
217
222
  expected_order.include?(visibility_key) ? visibility_key : key
218
223
  end
219
224
 
@@ -264,7 +269,7 @@ module RuboCop
264
269
 
265
270
  def source_range_with_comment(node)
266
271
  begin_pos, end_pos =
267
- if node.def_type?
272
+ if node.def_type? && !node.method?(:initialize) || node.send_type? && node.def_modifier?
268
273
  start_node = find_visibility_start(node) || node
269
274
  end_node = find_visibility_end(node) || node
270
275
  [begin_pos_with_comment(start_node),
@@ -88,7 +88,7 @@ module RuboCop
88
88
  include RangeHelp
89
89
  extend AutoCorrector
90
90
 
91
- MSG = 'Use empty lines between %<type>s definitions.'
91
+ MSG = 'Expected %<expected>s between %<type>s definitions; found %<actual>d.'
92
92
 
93
93
  def self.autocorrect_incompatible_with
94
94
  [Layout::EmptyLines]
@@ -107,19 +107,21 @@ module RuboCop
107
107
  end
108
108
 
109
109
  def check_defs(nodes)
110
- return if blank_lines_between?(*nodes)
110
+ count = blank_lines_count_between(*nodes)
111
+
112
+ return if line_count_allowed?(count)
111
113
  return if multiple_blank_lines_groups?(*nodes)
112
114
  return if nodes.all?(&:single_line?) &&
113
115
  cop_config['AllowAdjacentOneLineDefs']
114
116
 
115
117
  correction_node = nodes.last
116
118
  location = correction_node.loc.keyword.join(correction_node.loc.name)
117
- add_offense(location, message: message(correction_node)) do |corrector|
118
- autocorrect(corrector, *nodes)
119
+ add_offense(location, message: message(correction_node, count: count)) do |corrector|
120
+ autocorrect(corrector, *nodes, count)
119
121
  end
120
122
  end
121
123
 
122
- def autocorrect(corrector, prev_def, node)
124
+ def autocorrect(corrector, prev_def, node, count)
123
125
  # finds position of first newline
124
126
  end_pos = end_loc(prev_def).end_pos
125
127
  source_buffer = end_loc(prev_def).source_buffer
@@ -128,8 +130,6 @@ module RuboCop
128
130
  # Handle the case when multiple one-liners are on the same line.
129
131
  newline_pos = end_pos + 1 if newline_pos > node.source_range.begin_pos
130
132
 
131
- count = blank_lines_count_between(prev_def, node)
132
-
133
133
  if count > maximum_empty_lines
134
134
  autocorrect_remove_lines(corrector, newline_pos, count)
135
135
  else
@@ -157,14 +157,22 @@ module RuboCop
157
157
  cop_config['EmptyLineBetweenModuleDefs'] && node.module_type?
158
158
  end
159
159
 
160
- def message(node)
161
- type = case node.type
162
- when :def, :defs
163
- :method
164
- else
165
- node.type
166
- end
167
- format(MSG, type: type)
160
+ def message(node, count: nil)
161
+ type = node_type(node)
162
+
163
+ format(MSG,
164
+ type: type,
165
+ expected: expected_lines,
166
+ actual: count)
167
+ end
168
+
169
+ def expected_lines
170
+ if allowance_range?
171
+ "#{minimum_empty_lines..maximum_empty_lines} empty lines"
172
+ else
173
+ lines = maximum_empty_lines == 1 ? 'line' : 'lines'
174
+ "#{maximum_empty_lines} empty #{lines}"
175
+ end
168
176
  end
169
177
 
170
178
  def multiple_blank_lines_groups?(first_def_node, second_def_node)
@@ -176,8 +184,7 @@ module RuboCop
176
184
  blank_start > non_blank_end
177
185
  end
178
186
 
179
- def blank_lines_between?(first_def_node, second_def_node)
180
- count = blank_lines_count_between(first_def_node, second_def_node)
187
+ def line_count_allowed?(count)
181
188
  (minimum_empty_lines..maximum_empty_lines).cover?(count)
182
189
  end
183
190
 
@@ -230,6 +237,19 @@ module RuboCop
230
237
 
231
238
  corrector.insert_after(where_to_insert, "\n" * difference)
232
239
  end
240
+
241
+ def node_type(node)
242
+ case node.type
243
+ when :def, :defs
244
+ :method
245
+ else
246
+ node.type
247
+ end
248
+ end
249
+
250
+ def allowance_range?
251
+ minimum_empty_lines != maximum_empty_lines
252
+ end
233
253
  end
234
254
  end
235
255
  end
@@ -4,11 +4,14 @@ module RuboCop
4
4
  module Cop
5
5
  module Layout
6
6
  # This cop checks the indentation of the first argument in a method call.
7
- # Arguments after the first one are checked by Layout/ArgumentAlignment,
7
+ # Arguments after the first one are checked by `Layout/ArgumentAlignment`,
8
8
  # not by this cop.
9
9
  #
10
10
  # For indenting the first parameter of method _definitions_, check out
11
- # Layout/FirstParameterIndentation.
11
+ # `Layout/FirstParameterIndentation`.
12
+ #
13
+ # This cop will respect `Layout/ArgumentAlignment` and will not work when
14
+ # `EnforcedStyle: with_fixed_indentation` is specified for `Layout/ArgumentAlignment`.
12
15
  #
13
16
  # @example
14
17
  #
@@ -149,6 +152,7 @@ module RuboCop
149
152
  MSG = 'Indent the first argument one step more than %<base>s.'
150
153
 
151
154
  def on_send(node)
155
+ return if enforce_first_argument_with_fixed_indentation?
152
156
  return if !node.arguments? || node.operator_method?
153
157
 
154
158
  indent = base_indentation(node) + configured_indentation_width
@@ -250,6 +254,16 @@ module RuboCop
250
254
  def on_new_investigation
251
255
  @comment_lines = nil
252
256
  end
257
+
258
+ def enforce_first_argument_with_fixed_indentation?
259
+ return false unless argument_alignment_config['Enabled']
260
+
261
+ argument_alignment_config['EnforcedStyle'] == 'with_fixed_indentation'
262
+ end
263
+
264
+ def argument_alignment_config
265
+ config.for_cop('Layout/ArgumentAlignment')
266
+ end
253
267
  end
254
268
  end
255
269
  end
@@ -60,12 +60,13 @@ module RuboCop
60
60
  # }
61
61
  class LineLength < Base
62
62
  include CheckLineBreakable
63
- include ConfigurableMax
64
63
  include IgnoredPattern
65
64
  include RangeHelp
66
65
  include LineLengthHelp
67
66
  extend AutoCorrector
68
67
 
68
+ exclude_limit 'Max'
69
+
69
70
  MSG = 'Line is too long. [%<length>d/%<max>d]'
70
71
 
71
72
  def on_block(node)
@@ -39,12 +39,17 @@ module RuboCop
39
39
 
40
40
  range_between(receiver_end_pos, selector_begin_pos)
41
41
  elsif node.method?(:[]=)
42
- end_pos = node.receiver.source_range.end_pos
42
+ offense_range_for_assignment(node, begin_pos)
43
+ end
44
+ end
43
45
 
44
- return if begin_pos - end_pos == 1
46
+ def offense_range_for_assignment(node, begin_pos)
47
+ end_pos = node.receiver.source_range.end_pos
45
48
 
46
- range_between(end_pos, begin_pos - 1)
47
- end
49
+ return if begin_pos - end_pos == 1 ||
50
+ (range = range_between(end_pos, begin_pos - 1)).source == '['
51
+
52
+ range
48
53
  end
49
54
 
50
55
  def register_offense(range)
@@ -37,6 +37,11 @@ module RuboCop
37
37
  DO_NOT_USE_MSG = 'Do not use `%<bad>s`%<deprecated_message>s.'
38
38
 
39
39
  def on_const(node)
40
+ # FIXME: Workaround for "`undefined method `expression' for nil:NilClass`" when processing
41
+ # `__ENCODING__`. It is better to be able to work without this condition.
42
+ # Maybe further investigation of RuboCop AST will lead to an essential solution.
43
+ return unless node.loc
44
+
40
45
  constant = node.absolute? ? consntant_name(node, node.short_name.to_s) : node.source
41
46
  return unless (deprecated_constant = deprecated_constants[constant])
42
47
 
@@ -25,12 +25,18 @@ module RuboCop
25
25
  # '10'.to_i
26
26
  # '10.2'.to_f
27
27
  # '10'.to_c
28
+ # ['1', '2', '3'].map(&:to_i)
29
+ # foo.try(:to_f)
30
+ # bar.send(:to_c)
28
31
  #
29
32
  # # good
30
33
  #
31
34
  # Integer('10', 10)
32
35
  # Float('10.2')
33
36
  # Complex('10')
37
+ # ['1', '2', '3'].map { |i| Integer(i, 10) }
38
+ # foo.try { |i| Float(i) }
39
+ # bar.send { |i| Complex(i) }
34
40
  #
35
41
  # @example IgnoredMethods: [minutes]
36
42
  #
@@ -52,22 +58,33 @@ module RuboCop
52
58
  }.freeze
53
59
  MSG = 'Replace unsafe number conversion with number '\
54
60
  'class parsing, instead of using '\
55
- '%<number_object>s.%<to_method>s, use stricter '\
61
+ '%<current>s, use stricter '\
56
62
  '%<corrected_method>s.'
57
- RESTRICT_ON_SEND = CONVERSION_METHOD_CLASS_MAPPING.keys.freeze
63
+ METHODS = CONVERSION_METHOD_CLASS_MAPPING.keys.map(&:inspect).join(' ')
58
64
 
59
65
  def_node_matcher :to_method, <<~PATTERN
60
- (send $_ ${:to_i :to_f :to_c})
66
+ (send $_ ${#{METHODS}})
67
+ PATTERN
68
+
69
+ def_node_matcher :to_method_symbol, <<~PATTERN
70
+ {(send _ $_ ${(sym ${#{METHODS}})} ...)
71
+ (send _ $_ ${(block_pass (sym ${#{METHODS}}))} ...)}
61
72
  PATTERN
62
73
 
63
74
  def on_send(node)
75
+ handle_conversion_method(node)
76
+ handle_as_symbol(node)
77
+ end
78
+
79
+ private
80
+
81
+ def handle_conversion_method(node)
64
82
  to_method(node) do |receiver, to_method|
65
83
  next if receiver.nil? || ignore_receiver?(receiver)
66
84
 
67
85
  message = format(
68
86
  MSG,
69
- number_object: receiver.source,
70
- to_method: to_method,
87
+ current: "#{receiver.source}.#{to_method}",
71
88
  corrected_method: correct_method(node, receiver)
72
89
  )
73
90
  add_offense(node, message: message) do |corrector|
@@ -76,13 +93,31 @@ module RuboCop
76
93
  end
77
94
  end
78
95
 
79
- private
96
+ def handle_as_symbol(node)
97
+ to_method_symbol(node) do |receiver, sym_node, to_method|
98
+ next if receiver.nil?
99
+
100
+ message = format(
101
+ MSG,
102
+ current: sym_node.source,
103
+ corrected_method: correct_sym_method(to_method)
104
+ )
105
+ add_offense(node, message: message) do |corrector|
106
+ corrector.replace(sym_node, correct_sym_method(to_method))
107
+ end
108
+ end
109
+ end
80
110
 
81
111
  def correct_method(node, receiver)
82
112
  format(CONVERSION_METHOD_CLASS_MAPPING[node.method_name],
83
113
  number_object: receiver.source)
84
114
  end
85
115
 
116
+ def correct_sym_method(to_method)
117
+ body = format(CONVERSION_METHOD_CLASS_MAPPING[to_method], number_object: 'i')
118
+ "{ |i| #{body} }"
119
+ end
120
+
86
121
  def ignore_receiver?(receiver)
87
122
  if receiver.send_type? && ignored_method?(receiver.method_name)
88
123
  true