rubocop 1.30.0 → 1.31.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (144) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +4 -2
  3. data/config/default.yml +41 -8
  4. data/config/obsoletion.yml +2 -0
  5. data/exe/rubocop +15 -7
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  7. data/lib/rubocop/cli/command/suggest_extensions.rb +3 -3
  8. data/lib/rubocop/config.rb +4 -0
  9. data/lib/rubocop/config_loader.rb +1 -0
  10. data/lib/rubocop/config_validator.rb +9 -5
  11. data/lib/rubocop/cop/base.rb +4 -0
  12. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  13. data/lib/rubocop/cop/bundler/gem_filename.rb +4 -4
  14. data/lib/rubocop/cop/bundler/ordered_gems.rb +2 -2
  15. data/lib/rubocop/cop/corrector.rb +2 -2
  16. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +3 -3
  17. data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +1 -1
  18. data/lib/rubocop/cop/gemspec/deprecated_attribute_assignment.rb +31 -16
  19. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +1 -1
  20. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  21. data/lib/rubocop/cop/gemspec/require_mfa.rb +20 -20
  22. data/lib/rubocop/cop/generator.rb +1 -1
  23. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -5
  24. data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -1
  25. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +1 -1
  26. data/lib/rubocop/cop/layout/argument_alignment.rb +1 -1
  27. data/lib/rubocop/cop/layout/empty_comment.rb +1 -1
  28. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +25 -4
  29. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +4 -4
  30. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +9 -9
  31. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +9 -9
  32. data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -27
  33. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -11
  34. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +49 -10
  35. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  36. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +68 -0
  37. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +130 -0
  38. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  39. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  40. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  41. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_around_operators.rb +1 -1
  43. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -1
  44. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  45. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +10 -10
  46. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +5 -3
  47. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +7 -7
  48. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  49. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +51 -0
  50. data/lib/rubocop/cop/lint/interpolation_check.rb +1 -1
  51. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
  52. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +122 -0
  53. data/lib/rubocop/cop/lint/number_conversion.rb +3 -3
  54. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
  55. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +5 -5
  56. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  57. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -1
  58. data/lib/rubocop/cop/lint/regexp_as_condition.rb +2 -2
  59. data/lib/rubocop/cop/lint/struct_new_override.rb +2 -2
  60. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -1
  61. data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +44 -0
  62. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  63. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +41 -12
  64. data/lib/rubocop/cop/mixin/range_help.rb +7 -3
  65. data/lib/rubocop/cop/naming/accessor_method_name.rb +3 -1
  66. data/lib/rubocop/cop/naming/variable_number.rb +17 -17
  67. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  68. data/lib/rubocop/cop/style/and_or.rb +8 -8
  69. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  70. data/lib/rubocop/cop/style/block_delimiters.rb +4 -2
  71. data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
  72. data/lib/rubocop/cop/style/conditional_assignment.rb +1 -0
  73. data/lib/rubocop/cop/style/empty_else.rb +10 -10
  74. data/lib/rubocop/cop/style/empty_method.rb +16 -1
  75. data/lib/rubocop/cop/style/encoding.rb +1 -1
  76. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  77. data/lib/rubocop/cop/style/fetch_env_var.rb +9 -2
  78. data/lib/rubocop/cop/style/format_string_token.rb +48 -17
  79. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  80. data/lib/rubocop/cop/style/guard_clause.rb +8 -6
  81. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -1
  82. data/lib/rubocop/cop/style/hash_except.rb +88 -8
  83. data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
  84. data/lib/rubocop/cop/style/implicit_runtime_error.rb +2 -2
  85. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  86. data/lib/rubocop/cop/style/line_end_concatenation.rb +1 -1
  87. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  88. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  89. data/lib/rubocop/cop/style/missing_else.rb +24 -24
  90. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -2
  91. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
  92. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -3
  93. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -1
  94. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  95. data/lib/rubocop/cop/style/multiline_when_then.rb +1 -3
  96. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  97. data/lib/rubocop/cop/style/nested_ternary_operator.rb +19 -7
  98. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  99. data/lib/rubocop/cop/style/not.rb +1 -1
  100. data/lib/rubocop/cop/style/redundant_argument.rb +1 -1
  101. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  102. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  103. data/lib/rubocop/cop/style/rescue_standard_error.rb +10 -10
  104. data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
  105. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
  106. data/lib/rubocop/cop/style/string_concatenation.rb +5 -6
  107. data/lib/rubocop/cop/style/struct_inheritance.rb +2 -2
  108. data/lib/rubocop/cop/style/swap_values.rb +1 -1
  109. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  110. data/lib/rubocop/cop/style/unpack_first.rb +1 -1
  111. data/lib/rubocop/cop/util.rb +1 -1
  112. data/lib/rubocop/cops_documentation_generator.rb +18 -1
  113. data/lib/rubocop/formatter/disabled_config_formatter.rb +8 -5
  114. data/lib/rubocop/formatter/formatter_set.rb +20 -19
  115. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +15 -2
  116. data/lib/rubocop/formatter/html_formatter.rb +0 -1
  117. data/lib/rubocop/formatter/offense_count_formatter.rb +2 -0
  118. data/lib/rubocop/formatter/simple_text_formatter.rb +6 -7
  119. data/lib/rubocop/formatter.rb +31 -0
  120. data/lib/rubocop/options.rb +25 -2
  121. data/lib/rubocop/rake_task.rb +30 -9
  122. data/lib/rubocop/runner.rb +2 -2
  123. data/lib/rubocop/server/cache.rb +109 -0
  124. data/lib/rubocop/server/cli.rb +104 -0
  125. data/lib/rubocop/server/client_command/base.rb +44 -0
  126. data/lib/rubocop/server/client_command/exec.rb +59 -0
  127. data/lib/rubocop/server/client_command/restart.rb +25 -0
  128. data/lib/rubocop/server/client_command/start.rb +43 -0
  129. data/lib/rubocop/server/client_command/status.rb +28 -0
  130. data/lib/rubocop/server/client_command/stop.rb +31 -0
  131. data/lib/rubocop/server/client_command.rb +26 -0
  132. data/lib/rubocop/server/core.rb +79 -0
  133. data/lib/rubocop/server/errors.rb +23 -0
  134. data/lib/rubocop/server/helper.rb +34 -0
  135. data/lib/rubocop/server/server_command/base.rb +50 -0
  136. data/lib/rubocop/server/server_command/exec.rb +34 -0
  137. data/lib/rubocop/server/server_command/stop.rb +24 -0
  138. data/lib/rubocop/server/server_command.rb +21 -0
  139. data/lib/rubocop/server/socket_reader.rb +65 -0
  140. data/lib/rubocop/server.rb +53 -0
  141. data/lib/rubocop/version.rb +15 -8
  142. data/lib/rubocop.rb +7 -26
  143. metadata +42 -5
  144. data/lib/rubocop/cop/gemspec/date_assignment.rb +0 -49
