rubocop 1.34.1 → 1.36.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 (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +33 -0
  4. data/lib/rubocop/cli/command/{auto_genenerate_config.rb → auto_generate_config.rb} +0 -0
  5. data/lib/rubocop/config.rb +1 -1
  6. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +32 -2
  7. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  8. data/lib/rubocop/cop/generator/require_file_injector.rb +2 -2
  9. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +69 -0
  10. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  11. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  12. data/lib/rubocop/cop/layout/block_alignment.rb +16 -12
  13. data/lib/rubocop/cop/layout/block_end_newline.rb +2 -0
  14. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +5 -2
  15. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +2 -0
  16. data/lib/rubocop/cop/layout/end_of_line.rb +4 -4
  17. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
  18. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -2
  19. data/lib/rubocop/cop/layout/indentation_width.rb +5 -1
  20. data/lib/rubocop/cop/layout/line_length.rb +4 -1
  21. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  22. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  23. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  24. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  25. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  26. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +25 -9
  27. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  28. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  29. data/lib/rubocop/cop/lint/duplicate_require.rb +1 -1
  30. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  31. data/lib/rubocop/cop/lint/empty_conditional_body.rb +31 -1
  32. data/lib/rubocop/cop/lint/erb_new_arguments.rb +9 -9
  33. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
  34. data/lib/rubocop/cop/lint/next_without_accumulator.rb +25 -6
  35. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +6 -6
  36. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +12 -0
  37. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +9 -3
  38. data/lib/rubocop/cop/lint/redundant_with_index.rb +13 -10
  39. data/lib/rubocop/cop/lint/redundant_with_object.rb +12 -11
  40. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  41. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +15 -2
  42. data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
  43. data/lib/rubocop/cop/lint/useless_access_modifier.rb +8 -6
  44. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  45. data/lib/rubocop/cop/lint/void.rb +2 -0
  46. data/lib/rubocop/cop/mixin/allowed_methods.rb +10 -5
  47. data/lib/rubocop/cop/mixin/allowed_pattern.rb +13 -5
  48. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  49. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +76 -1
  50. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -6
  51. data/lib/rubocop/cop/mixin/method_complexity.rb +4 -4
  52. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  53. data/lib/rubocop/cop/mixin/range_help.rb +2 -3
  54. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  55. data/lib/rubocop/cop/naming/constant_name.rb +2 -2
  56. data/lib/rubocop/cop/style/access_modifier_declarations.rb +77 -1
  57. data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -2
  58. data/lib/rubocop/cop/style/case_equality.rb +40 -10
  59. data/lib/rubocop/cop/style/class_methods_definitions.rb +2 -1
  60. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  61. data/lib/rubocop/cop/style/combinable_loops.rb +3 -1
  62. data/lib/rubocop/cop/style/each_for_simple_loop.rb +41 -6
  63. data/lib/rubocop/cop/style/each_with_object.rb +39 -8
  64. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  65. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  66. data/lib/rubocop/cop/style/for.rb +2 -0
  67. data/lib/rubocop/cop/style/guard_clause.rb +27 -16
  68. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -1
  69. data/lib/rubocop/cop/style/hash_syntax.rb +17 -0
  70. data/lib/rubocop/cop/style/inverse_methods.rb +8 -6
  71. data/lib/rubocop/cop/style/magic_comment_format.rb +307 -0
  72. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  73. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +4 -1
  74. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -1
  75. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  76. data/lib/rubocop/cop/style/next.rb +3 -5
  77. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  78. data/lib/rubocop/cop/style/object_then.rb +2 -0
  79. data/lib/rubocop/cop/style/perl_backrefs.rb +22 -1
  80. data/lib/rubocop/cop/style/proc.rb +4 -1
  81. data/lib/rubocop/cop/style/redundant_begin.rb +2 -0
  82. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  83. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  84. data/lib/rubocop/cop/style/redundant_self.rb +2 -0
  85. data/lib/rubocop/cop/style/redundant_sort_by.rb +24 -8
  86. data/lib/rubocop/cop/style/safe_navigation.rb +4 -2
  87. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  88. data/lib/rubocop/cop/style/sole_nested_conditional.rb +0 -2
  89. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  90. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  91. data/lib/rubocop/cop/style/top_level_method_definition.rb +3 -1
  92. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  93. data/lib/rubocop/cop/style/word_array.rb +1 -1
  94. data/lib/rubocop/cop/util.rb +1 -1
  95. data/lib/rubocop/feature_loader.rb +6 -2
  96. data/lib/rubocop/formatter/html_formatter.rb +2 -2
  97. data/lib/rubocop/runner.rb +4 -0
  98. data/lib/rubocop/server/cache.rb +11 -8
  99. data/lib/rubocop/version.rb +3 -2
  100. data/lib/rubocop.rb +3 -2
  101. metadata +9 -7
@@ -59,18 +59,18 @@ module RuboCop
59
59
  def_node_matcher :inverse_candidate?, <<~PATTERN
60
60
  {
61
61
  (send $(send $(...) $_ $...) :!)
62
- (send (block $(send $(...) $_) $...) :!)
62
+ (send ({block numblock} $(send $(...) $_) $...) :!)
63
63
  (send (begin $(send $(...) $_ $...)) :!)
64
64
  }
65
65
  PATTERN
66
66
 
67
67
  # @!method inverse_block?(node)
68
68
  def_node_matcher :inverse_block?, <<~PATTERN
69
- (block $(send (...) $_) ... { $(send ... :!)
70
- $(send (...) {:!= :!~} ...)
71
- (begin ... $(send ... :!))
72
- (begin ... $(send (...) {:!= :!~} ...))
73
- })
69
+ ({block numblock} $(send (...) $_) ... { $(send ... :!)
70
+ $(send (...) {:!= :!~} ...)
71
+ (begin ... $(send ... :!))
72
+ (begin ... $(send (...) {:!= :!~} ...))
73
+ })
74
74
  PATTERN
75
75
 
76
76
  def on_send(node)
@@ -102,6 +102,8 @@ module RuboCop
102
102
  end
103
103
  end
104
104
 
105
+ alias on_numblock on_block
106
+
105
107
  private
106
108
 
107
109
  def correct_inverse_method(corrector, node)
@@ -0,0 +1,307 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Ensures magic comments are written consistently throughout your code base.
7
+ # Looks for discrepancies in separators (`-` vs `_`) and capitalization for
8
+ # both magic comment directives and values.
9
+ #
10
+ # Required capitalization can be set with the `DirectiveCapitalization` and
11
+ # `ValueCapitalization` configuration keys.
12
+ #
13
+ # NOTE: If one of these configuration is set to nil, any capitalization is allowed.
14
+ #
15
+ # @example EnforcedStyle: snake_case (default)
16
+ # # The `snake_case` style will enforce that the frozen string literal
17
+ # # comment is written in snake case. (Words separated by underscores)
18
+ # # bad
19
+ # # frozen-string-literal: true
20
+ #
21
+ # module Bar
22
+ # # ...
23
+ # end
24
+ #
25
+ # # good
26
+ # # frozen_string_literal: false
27
+ #
28
+ # module Bar
29
+ # # ...
30
+ # end
31
+ #
32
+ # @example EnforcedStyle: kebab_case
33
+ # # The `kebab_case` style will enforce that the frozen string literal
34
+ # # comment is written in kebab case. (Words separated by hyphens)
35
+ # # bad
36
+ # # frozen_string_literal: true
37
+ #
38
+ # module Baz
39
+ # # ...
40
+ # end
41
+ #
42
+ # # good
43
+ # # frozen-string-literal: true
44
+ #
45
+ # module Baz
46
+ # # ...
47
+ # end
48
+ #
49
+ # @example DirectiveCapitalization: lowercase (default)
50
+ # # bad
51
+ # # FROZEN-STRING-LITERAL: true
52
+ #
53
+ # # good
54
+ # # frozen-string-literal: true
55
+ #
56
+ # @example DirectiveCapitalization: uppercase
57
+ # # bad
58
+ # # frozen-string-literal: true
59
+ #
60
+ # # good
61
+ # # FROZEN-STRING-LITERAL: true
62
+ #
63
+ # @example DirectiveCapitalization: nil
64
+ # # any capitalization is accepted
65
+ #
66
+ # # good
67
+ # # frozen-string-literal: true
68
+ #
69
+ # # good
70
+ # # FROZEN-STRING-LITERAL: true
71
+ #
72
+ # @example ValueCapitalization: nil (default)
73
+ # # any capitalization is accepted
74
+ #
75
+ # # good
76
+ # # frozen-string-literal: true
77
+ #
78
+ # # good
79
+ # # frozen-string-literal: TRUE
80
+ #
81
+ # @example ValueCapitalization: lowercase
82
+ # # when a value is not given, any capitalization is accepted
83
+ #
84
+ # # bad
85
+ # # frozen-string-literal: TRUE
86
+ #
87
+ # # good
88
+ # # frozen-string-literal: TRUE
89
+ #
90
+ # @example ValueCapitalization: uppercase
91
+ # # bad
92
+ # # frozen-string-literal: true
93
+ #
94
+ # # good
95
+ # # frozen-string-literal: TRUE
96
+ #
97
+ class MagicCommentFormat < Base
98
+ include ConfigurableEnforcedStyle
99
+ extend AutoCorrector
100
+
101
+ SNAKE_SEPARATOR = '_'
102
+ KEBAB_SEPARATOR = '-'
103
+ MSG = 'Prefer %<style>s case for magic comments.'
104
+ MSG_VALUE = 'Prefer %<case>s for magic comment values.'
105
+
106
+ # Value object to extract source ranges for the different parts of a magic comment
107
+ class CommentRange
108
+ extend Forwardable
109
+
110
+ DIRECTIVE_REGEXP = Regexp.union(MagicComment::KEYWORDS.map do |_, v|
111
+ Regexp.new(v, Regexp::IGNORECASE)
112
+ end).freeze
113
+
114
+ VALUE_REGEXP = Regexp.new("(?:#{DIRECTIVE_REGEXP}:\s*)(.*?)(?=;|$)")
115
+
116
+ def_delegators :@comment, :text, :loc
117
+ attr_reader :comment
118
+
119
+ def initialize(comment)
120
+ @comment = comment
121
+ end
122
+
123
+ # A magic comment can contain one directive (normal style) or
124
+ # multiple directives (emacs style)
125
+ def directives
126
+ @directives ||= begin
127
+ matches = []
128
+
129
+ text.scan(DIRECTIVE_REGEXP) do
130
+ offset = Regexp.last_match.offset(0)
131
+ matches << loc.expression.adjust(begin_pos: offset.first)
132
+ .with(end_pos: loc.expression.begin_pos + offset.last)
133
+ end
134
+
135
+ matches
136
+ end
137
+ end
138
+
139
+ # A magic comment can contain one value (normal style) or
140
+ # multiple directives (emacs style)
141
+ def values
142
+ @values ||= begin
143
+ matches = []
144
+
145
+ text.scan(VALUE_REGEXP) do
146
+ offset = Regexp.last_match.offset(1)
147
+ matches << loc.expression.adjust(begin_pos: offset.first)
148
+ .with(end_pos: loc.expression.begin_pos + offset.last)
149
+ end
150
+
151
+ matches
152
+ end
153
+ end
154
+ end
155
+
156
+ def on_new_investigation
157
+ return unless processed_source.ast
158
+
159
+ magic_comments.each do |comment|
160
+ issues = find_issues(comment)
161
+ register_offenses(issues) if issues.any?
162
+ end
163
+ end
164
+
165
+ private
166
+
167
+ def magic_comments
168
+ processed_source.each_comment_in_lines(leading_comment_lines)
169
+ .select { |comment| MagicComment.parse(comment.text).valid? }
170
+ .map { |comment| CommentRange.new(comment) }
171
+ end
172
+
173
+ def leading_comment_lines
174
+ first_non_comment_token = processed_source.tokens.find { |token| !token.comment? }
175
+
176
+ if first_non_comment_token
177
+ 0...first_non_comment_token.line
178
+ else
179
+ (0..)
180
+ end
181
+ end
182
+
183
+ def find_issues(comment)
184
+ issues = { directives: [], values: [] }
185
+
186
+ comment.directives.each do |directive|
187
+ issues[:directives] << directive if directive_offends?(directive)
188
+ end
189
+
190
+ comment.values.each do |value| # rubocop:disable Style/HashEachMethods
191
+ issues[:values] << value if wrong_capitalization?(value.source, value_capitalization)
192
+ end
193
+
194
+ issues
195
+ end
196
+
197
+ def directive_offends?(directive)
198
+ incorrect_separator?(directive.source) ||
199
+ wrong_capitalization?(directive.source, directive_capitalization)
200
+ end
201
+
202
+ def register_offenses(issues)
203
+ fix_directives(issues[:directives])
204
+ fix_values(issues[:values])
205
+ end
206
+
207
+ def fix_directives(issues)
208
+ return if issues.empty?
209
+
210
+ msg = format(MSG, style: expected_style)
211
+
212
+ issues.each do |directive|
213
+ add_offense(directive, message: msg) do |corrector|
214
+ replacement = replace_separator(replace_capitalization(directive.source,
215
+ directive_capitalization))
216
+ corrector.replace(directive, replacement)
217
+ end
218
+ end
219
+ end
220
+
221
+ def fix_values(issues)
222
+ return if issues.empty?
223
+
224
+ msg = format(MSG_VALUE, case: value_capitalization)
225
+
226
+ issues.each do |value|
227
+ add_offense(value, message: msg) do |corrector|
228
+ corrector.replace(value, replace_capitalization(value.source, value_capitalization))
229
+ end
230
+ end
231
+ end
232
+
233
+ def expected_style
234
+ [directive_capitalization, style].compact.join(' ').gsub(/_?case\b/, '')
235
+ end
236
+
237
+ def wrong_separator
238
+ style == :snake_case ? KEBAB_SEPARATOR : SNAKE_SEPARATOR
239
+ end
240
+
241
+ def correct_separator
242
+ style == :snake_case ? SNAKE_SEPARATOR : KEBAB_SEPARATOR
243
+ end
244
+
245
+ def incorrect_separator?(text)
246
+ text[wrong_separator]
247
+ end
248
+
249
+ def wrong_capitalization?(text, expected_case)
250
+ return false unless expected_case
251
+
252
+ case expected_case
253
+ when :lowercase
254
+ text != text.downcase
255
+ when :uppercase
256
+ text != text.upcase
257
+ end
258
+ end
259
+
260
+ def replace_separator(text)
261
+ text.tr(wrong_separator, correct_separator)
262
+ end
263
+
264
+ def replace_capitalization(text, style)
265
+ return text unless style
266
+
267
+ case style
268
+ when :lowercase
269
+ text.downcase
270
+ when :uppercase
271
+ text.upcase
272
+ end
273
+ end
274
+
275
+ def line_range(line)
276
+ processed_source.buffer.line_range(line)
277
+ end
278
+
279
+ def directive_capitalization
280
+ cop_config['DirectiveCapitalization']&.to_sym.tap do |style|
281
+ unless valid_capitalization?(style)
282
+ raise "Unknown `DirectiveCapitalization` #{style} selected!"
283
+ end
284
+ end
285
+ end
286
+
287
+ def value_capitalization
288
+ cop_config['ValueCapitalization']&.to_sym.tap do |style|
289
+ unless valid_capitalization?(style)
290
+ raise "Unknown `ValueCapitalization` #{style} selected!"
291
+ end
292
+ end
293
+ end
294
+
295
+ def valid_capitalization?(style)
296
+ return true unless style
297
+
298
+ supported_capitalizations.include?(style)
299
+ end
300
+
301
+ def supported_capitalizations
302
+ cop_config['SupportedCapitalizations'].map(&:to_sym)
303
+ end
304
+ end
305
+ end
306
+ end
307
+ end
@@ -130,7 +130,7 @@ module RuboCop
130
130
  call_as_argument_or_chain?(node) ||
131
131
  hash_literal_in_arguments?(node) ||
132
132
  node.descendants.any? do |n|
133
- n.forwarded_args_type? || ambigious_literal?(n) || logical_operator?(n) ||
133
+ n.forwarded_args_type? || ambiguous_literal?(n) || logical_operator?(n) ||
134
134
  call_with_braced_block?(n)
135
135
  end
136
136
  end
@@ -166,7 +166,7 @@ module RuboCop
166
166
  previous.parenthesized? || allowed_chained_call_with_parentheses?(previous)
167
167
  end
168
168
 
169
- def ambigious_literal?(node)
169
+ def ambiguous_literal?(node)
170
170
  splat?(node) || ternary_if?(node) || regexp_slash_literal?(node) || unary_literal?(node)
171
171
  end
172
172
 
@@ -35,12 +35,15 @@ module RuboCop
35
35
  ignore_node(node.send_node)
36
36
  end
37
37
 
38
+ alias on_numblock on_block
39
+
38
40
  def on_send(node)
39
41
  return if ignored_node?(node)
40
42
 
41
43
  receiver = node.receiver
42
44
 
43
- return unless receiver&.block_type? && receiver.loc.end.is?('end')
45
+ return unless (receiver&.block_type? || receiver&.numblock_type?) &&
46
+ receiver.loc.end.is?('end')
44
47
 
45
48
  range = range_between(receiver.loc.end.begin_pos, node.source_range.end_pos)
46
49
 
@@ -31,7 +31,7 @@ module RuboCop
31
31
  node.send_node.each_node(:send) do |send_node|
32
32
  receiver = send_node.receiver
33
33
 
34
- next unless receiver&.block_type? && receiver&.multiline?
34
+ next unless (receiver&.block_type? || receiver&.numblock_type?) && receiver&.multiline?
35
35
 
36
36
  range = range_between(receiver.loc.end.begin_pos, node.send_node.source_range.end_pos)
37
37
 
@@ -42,6 +42,8 @@ module RuboCop
42
42
  break
43
43
  end
44
44
  end
45
+
46
+ alias on_numblock on_block
45
47
  end
46
48
  end
47
49
  end
@@ -49,7 +49,7 @@ module RuboCop
49
49
 
50
50
  # Requires `then` for write `in` and its body on the same line.
51
51
  def require_then?(in_pattern_node)
52
- return true if in_pattern_node.pattern.first_line != in_pattern_node.pattern.last_line
52
+ return true unless in_pattern_node.pattern.single_line?
53
53
  return false unless in_pattern_node.body
54
54
 
55
55
  same_line?(in_pattern_node, in_pattern_node.body)
@@ -71,6 +71,8 @@ module RuboCop
71
71
  check(node)
72
72
  end
73
73
 
74
+ alias on_numblock on_block
75
+
74
76
  def on_while(node)
75
77
  check(node)
76
78
  end
@@ -223,11 +225,7 @@ module RuboCop
223
225
  adjustment = delta + @reindented_lines[lineno]
224
226
  @reindented_lines[lineno] = adjustment
225
227
 
226
- if adjustment.positive?
227
- corrector.remove_leading(buffer.line_range(lineno), adjustment)
228
- elsif adjustment.negative?
229
- corrector.insert_before(buffer.line_range(lineno), ' ' * -adjustment)
230
- end
228
+ corrector.remove_leading(buffer.line_range(lineno), adjustment) if adjustment.positive?
231
229
  end
232
230
  end
233
231
  end
@@ -43,7 +43,7 @@ module RuboCop
43
43
  { ({return next break} nil) (nil) }
44
44
  PATTERN
45
45
 
46
- def on_block(node)
46
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
47
47
  return unless node.lambda? || node.proc?
48
48
  return unless nil_return?(node.body)
49
49
 
@@ -32,6 +32,8 @@ module RuboCop
32
32
  check_method_node(node.send_node)
33
33
  end
34
34
 
35
+ alias on_numblock on_block
36
+
35
37
  def on_send(node)
36
38
  return unless node.arguments.one? && node.first_argument.block_pass_type?
37
39
 
@@ -83,10 +83,31 @@ module RuboCop
83
83
  end
84
84
  end
85
85
 
86
+ # @private
87
+ # @param [RuboCop::AST::Node] node
88
+ # @return [String, nil]
89
+ def preferred_expression_to_node_with_constant_prefix(node)
90
+ expression = preferred_expression_to(node)
91
+ return unless expression
92
+
93
+ "#{constant_prefix(node)}#{expression}"
94
+ end
95
+
96
+ # @private
97
+ # @param [RuboCop::AST::Node] node
98
+ # @return [String]
99
+ def constant_prefix(node)
100
+ if node.each_ancestor(:class, :module).any?
101
+ '::'
102
+ else
103
+ ''
104
+ end
105
+ end
106
+
86
107
  # @private
87
108
  # @param [RuboCop::AST::Node] node
88
109
  def on_back_ref_or_gvar_or_nth_ref(node)
89
- preferred_expression = preferred_expression_to(node)
110
+ preferred_expression = preferred_expression_to_node_with_constant_prefix(node)
90
111
  return unless preferred_expression
91
112
 
92
113
  add_offense(
@@ -19,7 +19,8 @@ module RuboCop
19
19
  MSG = 'Use `proc` instead of `Proc.new`.'
20
20
 
21
21
  # @!method proc_new?(node)
22
- def_node_matcher :proc_new?, '(block $(send (const {nil? cbase} :Proc) :new) ...)'
22
+ def_node_matcher :proc_new?,
23
+ '({block numblock} $(send (const {nil? cbase} :Proc) :new) ...)'
23
24
 
24
25
  def on_block(node)
25
26
  proc_new?(node) do |block_method|
@@ -28,6 +29,8 @@ module RuboCop
28
29
  end
29
30
  end
30
31
  end
32
+
33
+ alias on_numblock on_block
31
34
  end
32
35
  end
33
36
  end
@@ -89,6 +89,8 @@ module RuboCop
89
89
  register_offense(node.body)
90
90
  end
91
91
 
92
+ alias on_numblock on_block
93
+
92
94
  def on_kwbegin(node)
93
95
  return unless (target_node = offensive_kwbegins(node).to_a.last)
94
96
 
@@ -50,7 +50,7 @@ module RuboCop
50
50
  ${nil? #basic_literal? #const_type?})
51
51
  PATTERN
52
52
 
53
- def on_block(node)
53
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
54
54
  redundant_fetch_block_candidate?(node) do |send, body|
55
55
  return if should_not_check?(send, body)
56
56
 
@@ -29,6 +29,9 @@ module RuboCop
29
29
  # @!method rescue?(node)
30
30
  def_node_matcher :rescue?, '{^resbody ^^resbody}'
31
31
 
32
+ # @!method allowed_pin_operator?(node)
33
+ def_node_matcher :allowed_pin_operator?, '^(pin (begin !{lvar ivar cvar gvar}))'
34
+
32
35
  # @!method arg_in_call_with_block?(node)
33
36
  def_node_matcher :arg_in_call_with_block?, '^^(block (send _ _ equal?(%0) ...) ...)'
34
37
 
@@ -44,6 +47,7 @@ module RuboCop
44
47
  empty_parentheses?(node) ||
45
48
  first_arg_begins_with_hash_literal?(node) ||
46
49
  rescue?(node) ||
50
+ allowed_pin_operator?(node) ||
47
51
  allowed_expression?(node)
48
52
  end
49
53
 
@@ -120,6 +120,8 @@ module RuboCop
120
120
  add_scope(node, @local_variables_scopes[node])
121
121
  end
122
122
 
123
+ alias on_numblock on_block
124
+
123
125
  def on_if(node)
124
126
  # Allow conditional nodes to use `self` in the condition if that variable
125
127
  # name is used in an `lvasgn` or `masgn` within the `if`.
@@ -19,18 +19,24 @@ module RuboCop
19
19
  include RangeHelp
20
20
  extend AutoCorrector
21
21
 
22
- MSG = 'Use `sort` instead of `sort_by { |%<var>s| %<var>s }`.'
23
-
24
- # @!method redundant_sort_by(node)
25
- def_node_matcher :redundant_sort_by, <<~PATTERN
26
- (block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
27
- PATTERN
22
+ MSG_BLOCK = 'Use `sort` instead of `sort_by { |%<var>s| %<var>s }`.'
23
+ MSG_NUMBLOCK = 'Use `sort` instead of `sort_by { _1 }`.'
28
24
 
29
25
  def on_block(node)
30
- redundant_sort_by(node) do |send, var_name|
26
+ redundant_sort_by_block(node) do |send, var_name|
31
27
  range = sort_by_range(send, node)
32
28
 
33
- add_offense(range, message: format(MSG, var: var_name)) do |corrector|
29
+ add_offense(range, message: format(MSG_BLOCK, var: var_name)) do |corrector|
30
+ corrector.replace(range, 'sort')
31
+ end
32
+ end
33
+ end
34
+
35
+ def on_numblock(node)
36
+ redundant_sort_by_numblock(node) do |send|
37
+ range = sort_by_range(send, node)
38
+
39
+ add_offense(range, message: format(MSG_NUMBLOCK)) do |corrector|
34
40
  corrector.replace(range, 'sort')
35
41
  end
36
42
  end
@@ -38,6 +44,16 @@ module RuboCop
38
44
 
39
45
  private
40
46
 
47
+ # @!method redundant_sort_by_block(node)
48
+ def_node_matcher :redundant_sort_by_block, <<~PATTERN
49
+ (block $(send _ :sort_by) (args (arg $_x)) (lvar _x))
50
+ PATTERN
51
+
52
+ # @!method redundant_sort_by_numblock(node)
53
+ def_node_matcher :redundant_sort_by_numblock, <<~PATTERN
54
+ (numblock $(send _ :sort_by) 1 (lvar :_1))
55
+ PATTERN
56
+
41
57
  def sort_by_range(send, node)
42
58
  range_between(send.loc.selector.begin_pos, node.loc.end.end_pos)
43
59
  end
@@ -141,7 +141,7 @@ module RuboCop
141
141
 
142
142
  corrector.remove(begin_range(node, body))
143
143
  corrector.remove(end_range(node, body))
144
- corrector.insert_before(method_call.loc.dot, '&')
144
+ corrector.insert_before(method_call.loc.dot, '&') unless method_call.safe_navigation?
145
145
  handle_comments(corrector, node, method_call)
146
146
 
147
147
  add_safe_nav_to_all_methods_in_chain(corrector, method_call, body)
@@ -250,7 +250,9 @@ module RuboCop
250
250
  end
251
251
 
252
252
  def unsafe_method?(send_node)
253
- negated?(send_node) || send_node.assignment? || !send_node.dot?
253
+ negated?(send_node) ||
254
+ send_node.assignment? ||
255
+ (!send_node.dot? && !send_node.safe_navigation?)
254
256
  end
255
257
 
256
258
  def negated?(send_node)
@@ -33,7 +33,7 @@ module RuboCop
33
33
 
34
34
  MSG = 'Name `%<method>s` block params `|%<params>s|`.'
35
35
 
36
- def on_block(node)
36
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
37
37
  return unless node.single_line?
38
38
 
39
39
  return unless eligible_method?(node)
@@ -173,8 +173,6 @@ module RuboCop
173
173
  end
174
174
 
175
175
  def correct_for_comment(corrector, node, if_branch)
176
- return if config.for_cop('Style/IfUnlessModifier')['Enabled']
177
-
178
176
  comments = processed_source.ast_with_comments[if_branch]
179
177
  comment_text = comments.map(&:text).join("\n") << "\n"
180
178
 
@@ -69,7 +69,7 @@ module RuboCop
69
69
  if c.dsym_type?
70
70
  string_literal = to_string_literal(c.source)
71
71
 
72
- ":#{trim_string_interporation_escape_character(string_literal)}"
72
+ ":#{trim_string_interpolation_escape_character(string_literal)}"
73
73
  else
74
74
  to_symbol_literal(c.value.to_s)
75
75
  end
@@ -11,10 +11,11 @@ module RuboCop
11
11
  # These are customizable with `AllowedMethods` option.
12
12
  #
13
13
  # @safety
14
- # This cop is unsafe because `proc`s and blocks work differently
15
- # when additional arguments are passed in. A block will silently
16
- # allow additional arguments, but a `proc` will raise
17
- # an `ArgumentError`.
14
+ # This cop is unsafe because there is a difference that a `Proc`
15
+ # generated from `Symbol#to_proc` behaves as a lambda, while
16
+ # a `Proc` generated from a block does not.
17
+ # For example, a lambda will raise an `ArgumentError` if the
18
+ # number of arguments is wrong, but a non-lambda `Proc` will not.
18
19
  #
19
20
  # For example:
20
21
  #