rubocop 0.79.0 → 0.80.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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -3
  4. data/config/default.yml +33 -19
  5. data/lib/rubocop.rb +5 -1
  6. data/lib/rubocop/ast/node.rb +0 -12
  7. data/lib/rubocop/ast/node/regexp_node.rb +2 -4
  8. data/lib/rubocop/ast/traversal.rb +9 -0
  9. data/lib/rubocop/comment_config.rb +6 -1
  10. data/lib/rubocop/config_obsoletion.rb +2 -1
  11. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -1
  12. data/lib/rubocop/cop/layout/leading_comment_space.rb +33 -2
  13. data/lib/rubocop/cop/layout/line_length.rb +30 -1
  14. data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +0 -4
  15. data/lib/rubocop/cop/layout/space_around_operators.rb +18 -0
  16. data/lib/rubocop/cop/layout/space_before_first_arg.rb +8 -0
  17. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -9
  18. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  19. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +12 -7
  20. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -0
  21. data/lib/rubocop/cop/migration/department_name.rb +14 -1
  22. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +4 -0
  23. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +6 -0
  24. data/lib/rubocop/cop/mixin/hash_transform_method.rb +172 -0
  25. data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -9
  26. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  27. data/lib/rubocop/cop/style/block_delimiters.rb +60 -1
  28. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +89 -11
  29. data/lib/rubocop/cop/style/hash_each_methods.rb +87 -0
  30. data/lib/rubocop/cop/style/hash_transform_keys.rb +79 -0
  31. data/lib/rubocop/cop/style/hash_transform_values.rb +79 -0
  32. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +5 -0
  33. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +5 -4
  34. data/lib/rubocop/cop/style/or_assignment.rb +3 -2
  35. data/lib/rubocop/cop/style/symbol_array.rb +2 -2
  36. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  37. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +0 -22
  38. data/lib/rubocop/cop/variable_force.rb +4 -1
  39. data/lib/rubocop/formatter/formatter_set.rb +1 -0
  40. data/lib/rubocop/formatter/junit_formatter.rb +63 -0
  41. data/lib/rubocop/node_pattern.rb +96 -10
  42. data/lib/rubocop/version.rb +1 -1
  43. metadata +21 -3
  44. data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +0 -209
@@ -54,6 +54,7 @@ module RuboCop
54
54
 
55
55
  def expect_params_after_method_name?(node)
56
56
  return false if node.parenthesized?
57
+ return true if no_space_between_method_name_and_first_argument?(node)
57
58
 
58
59
  first_arg = node.first_argument
59
60
 
@@ -61,6 +62,13 @@ module RuboCop
61
62
  !(allow_for_alignment? &&
62
63
  aligned_with_something?(first_arg.source_range))
63
64
  end
65
+
66
+ def no_space_between_method_name_and_first_argument?(node)
67
+ end_pos_of_method_name = node.loc.selector.end_pos
68
+ begin_pos_of_argument = node.first_argument.source_range.begin_pos
69
+
70
+ end_pos_of_method_name == begin_pos_of_argument
71
+ end
64
72
  end
65
73
  end
66
74
  end
@@ -83,17 +83,10 @@ module RuboCop
83
83
 
84
84
  def autocorrect(range)
85
85
  lambda do |corrector|
86
- # It is possible that BracesAroundHashParameters will remove the
87
- # braces while this cop inserts spaces. This can lead to unwanted
88
- # changes to the inspected code. If we replace the brace with a
89
- # brace plus space (rather than just inserting a space), then any
90
- # removal of the same brace will give us a clobbering error. This
91
- # in turn will make RuboCop fall back on cop-by-cop
92
- # auto-correction. Problem solved.
93
86
  case range.source
94
87
  when /\s/ then corrector.remove(range)
95
- when '{' then corrector.replace(range, '{ ')
96
- else corrector.replace(range, ' }')
88
+ when '{' then corrector.insert_after(range, ' ')
89
+ else corrector.insert_before(range, ' ')
97
90
  end
98
91
  end