@@ -133,7 +133,7 @@ module RuboCop
133
133
  def register_offense_to_method_definition_arguments(method_definition)
134
134
  add_offense(arguments_range(method_definition)) do |corrector|
135
135
  arguments_range = range_with_surrounding_space(
136
- range: method_definition.arguments.source_range, side: :left
136
+ method_definition.arguments.source_range, side: :left
137
137
  )
138
138
  corrector.replace(arguments_range, '(...)')
139
139
  end
@@ -184,6 +184,8 @@ module RuboCop
184
184
  end
185
185
  end
186
186
 
187
+ alias on_numblock on_block
188
+
187
189
  private
188
190
 
189
191
  def autocorrect(corrector, node)
@@ -281,7 +283,7 @@ module RuboCop
281
283
  def move_comment_before_block(corrector, comment, block_node, closing_brace)
282
284
  range = block_node.chained? ? end_of_chain(block_node.parent).source_range : closing_brace
283
285
  comment_range = range_between(range.end_pos, comment.loc.expression.end_pos)
284
- corrector.remove(range_with_surrounding_space(range: comment_range, side: :right))
286
+ corrector.remove(range_with_surrounding_space(comment_range, side: :right))
285
287
  corrector.insert_after(range, "\n")
286
288
 
287
289
  corrector.insert_before(block_node, "#{comment.text}\n")
@@ -300,7 +302,7 @@ module RuboCop
300
302
 
301
303
  def get_blocks(node, &block)
302
304
  case node.type
303
- when :block
305
+ when :block, :numblock
304
306
  yield node
305
307
  when :send
306
308
  get_blocks(node.receiver, &block) if node.receiver
@@ -66,7 +66,7 @@ module RuboCop
66
66
 
