rubocop 0.79.0 → 0.80.0

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