99
92
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  PATTERN
44
44
 
45
45
  def_node_matcher :debugger_call?, <<~PATTERN
46
- {(send {nil? #kernel?} {:debugger :byebug :remote_byebug :console} ...)
46
+ {(send {nil? #kernel?} {:debugger :byebug :remote_byebug} ...)
47
47
  (send (send {#kernel? nil?} :binding)
48
48
  {:pry :remote_pry :pry_remote :console} ...)
49
49
  (send (const {nil? (cbase)} :Pry) :rescue ...)
@@ -1,9 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # The Lint/RedundantCopEnableDirective cop needs to be disabled so as
4
- # to be able to provide a (bad) example of an unneeded enable.
3
+ # The Lint/RedundantCopEnableDirective and Lint/RedundantCopDisableDirective
4
+ # cops need to be disabled so as to be able to provide a (bad) example of an
5
+ # unneeded enable.
5
6
 
6
7
  # rubocop:disable Lint/RedundantCopEnableDirective
8
+ # rubocop:disable Lint/RedundantCopDisableDirective
7
9
  module RuboCop
8
10
  module Cop
9
11
  module Lint
@@ -21,15 +23,15 @@ module RuboCop
21
23
  # foo = 1
22
24
  # @example
23
25
  # # bad
24
- # # rubocop:disable Layout/LineLength
25
- # baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarrrrrrrrrrrrr
26
- # # rubocop:enable Layout/LineLength
26
+ # # rubocop:disable Style/StringLiterals
27
+ # foo = "1"
28
+ # # rubocop:enable Style/StringLiterals
27
29
  # baz
28
30
  # # rubocop:enable all
29
31
  #
30
32
  # # good
31
- # # rubocop:disable Layout/LineLength
32
- # baaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaarrrrrrrrrrrrr
33
+ # # rubocop:disable Style/StringLiterals
34
+ # foo = "1"
33
35
  # # rubocop:enable all
34
36
  # baz
35
37
  class RedundantCopEnableDirective < Cop
@@ -112,3 +114,6 @@ module RuboCop
112
114
  end
113
115
  end
114
116
  end
117
+
118
+ # rubocop:enable Lint/RedundantCopDisableDirective
119
+ # rubocop:enable Lint/RedundantCopEnableDirective
@@ -6,6 +6,10 @@ module RuboCop
6
6
  # This cop checks for setter call to local variable as the final
7
7
  # expression of a function definition.
8
8
  #
9
+ # Note: There are edge cases in which the local variable references a
10
+ # value that is also accessible outside the local scope. This is not
11
+ # detected by the cop, and it can yield a false positive.
12
+ #
9
13
  # @example
10
14
  #
11
15
  # # bad
@@ -35,8 +35,13 @@ module RuboCop
35
35
 
36
36
  def autocorrect(range)
37
37
  shall_warn = false
38
- qualified_cop_name = Cop.registry.qualified_cop_name(range.source,
38
+ cop_name = range.source
39
+ qualified_cop_name = Cop.registry.qualified_cop_name(cop_name,
39
40
  nil, shall_warn)
41
+ unless qualified_cop_name.include?('/')
42
+ qualified_cop_name = qualified_legacy_cop_name(cop_name)
43
+ end
44
+
40
45
  ->(corrector) { corrector.replace(range, qualified_cop_name) }
41
46
  end
42
47
 
@@ -53,6 +58,14 @@ module RuboCop
53
58
  def valid_content_token?(content_token)
54
59
  !DISABLING_COPS_CONTENT_TOKEN.match(content_token).nil?
55
60
  end
61
+
62
+ def qualified_legacy_cop_name(cop_name)
63
+ legacy_cop_names = RuboCop::ConfigObsoletion::OBSOLETE_COPS.keys
64
+
65
+ legacy_cop_names.detect do |legacy_cop_name|
66
+ legacy_cop_name.split('/')[1] == cop_name
67
+ end
68
+ end
56
69
  end
57
70
  end
58
71
  end
@@ -60,6 +60,10 @@ module RuboCop
60
60
  alias conflicting_styles_detected no_acceptable_style!
61
61
  alias unrecognized_style_detected no_acceptable_style!
62
62
 
63
+ def style_configured?
64
+ cop_config.key?(style_parameter_name)
65
+ end
66
+
63
67
  def style
64
68
  @style ||= begin
65
69
  s = cop_config[style_parameter_name].to_sym
@@ -39,6 +39,12 @@ module RuboCop
39
39
  end
40
40
  end
41
41
 
42
+ def frozen_string_literal_specified?
43
+ leading_comment_lines.any? do |line|
44
+ MagicComment.parse(line).frozen_string_literal_specified?
45
+ end
46
+ end
47
+
42
48
  def leading_comment_lines
43
49
  processed_source.comments.first(3).map(&:text)
44
50
  end
@@ -0,0 +1,172 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ # Common functionality for Style/HashTransformKeys and
6
+ # Style/HashTransformValues
7
+ module HashTransformMethod
8
+ def on_block(node)
9
+ on_bad_each_with_object(node) do |*match|
10
+ handle_possible_offense(node, match, 'each_with_object')
11
+ end
12
+ end
13
+
14
+ def on_send(node)
15
+ on_bad_hash_brackets_map(node) do |*match|
16
+ handle_possible_offense(node, match, 'Hash[_.map {...}]')
17
+ end
18
+ on_bad_map_to_h(node) do |*match|
19
+ handle_possible_offense(node, match, 'map {...}.to_h')
20
+ end
21
+ end
22
+
23
+ def on_csend(node)
24
+ on_bad_map_to_h(node) do |*match|
25
+ handle_possible_offense(node, match, 'map {...}.to_h')
26
+ end
27
+ end
28
+
29
+ def autocorrect(node)
30
+ lambda do |corrector|
31
+ correction = prepare_correction(node)
32
+ execute_correction(corrector, node, correction)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ # @abstract Implemented with `def_node_matcher`
39
+ def on_bad_each_with_object(_node)
40
+ raise NotImplementedError
41
+ end
42
+
43
+ # @abstract Implemented with `def_node_matcher`
44
+ def on_bad_hash_brackets_map(_node)
45
+ raise NotImplementedError
46
+ end
47
+
48
+ # @abstract Implemented with `def_node_matcher`
49
+ def on_bad_map_to_h(_node)
50
+ raise NotImplementedError
51
+ end
52
+
53
+ def handle_possible_offense(node, match, match_desc)
54
+ puts node.class
55
+ captures = extract_captures(match)
56
+
57
+ # If key didn't actually change either, this is most likely a false
58
+ # positive (receiver isn't a hash).
59
+ return if captures.noop_transformation?
60
+
61
+ # Can't `transform_keys` if key transformation uses value, or
62
+ # `transform_values` if value transformation uses key.
63
+ return if captures.transformation_uses_both_args?
64
+
65
+ add_offense(
66
+ node,
67
+ message: "Prefer `#{new_method_name}` over `#{match_desc}`."
68
+ )
69
+ end
70
+
71
+ # @abstract
72
+ #
73
+ # @return [Captures]
74
+ def extract_captures(_match)
75
+ raise NotImplementedError
76
+ end
77
+
78
+ # @abstract
79
+ #
80
+ # @return [String]
81
+ def new_method_name
82
+ raise NotImplementedError
83
+ end
84
+
85
+ def prepare_correction(node)
86
+ if (match = on_bad_each_with_object(node))
87
+ Autocorrection.from_each_with_object(node, match)
88
+ elsif (match = on_bad_hash_brackets_map(node))
89
+ Autocorrection.from_hash_brackets_map(node, match)
90
+ elsif (match = on_bad_map_to_h(node))
91
+ Autocorrection.from_map_to_h(node, match)
92
+ else
93
+ raise 'unreachable'
94
+ end
95
+ end
96
+
97
+ def execute_correction(corrector, node, correction)
98
+ correction.strip_prefix_and_suffix(node, corrector)
99
+ correction.set_new_method_name(new_method_name, corrector)
100
+
101
+ captures = extract_captures(correction.match)
102
+ correction.set_new_arg_name(captures.transformed_argname, corrector)
103
+ correction.set_new_body_expression(
104
+ captures.transforming_body_expr,
105
+ corrector
106
+ )
107
+ end
108
+
109
+ # Internal helper class to hold match data
110
+ Captures = Struct.new(
111
+ :transformed_argname,
112
+ :transforming_body_expr,
113
+ :unchanged_body_expr
114
+ ) do
115
+ def noop_transformation?
116
+ transforming_body_expr.lvar_type? &&
117
+ transforming_body_expr.children == [transformed_argname]
118
+ end
119
+
120
+ def transformation_uses_both_args?
121
+ transforming_body_expr.descendants.include?(unchanged_body_expr)
122
+ end
123
+ end
124
+
125
+ # Internal helper class to hold autocorrect data
126
+ Autocorrection = Struct.new(:match, :block_node, :leading, :trailing) do # rubocop:disable Metrics/BlockLength
127
+ def self.from_each_with_object(node, match)
128
+ new(match, node, 0, 0)
129
+ end
130
+
131
+ def self.from_hash_brackets_map(node, match)
132
+ new(match, node.children.last, 'Hash['.length, ']'.length)
133
+ end
134
+
135
+ def self.from_map_to_h(node, match)
136
+ strip_trailing_chars = node.parent&.block_type? ? 0 : '.to_h'.length
137
+ new(match, node.children.first, 0, strip_trailing_chars)
138
+ end
139
+
140
+ def strip_prefix_and_suffix(node, corrector)
141
+ expression = node.loc.expression
142
+ corrector.remove_leading(expression, leading)
143
+ corrector.remove_trailing(expression, trailing)
144
+ end
145
+
146
+ def set_new_method_name(new_method_name, corrector)
147
+ range = block_node.send_node.loc.selector
148
+ if (send_end = block_node.send_node.loc.end)
149
+ # If there are arguments (only true in the `each_with_object`
150
+ # case)
151
+ range = range.begin.join(send_end)
152
+ end
153
+ corrector.replace(range, new_method_name)
154
+ end
155
+
156
+ def set_new_arg_name(transformed_argname, corrector)
157
+ corrector.replace(
158
+ block_node.arguments.loc.expression,
159
+ "|#{transformed_argname}|"
160
+ )
161
+ end
162
+
163
+ def set_new_body_expression(transforming_body_expr, corrector)
164
+ corrector.replace(
165
+ block_node.body.loc.expression,
166
+ transforming_body_expr.loc.expression.source
167
+ )
168
+ end
169
+ end
170
+ end
171
+ end
172
+ end
@@ -23,7 +23,7 @@ module RuboCop
23
23
  if comma_offset && !inside_comment?(after_last_item, comma_offset)
24
24
  check_comma(node, kind, after_last_item.begin_pos + comma_offset)
25
25
  elsif should_have_comma?(style, node)
26
- put_comma(node, items, kind)
26
+ put_comma(items, kind)
27
27
  end
28
28
  end
29
29
 
@@ -145,9 +145,7 @@ module RuboCop
145
145
  add_offense(range, location: range, message: msg)
146
146
  end
147
147
 
148
- def put_comma(node, items, kind)
149
- return if avoid_autocorrect?(elements(node))
150
-
148
+ def put_comma(items, kind)
151
149
  last_item = items.last
152
150
  return if last_item.block_pass_type?
153
151
 
@@ -169,11 +167,6 @@ module RuboCop
169
167
  range_between(expr.begin_pos + ix, expr.end_pos)
170
168
  end
171
169
 
172
- # By default, there's no reason to avoid auto-correct.
173
- def avoid_autocorrect?(_nodes)
174
- false
175
- end
176
-
177
170
  def any_heredoc?(items)
178
171
  items.any? { |item| heredoc?(item) }
179
172
  end
@@ -10,7 +10,7 @@ module RuboCop
10
10
  # directive. It can be configured to allow for memoized instance variables
11
11
  # prefixed with an underscore. Prefixing ivars with an underscore is a
12
12
  # convention that is used to implicitly indicate that an ivar should not
13
- # be set or referencd outside of the memoization method.
13
+ # be set or referenced outside of the memoization method.
14
14
  #
15
15
  # @example EnforcedStyleForLeadingUnderscores: disallowed (default)
16
16
  # # bad
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # rubocop:disable Metrics/ClassLength
3
4
  module RuboCop
4
5
  module Cop
5
6
  module Style
@@ -106,12 +107,41 @@ module RuboCop
106
107
  # word.flip.flop
107
108
  # }
108
109
  #
110
+ # @example BracesRequiredMethods: ['sig']
111
+ #
112
+ # # Methods listed in the BracesRequiredMethods list, such as 'sig'
113
+ # # in this example, will require `{...}` braces. This option takes
114
+ # # precedence over all other configurations except IgnoredMethods.
115
+ #
116
+ # # bad
117
+ # sig do
118
+ # params(
119
+ # foo: string,
120
+ # ).void
121
+ # end
122
+ # def bar(foo)
123
+ # puts foo
124
+ # end
125
+ #
126
+ # # good
127
+ # sig {
128
+ # params(
129
+ # foo: string,
130
+ # ).void
131
+ # }
132
+ # def bar(foo)
133
+ # puts foo
134
+ # end
135
+ #
109
136
  class BlockDelimiters < Cop
110
137
  include ConfigurableEnforcedStyle
111
138
  include IgnoredMethods
112
139
 
113
140
  ALWAYS_BRACES_MESSAGE = 'Prefer `{...}` over `do...end` for blocks.'
114
141
 
142
+ BRACES_REQUIRED_MESSAGE = 'Brace delimiters `{...}` required for ' \
143
+ "'%<method_name>s' method."
144
+
115
145
  def on_send(node)
116
146
  return unless node.arguments?
117
147
  return if node.parenthesized?
@@ -175,7 +205,15 @@ module RuboCop
175
205
  end
176
206
  end
177
207
 
208
+ def braces_required_message(node)
209
+ format(BRACES_REQUIRED_MESSAGE, method_name: node.method_name.to_s)
210
+ end
211
+
178
212
  def message(node)
213
+ if braces_required_method?(node.method_name)
214
+ return braces_required_message(node)
215
+ end
216
+
179
217
  case style
180
218
  when :line_count_based then line_count_based_message(node)
181
219
  when :semantic then semantic_message(node)
@@ -238,7 +276,9 @@ module RuboCop
238
276
  # rubocop:enable Metrics/CyclomaticComplexity
239
277
 
240
278
  def proper_block_style?(node)
241
- return true if ignored_method?(node.method_name)
279
+ if special_method?(node.method_name)
280
+ return special_method_proper_block_style?(node)
281
+ end
242
282
 
243
283
  case style
244
284
  when :line_count_based then line_count_based_block_style?(node)
@@ -248,6 +288,24 @@ module RuboCop
248
288
  end
249
289
  end
250
290
 
291
+ def special_method?(method_name)
292
+ ignored_method?(method_name) || braces_required_method?(method_name)
293
+ end
294
+
295
+ def special_method_proper_block_style?(node)
296
+ method_name = node.method_name
297
+ return true if ignored_method?(method_name)
298
+ return node.braces? if braces_required_method?(method_name)
299
+ end
300
+
301
+ def braces_required_method?(method_name)
302
+ braces_required_methods.include?(method_name.to_s)
303
+ end
304
+
305
+ def braces_required_methods
306
+ cop_config.fetch('BracesRequiredMethods', [])
307
+ end
308
+
251
309
  def line_count_based_block_style?(node)
252
310
  node.multiline? ^ node.braces?
253
311
  end
@@ -329,3 +387,4 @@ module RuboCop
329
387
  end
330
388
  end
331
389
  end
390
+ # rubocop:enable Metrics/ClassLength