67
67
  def register_offense(comment, matched_keyword)
68
68
  add_offense(comment, message: format(MSG, keyword: matched_keyword)) do |corrector|
69
- range = range_with_surrounding_space(range: comment.loc.expression, newlines: false)
69
+ range = range_with_surrounding_space(comment.loc.expression, newlines: false)
70
70
  corrector.remove(range)
71
71
 
72
72
  unless matched_keyword == 'end'
@@ -237,6 +237,7 @@ module RuboCop
237
237
  ASSIGNMENT_TYPES.each do |type|
238
238
  define_method "on_#{type}" do |node|
239
239
  return if part_of_ignored_node?(node)
240
+ return if node.parent&.shorthand_asgn?
240
241
 
241
242
  check_assignment_to_condition(node)
242
243
  end
@@ -6,20 +6,20 @@ module RuboCop
6
6
  # Checks for empty else-clauses, possibly including comments and/or an
7
7
  # explicit `nil` depending on the EnforcedStyle.
8
8
  #
9
- # @example EnforcedStyle: empty
10
- # # warn only on empty else
9
+ # @example EnforcedStyle: both (default)
10
+ # # warn on empty else and else with nil in it
11
11
  #
12
12
  # # bad
13
13
  # if condition
14
14
  # statement
15
15
  # else
16
+ # nil
16
17
  # end
17
18
  #
18
- # # good
19
+ # # bad
19
20
  # if condition
20
21
  # statement
21
22
  # else
22
- # nil
23
23
  # end
24
24
  #
25
25
  # # good
@@ -34,20 +34,20 @@ module RuboCop
34
34
  # statement
35
35
  # end
36
36
  #
37
- # @example EnforcedStyle: nil
38
- # # warn on else with nil in it
37
+ # @example EnforcedStyle: empty
38
+ # # warn only on empty else
39
39
  #
40
40
  # # bad
41
41
  # if condition
42
42
  # statement
43
43
  # else
44
- # nil
45
44
  # end
46
45
  #
47
46
  # # good
48
47
  # if condition
49
48
  # statement
50
49
  # else
50
+ # nil
51
51
  # end
52
52
  #
53
53
  # # good
@@ -62,8 +62,8 @@ module RuboCop
62
62
  # statement
63
63
  # end
64
64
  #
65
- # @example EnforcedStyle: both (default)
66
- # # warn on empty else and else with nil in it
65
+ # @example EnforcedStyle: nil
66
+ # # warn on else with nil in it
67
67
  #
68
68
  # # bad
69
69
  # if condition
@@ -72,7 +72,7 @@ module RuboCop
72
72
  # nil
73
73
  # end
74
74
  #
75
- # # bad
75
+ # # good
76
76
  # if condition
77
77
  # statement
78
78
  # else
@@ -11,6 +11,10 @@ module RuboCop
11
11
  # NOTE: A method definition is not considered empty if it contains
12
12
  # comments.
13
13
  #
14
+ # NOTE: Autocorrection will not be applied for the `compact` style
15
+ # if the resulting code is longer than the `Max` configuration for
16
+ # `Layout/LineLength`, but an offense will still be registered.
17
+ #
14
18
  # @example EnforcedStyle: compact (default)
15
19
  # # bad
16
20
  # def foo(bar)
@@ -51,7 +55,12 @@ module RuboCop
51
55
  return if node.body || comment_lines?(node)
52
56
  return if correct_style?(node)
53
57
 
54
- add_offense(node) { |corrector| corrector.replace(node, corrected(node)) }
58
+ add_offense(node) do |corrector|
59
+ correction = corrected(node)
60
+ next if compact_style? && max_line_length && correction.size > max_line_length
61
+
62
+ corrector.replace(node, correction)
63
+ end
55
64
  end
56
65
  alias on_defs on_def
57
66
 
@@ -98,6 +107,12 @@ module RuboCop
98
107
  def expanded_style?
99
108
  style == :expanded
100
109
  end
110
+
111
+ def max_line_length
112
+ return unless config.for_cop('Layout/LineLength')['Enabled']
113
+
114
+ config.for_cop('Layout/LineLength')['Max']
115
+ end
101
116
  end
102
117
  end
103
118
  end
@@ -51,7 +51,7 @@ module RuboCop
51
51
  text = comment.without(:encoding)
52
52
 
53
53
  if text.blank?
54
- corrector.remove(range_with_surrounding_space(range: range, side: :right))
54
+ corrector.remove(range_with_surrounding_space(range, side: :right))
55
55
  else
56
56
  corrector.replace(range, text)
57
57
  end
@@ -42,7 +42,7 @@ module RuboCop
42
42
  include RangeHelp
43
43
  extend AutoCorrector
44
44
 
45
- MSG = 'Consider using explicit block argument in the '\
45
+ MSG = 'Consider using explicit block argument in the ' \
46
46
  "surrounding method's signature over `yield`."
47
47
 
48
48
  # @!method yielding_block?(node)
@@ -112,13 +112,20 @@ module RuboCop
112
112
  end
113
113
 
114
114
  def used_in_condition?(node, condition)
115
- if condition.send_type? && (!condition.comparison_method? && !condition.predicate_method?)
116
- return false
115
+ if condition.send_type?
116
+ return true if condition.assignment_method? && partial_matched?(node, condition)
117
+ return false if !condition.comparison_method? && !condition.predicate_method?
117
118
  end
118
119
 
119
120
  condition.child_nodes.any?(node)
120
121
  end
121
122
 
123
+ # Avoid offending in the following cases:
124
+ # `ENV['key'] if ENV['key'] = x`
125
+ def partial_matched?(node, condition)
126
+ node.child_nodes == node.child_nodes & condition.child_nodes
127
+ end
128
+
122
129
  def offensive?(node)
123
130
  !(allowed_var?(node) || allowable_use?(node))
124
131
  end
@@ -69,6 +69,7 @@ module RuboCop
69
69
  class FormatStringToken < Base
70
70
  include ConfigurableEnforcedStyle
71
71
  include IgnoredMethods
72
+ extend AutoCorrector
72
73
 
73
74
  def on_str(node)
74
75
  return if format_string_token?(node) || use_ignored_method?(node)
@@ -77,13 +78,8 @@ module RuboCop
77
78
  return if detections.empty?
78
79
  return if allowed_unannotated?(detections)
79
80
 
80
- detections.each do |detected_style, token_range|
81
- if detected_style == style
82
- correct_style_detected
83
- else
84
- style_detected(detected_style)
85
- add_offense(token_range, message: message(detected_style))
86
- end
81
+ detections.each do |detected_sequence, token_range|
82
+ check_sequence(detected_sequence, token_range)
87
83
  end
88
84
  end
89
85
 
@@ -106,6 +102,38 @@ module RuboCop
106
102
  send_parent && ignored_method?(send_parent.method_name)
107
103
  end
108
104
 
105
+ def check_sequence(detected_sequence, token_range)
106
+ if detected_sequence.style == style
107
+ correct_style_detected
108
+ elsif correctable_sequence?(detected_sequence.type)
109
+ style_detected(detected_sequence.style)
110
+ add_offense(token_range, message: message(detected_sequence.style)) do |corrector|
111
+ autocorrect_sequence(corrector, detected_sequence, token_range)
112
+ end
113
+ end
114
+ end
115
+
116
+ def correctable_sequence?(detected_type)
117
+ detected_type == 's' || style == :annotated || style == :unannotated
118
+ end
119
+
120
+ def autocorrect_sequence(corrector, detected_sequence, token_range)
121
+ return if style == :unannotated
122
+
123
+ name = detected_sequence.name
124
+ return if name.nil?
125
+
126
+ flags = detected_sequence.flags
127
+ width = detected_sequence.width
128
+ precision = detected_sequence.precision
129
+ type = detected_sequence.style == :template ? 's' : detected_sequence.type
130
+ correction = case style
131
+ when :annotated then "%<#{name}>#{flags}#{width}#{precision}#{type}"
132
+ when :template then "%#{flags}#{width}#{precision}{#{name}}"
133
+ end
134
+ corrector.replace(token_range, correction)
135
+ end
136
+
109
137
  def unannotated_format?(node, detected_style)
110
138
  detected_style == :unannotated && !format_string_in_typical_context?(node)
111
139
  end
@@ -143,30 +171,33 @@ module RuboCop
143
171
  def token_ranges(contents)
144
172
  format_string = RuboCop::Cop::Utils::FormatString.new(contents.source)
145
173
 
146
- format_string.format_sequences.each do |seq|
147
- next if seq.percent?
174
+ format_string.format_sequences.each do |detected_sequence|
175
+ next if detected_sequence.percent?
148
176
 
149
- detected_style = seq.style
150
- token = contents.begin.adjust(begin_pos: seq.begin_pos, end_pos: seq.end_pos)
177
+ token = contents.begin.adjust(begin_pos: detected_sequence.begin_pos,
178
+ end_pos: detected_sequence.end_pos)
151
179
 
152
- yield(detected_style, token)
180
+ yield(detected_sequence, token)
153
181
  end
154
182
  end
155
183
 
156
184
  def collect_detections(node)
157
185
  detections = []
158
- tokens(node) do |detected_style, token_range|
159
- unless unannotated_format?(node, detected_style)
160
- detections << [detected_style, token_range]
186
+ tokens(node) do |detected_sequence, token_range|
187
+ unless unannotated_format?(node, detected_sequence.style)
188
+ detections << [detected_sequence, token_range]
161
189
  end
162
190
  end
163
191
  detections
164
192
  end
165
193
 
166
194
  def allowed_unannotated?(detections)
167
- return false if detections.size > max_unannotated_placeholders_allowed
195
+ return false unless detections.all? do |detected_sequence,|
196
+ detected_sequence.style == :unannotated
197
+ end
198
+ return true if detections.size <= max_unannotated_placeholders_allowed
168
199
 
169
- detections.all? { |detected_style,| detected_style == :unannotated }
200
+ detections.any? { |detected_sequence,| !correctable_sequence?(detected_sequence.type) }
170
201
  end
171
202
 
172
203
  def max_unannotated_placeholders_allowed
@@ -182,7 +182,7 @@ module RuboCop
182
182
  end
183
183
 
184
184
  def remove_comment(corrector, node)
185
- corrector.remove(range_with_surrounding_space(range: node.pos, side: :right))
185
+ corrector.remove(range_with_surrounding_space(node.pos, side: :right))
186
186
  end
187
187
 
188
188
  def enable_comment(corrector)
@@ -110,7 +110,7 @@ module RuboCop
110
110
  kw = if guard_clause_in_if
111
111
  node.loc.keyword.source
112
112
  else
113
- opposite_keyword(node)
113
+ node.inverse_keyword
114
114
  end
115
115
 
116
116
  register_offense(node, guard_clause_source(guard_clause), kw)
@@ -123,7 +123,7 @@ module RuboCop
123
123
  return if allowed_consecutive_conditionals? &&
124
124
  consecutive_conditionals?(node.parent, node)
125
125
 
126
- register_offense(node, 'return', opposite_keyword(node))
126
+ register_offense(node, 'return', node.inverse_keyword)
127
127
  end
128
128
 
129
129
  def consecutive_conditionals?(parent, node)
@@ -134,14 +134,12 @@ module RuboCop
134
134
  end
135
135
  end
136
136
 
137
- def opposite_keyword(node)
138
- node.if? ? 'unless' : 'if'
139
- end
140
-
141
137
  def register_offense(node, scope_exiting_keyword, conditional_keyword)
142
138
  condition, = node.node_parts
143
139
  example = [scope_exiting_keyword, conditional_keyword, condition.source].join(' ')
144
140
  if too_long_for_single_line?(node, example)
141
+ return if trivial?(node)
142
+
145
143
  example = "#{conditional_keyword} #{condition.source}; #{scope_exiting_keyword}; end"
146
144
  end
147
145
 
@@ -167,6 +165,10 @@ module RuboCop
167
165
  accepted_if?(node, ending) || node.condition.multiline? || node.parent&.assignment?
168
166
  end
169
167
 
168
+ def trivial?(node)
169
+ node.branches.one? && !node.if_branch.if_type? && !node.if_branch.begin_type?
170
+ end
171
+
170
172
  def accepted_if?(node, ending)
171
173
  return true if node.modifier_form? || node.ternary?
172
174
 
@@ -87,7 +87,7 @@ module RuboCop
87
87
 
88
88
  def remove_last_element_trailing_comma(corrector, node)
89
89
  range = range_with_surrounding_space(
90
- range: node.children.last.source_range,
90
+ node.children.last.source_range,
91
91
  side: :right
92
92
  ).end.resize(1)
93
93
 
@@ -19,6 +19,9 @@ module RuboCop
19
19
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
20
20
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
21
21
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
22
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[foo bar].include?(k) }
23
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[foo bar].include?(k) }
24
+ # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[foo bar].include?(k) }
22
25
  #
23
26
  # # good
24
27
  # {foo: 1, bar: 2, baz: 3}.except(:bar)
@@ -33,15 +36,36 @@ module RuboCop
33
36
  MSG = 'Use `%<prefer>s` instead.'
34
37
  RESTRICT_ON_SEND = %i[reject select filter].freeze
35
38
 
36
- # @!method bad_method?(node)
37
- def_node_matcher :bad_method?, <<~PATTERN
39
+ # @!method bad_method_with_poro?(node)
40
+ def_node_matcher :bad_method_with_poro?, <<~PATTERN
38
41
  (block
39
42
  (send _ _)
40
43
  (args
41
44
  (arg _)
42
45
  (arg _))
43
- (send
44
- _ {:== :!= :eql?} _))
46
+ {
47
+ (send
48
+ _ {:== :!= :eql? :include?} _)
49
+ (send
50
+ (send
51
+ _ {:== :!= :eql? :include?} _) :!)
52
+ })
53
+ PATTERN
54
+
55
+ # @!method bad_method_with_active_support?(node)
56
+ def_node_matcher :bad_method_with_active_support?, <<~PATTERN
57
+ (block
58
+ (send _ _)
59
+ (args
60
+ (arg _)
61
+ (arg _))
62
+ {
63
+ (send
64
+ _ {:== :!= :eql? :in? :include? :exclude?} _)
65
+ (send
66
+ (send
67
+ _ {:== :!= :eql? :in? :include? :exclude?} _) :!)
68
+ })
45
69
  PATTERN
46
70
 
47
71
  def on_send(node)
@@ -52,7 +76,7 @@ module RuboCop
52
76
  return if except_key.nil? || !safe_to_register_offense?(block, except_key)
53
77
 
54
78
  range = offense_range(node)
55
- preferred_method = "except(#{except_key.source})"
79
+ preferred_method = "except(#{except_key_source(except_key)})"
56
80
 
57
81
  add_offense(range, message: format(MSG, prefer: preferred_method)) do |corrector|
58
82
  corrector.replace(range, preferred_method)
@@ -61,28 +85,84 @@ module RuboCop
61
85
 
62
86
  private
63
87
 
88
+ def bad_method?(block)
89
+ if active_support_extensions_enabled?
90
+ bad_method_with_active_support?(block)
91
+ else
92
+ bad_method_with_poro?(block)
93
+ end
94
+ end
95
+
64
96
  def semantically_except_method?(send, block)
65
97
  body = block.body
66
98
 
99
+ negated = body.method?('!')
100
+ body = body.receiver if negated
101
+
67
102
  case send.method_name
68
103
  when :reject
69
- body.method?('==') || body.method?('eql?')
104
+ body.method?('==') || body.method?('eql?') || included?(negated, body)
70
105
  when :select, :filter
71
- body.method?('!=')
106
+ body.method?('!=') || not_included?(negated, body)
72
107
  else
73
108
  false
74
109
  end
75
110
  end
76
111
 
112
+ def included?(negated, body)
113
+ body.method?('include?') || body.method?('in?') || (negated && body.method?('exclude?'))
114
+ end
115
+
116
+ def not_included?(negated, body)
117
+ body.method?('exclude?') || (negated && (body.method?('include?') || body.method?('in?')))
118
+ end
119
+
77
120
  def safe_to_register_offense?(block, except_key)
121
+ extracted = extract_body_if_nagated(block.body)
122
+ if extracted.method?('in?') || extracted.method?('include?') || \
123
+ extracted.method?('exclude?')
124
+ return true
125
+ end
78
126
  return true if block.body.method?('eql?')
79
127
 
80
128
  except_key.sym_type? || except_key.str_type?
81
129
  end
82
130
 
131
+ def extract_body_if_nagated(body)
132
+ return body unless body.method?('!')
133
+
134
+ body.receiver
135
+ end
136
+
137
+ def except_key_source(key)
138
+ if key.array_type?
139
+ key = if key.percent_literal?
140
+ key.each_value.map { |v| decorate_source(v) }
141
+ else
142
+ key.each_value.map(&:source)
143
+ end
144
+ return key.join(', ')
145
+ end
146
+
147
+ key.literal? ? key.source : "*#{key.source}"
148
+ end
149
+
150
+ def decorate_source(value)
151
+ return ":\"#{value.source}\"" if value.dsym_type?
152
+ return "\"#{value.source}\"" if value.dstr_type?
153
+ return ":#{value.source}" if value.sym_type?
154
+
155
+ "'#{value.source}'"
156
+ end
157
+
83
158
  def except_key(node)
84
159
  key_argument = node.argument_list.first.source
85
- lhs, _method_name, rhs = *node.body
160
+ body = extract_body_if_nagated(node.body)
161
+ lhs, _method_name, rhs = *body
162
+
163
+ return lhs if body.method?('include?')
164
+ return lhs if body.method?('exclude?')
165
+ return rhs if body.method?('in?')
86
166
  return if [lhs, rhs].map(&:source).none?(key_argument)
87
167
 
88
168
  [lhs, rhs].find { |operand| operand.source != key_argument }
@@ -223,7 +223,7 @@ module RuboCop
223
223
  operator = pair_node.loc.operator
224
224
 
225
225
  range = key.join(operator)
226
- range_with_surrounding_space(range: range, side: :right)
226
+ range_with_surrounding_space(range, side: :right)
227
227
  end
228
228
 
229
229
  def argument_without_space?(node)
@@ -235,7 +235,7 @@ module RuboCop
235
235
 
236
236
  key_with_hash_rocket = ":#{pair_node.key.source}#{pair_node.inverse_delimiter(true)}"
237
237
  corrector.replace(pair_node.key, key_with_hash_rocket)
238
- corrector.remove(range_with_surrounding_space(range: op))
238
+ corrector.remove(range_with_surrounding_space(op))
239
239
  end
240
240
 
241
241
  def autocorrect_no_mixed_keys(corrector, pair_node)
@@ -15,8 +15,8 @@ module RuboCop
15
15
  # # good
16
16
  # raise ArgumentError, 'Error message here'
17
17
  class ImplicitRuntimeError < Base
18
- MSG = 'Use `%<method>s` with an explicit exception class and message,' \
19
- ' rather than just a message.'
18
+ MSG = 'Use `%<method>s` with an explicit exception class and message, ' \
19
+ 'rather than just a message.'
20
20
  RESTRICT_ON_SEND = %i[raise fail].freeze
21
21
 
22
22
  # @!method implicit_runtime_error_raise_or_fail(node)
@@ -65,7 +65,7 @@ module RuboCop
65
65
 
66
66
  def remove_kwargs(kwarg_nodes, corrector)
67
67
  kwarg_nodes.each do |kwarg|
68
- with_space = range_with_surrounding_space(range: kwarg.source_range)
68
+ with_space = range_with_surrounding_space(kwarg.source_range)
69
69
  corrector.remove(range_with_surrounding_comma(with_space, :left))
70
70
  end
71
71
  end
@@ -70,7 +70,7 @@ module RuboCop
70
70
 
71
71
  def autocorrect(corrector, operator_range)
72
72
  # Include any trailing whitespace so we don't create a syntax error.
73
- operator_range = range_with_surrounding_space(range: operator_range,
73
+ operator_range = range_with_surrounding_space(operator_range,
74
74
  side: :right,
75
75
  newlines: false)
76
76
  one_more_char = operator_range.resize(operator_range.size + 1)
@@ -59,7 +59,7 @@ module RuboCop
59
59
  def autocorrect(corrector, to_h, map)
60
60
  removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
61
61
 
62
- corrector.remove(range_with_surrounding_space(range: removal_range, side: :left))
62
+ corrector.remove(range_with_surrounding_space(removal_range, side: :left))
63
63
  corrector.replace(map.loc.selector, 'to_h')
64
64
  end
65
65
  end
@@ -25,12 +25,12 @@ module RuboCop
25
25
  return if allowed_string_interpolation_method_call?(node)
26
26
 
27
27
  add_offense(offense_range(node), message: OMIT_MSG) do |corrector|
28
- auto_correct(corrector, node)
28
+ autocorrect(corrector, node)
29
29
  end
30
30
  end
31
31
  # rubocop:enable Metrics/PerceivedComplexity
32
32
 
33
- def auto_correct(corrector, node)
33
+ def autocorrect(corrector, node)
34
34
  if parentheses_at_the_end_of_multiline_call?(node)
35
35
  corrector.replace(args_begin(node), ' \\')
36
36
  else