rubocop 1.87.0 → 1.88.2

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 (193) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +80 -72
  3. data/config/obsoletion.yml +21 -1
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +6 -0
  5. data/lib/rubocop/cop/base.rb +30 -10
  6. data/lib/rubocop/cop/bundler/gem_comment.rb +5 -3
  7. data/lib/rubocop/cop/commissioner.rb +13 -11
  8. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
  9. data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -1
  10. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
  11. data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +12 -2
  13. data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -1
  14. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +5 -3
  15. data/lib/rubocop/cop/layout/block_alignment.rb +58 -4
  16. data/lib/rubocop/cop/layout/class_structure.rb +7 -3
  17. data/lib/rubocop/cop/layout/comment_indentation.rb +9 -2
  18. data/lib/rubocop/cop/layout/condition_position.rb +13 -3
  19. data/lib/rubocop/cop/layout/else_alignment.rb +1 -14
  20. data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
  21. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +14 -1
  22. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +13 -14
  23. data/lib/rubocop/cop/layout/indentation_width.rb +28 -0
  24. data/lib/rubocop/cop/layout/line_length.rb +10 -5
  25. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +6 -1
  26. data/lib/rubocop/cop/layout/space_around_operators.rb +6 -2
  27. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +1 -11
  28. data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +1 -10
  29. data/lib/rubocop/cop/lint/assignment_in_condition.rb +13 -1
  30. data/lib/rubocop/cop/lint/circular_argument_reference.rb +1 -3
  31. data/lib/rubocop/cop/lint/debugger.rb +13 -2
  32. data/lib/rubocop/cop/lint/deprecated_constants.rb +1 -7
  33. data/lib/rubocop/cop/lint/empty_block.rb +3 -3
  34. data/lib/rubocop/cop/lint/ensure_return.rb +19 -1
  35. data/lib/rubocop/cop/lint/erb_new_arguments.rb +3 -1
  36. data/lib/rubocop/cop/lint/float_comparison.rb +1 -0
  37. data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +5 -1
  38. data/lib/rubocop/cop/lint/interpolation_check.rb +18 -3
  39. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +1 -1
  40. data/lib/rubocop/cop/lint/literal_assignment_in_condition.rb +11 -1
  41. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -11
  42. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +4 -4
  43. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +16 -0
  44. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  45. data/lib/rubocop/cop/lint/number_conversion.rb +13 -4
  46. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +10 -0
  47. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +7 -7
  48. data/lib/rubocop/cop/lint/raise_exception.rb +1 -1
  49. data/lib/rubocop/cop/lint/rand_one.rb +1 -1
  50. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +4 -1
  51. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +4 -1
  52. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +15 -4
  53. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +14 -7
  54. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +4 -0
  55. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -0
  56. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  57. data/lib/rubocop/cop/lint/redundant_with_object.rb +5 -0
  58. data/lib/rubocop/cop/lint/refinement_import_methods.rb +8 -1
  59. data/lib/rubocop/cop/lint/regexp_as_condition.rb +9 -1
  60. data/lib/rubocop/cop/lint/require_parentheses.rb +13 -4
  61. data/lib/rubocop/cop/lint/require_range_parentheses.rb +2 -1
  62. data/lib/rubocop/cop/lint/require_relative_self_path.rb +5 -5
  63. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  64. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -0
  65. data/lib/rubocop/cop/lint/safe_navigation_with_empty.rb +1 -1
  66. data/lib/rubocop/cop/lint/script_permission.rb +5 -1
  67. data/lib/rubocop/cop/lint/self_assignment.rb +24 -1
  68. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +1 -1
  69. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +14 -0
  70. data/lib/rubocop/cop/lint/shared_mutable_default.rb +3 -1
  71. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +12 -0
  72. data/lib/rubocop/cop/lint/symbol_conversion.rb +21 -4
  73. data/lib/rubocop/cop/lint/to_enum_arguments.rb +35 -2
  74. data/lib/rubocop/cop/lint/to_json.rb +8 -1
  75. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  76. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +4 -1
  77. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +35 -9
  78. data/lib/rubocop/cop/lint/unreachable_code.rb +9 -4
  79. data/lib/rubocop/cop/lint/useless_assignment.rb +10 -5
  80. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
  81. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +7 -3
  82. data/lib/rubocop/cop/lint/useless_setter_call.rb +4 -1
  83. data/lib/rubocop/cop/lint/useless_times.rb +22 -1
  84. data/lib/rubocop/cop/lint/void.rb +2 -2
  85. data/lib/rubocop/cop/metrics/collection_literal_length.rb +1 -1
  86. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  87. data/lib/rubocop/cop/metrics/perceived_complexity.rb +38 -7
  88. data/lib/rubocop/cop/mixin/allowed_methods.rb +5 -5
  89. data/lib/rubocop/cop/mixin/allowed_pattern.rb +5 -1
  90. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
  91. data/lib/rubocop/cop/mixin/hash_subset.rb +8 -0
  92. data/lib/rubocop/cop/mixin/hash_transform_method.rb +4 -0
  93. data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
  94. data/lib/rubocop/cop/naming/file_name.rb +4 -3
  95. data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
  96. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +9 -0
  97. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +9 -3
  98. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  99. data/lib/rubocop/cop/security/marshal_load.rb +1 -1
  100. data/lib/rubocop/cop/style/accessor_grouping.rb +11 -1
  101. data/lib/rubocop/cop/style/alias.rb +1 -1
  102. data/lib/rubocop/cop/style/and_or.rb +1 -1
  103. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  104. data/lib/rubocop/cop/style/array_first_last.rb +12 -1
  105. data/lib/rubocop/cop/style/array_intersect.rb +8 -4
  106. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +3 -0
  107. data/lib/rubocop/cop/style/block_delimiters.rb +16 -2
  108. data/lib/rubocop/cop/style/case_equality.rb +14 -2
  109. data/lib/rubocop/cop/style/class_equality_comparison.rb +21 -13
  110. data/lib/rubocop/cop/style/class_methods_definitions.rb +11 -5
  111. data/lib/rubocop/cop/style/collection_compact.rb +1 -1
  112. data/lib/rubocop/cop/style/colon_method_call.rb +13 -6
  113. data/lib/rubocop/cop/style/combinable_loops.rb +5 -0
  114. data/lib/rubocop/cop/style/comparable_clamp.rb +12 -1
  115. data/lib/rubocop/cop/style/concat_array_literals.rb +5 -1
  116. data/lib/rubocop/cop/style/conditional_assignment.rb +6 -1
  117. data/lib/rubocop/cop/style/constant_visibility.rb +4 -1
  118. data/lib/rubocop/cop/style/data_inheritance.rb +4 -0
  119. data/lib/rubocop/cop/style/date_time.rb +2 -2
  120. data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
  121. data/lib/rubocop/cop/style/dig_chain.rb +5 -0
  122. data/lib/rubocop/cop/style/dir_empty.rb +4 -0
  123. data/lib/rubocop/cop/style/documentation_method.rb +5 -1
  124. data/lib/rubocop/cop/style/empty_case_condition.rb +12 -2
  125. data/lib/rubocop/cop/style/empty_class_definition.rb +8 -1
  126. data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
  127. data/lib/rubocop/cop/style/empty_literal.rb +7 -2
  128. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +30 -20
  129. data/lib/rubocop/cop/style/env_home.rb +4 -0
  130. data/lib/rubocop/cop/style/even_odd.rb +11 -1
  131. data/lib/rubocop/cop/style/exact_regexp_match.rb +8 -1
  132. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  133. data/lib/rubocop/cop/style/file_null.rb +4 -2
  134. data/lib/rubocop/cop/style/file_write.rb +17 -14
  135. data/lib/rubocop/cop/style/format_string.rb +13 -1
  136. data/lib/rubocop/cop/style/hash_conversion.rb +14 -6
  137. data/lib/rubocop/cop/style/hash_lookup_method.rb +2 -6
  138. data/lib/rubocop/cop/style/hash_slice.rb +16 -0
  139. data/lib/rubocop/cop/style/hash_syntax.rb +2 -0
  140. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -4
  141. data/lib/rubocop/cop/style/if_unless_modifier.rb +5 -5
  142. data/lib/rubocop/cop/style/if_with_semicolon.rb +9 -1
  143. data/lib/rubocop/cop/style/inline_comment.rb +1 -1
  144. data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
  145. data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
  146. data/lib/rubocop/cop/style/keyword_parameters_order.rb +7 -3
  147. data/lib/rubocop/cop/style/lambda.rb +7 -1
  148. data/lib/rubocop/cop/style/lambda_call.rb +11 -0
  149. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
  150. data/lib/rubocop/cop/style/map_into_array.rb +1 -1
  151. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -5
  152. data/lib/rubocop/cop/style/method_def_parentheses.rb +5 -1
  153. data/lib/rubocop/cop/style/min_max_comparison.rb +3 -0
  154. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +10 -7
  155. data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
  156. data/lib/rubocop/cop/style/module_function.rb +4 -2
  157. data/lib/rubocop/cop/style/multiline_if_then.rb +1 -1
  158. data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
  159. data/lib/rubocop/cop/style/multiline_method_signature.rb +11 -4
  160. data/lib/rubocop/cop/style/mutable_constant.rb +105 -11
  161. data/lib/rubocop/cop/style/nil_lambda.rb +8 -0
  162. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  163. data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
  164. data/lib/rubocop/cop/style/option_hash.rb +1 -1
  165. data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
  166. data/lib/rubocop/cop/style/parallel_assignment.rb +19 -3
  167. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  168. data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
  169. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
  170. data/lib/rubocop/cop/style/redundant_exception.rb +6 -0
  171. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  172. data/lib/rubocop/cop/style/redundant_format.rb +29 -0
  173. data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
  174. data/lib/rubocop/cop/style/redundant_line_continuation.rb +11 -3
  175. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -4
  176. data/lib/rubocop/cop/style/redundant_self.rb +9 -0
  177. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +23 -4
  178. data/lib/rubocop/cop/style/semicolon.rb +20 -5
  179. data/lib/rubocop/cop/style/single_line_do_end_block.rb +17 -4
  180. data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
  181. data/lib/rubocop/cop/style/struct_inheritance.rb +3 -1
  182. data/lib/rubocop/cop/style/ternary_parentheses.rb +11 -0
  183. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -8
  184. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  185. data/lib/rubocop/cop/style/while_until_do.rb +7 -0
  186. data/lib/rubocop/cop/style/word_array.rb +1 -0
  187. data/lib/rubocop/cop/style/zero_length_predicate.rb +6 -3
  188. data/lib/rubocop/cop/team.rb +18 -4
  189. data/lib/rubocop/formatter/disabled_config_formatter.rb +14 -7
  190. data/lib/rubocop/runner.rb +55 -13
  191. data/lib/rubocop/server/core.rb +6 -0
  192. data/lib/rubocop/version.rb +1 -1
  193. metadata +3 -3
@@ -113,6 +113,10 @@ module RuboCop
113
113
  def offense?(node)
114
114
  condition = node.condition
115
115
 
116
+ # A modifier `if`/`unless` requires the parentheses, e.g. `(a if b) ? x : y`,
117
+ # so removing them would change the meaning. Don't flag it.
118
+ return false if parenthesized_modifier_condition?(condition)
119
+
116
120
  if safe_assignment?(condition)
117
121
  !safe_assignment_allowed?
118
122
  else
@@ -174,6 +178,13 @@ module RuboCop
174
178
  condition.children.any? { |child| below_ternary_precedence?(child) }
175
179
  end
176
180
 
181
+ def parenthesized_modifier_condition?(condition)
182
+ return false unless condition.begin_type?
183
+
184
+ inner = condition.children.first
185
+ inner&.if_type? && inner.modifier_form?
186
+ end
187
+
177
188
  def unparenthesized_method_call?(child)
178
189
  /^[a-z]/i.match?(method_name(child)) && !child.parenthesized?
179
190
  end
@@ -113,9 +113,7 @@ module RuboCop
113
113
 
114
114
  return unless first_offense
115
115
 
116
- if unused_variables_only?(first_offense, variables)
117
- return unused_range(node.type, mlhs_node, node.rhs)
118
- end
116
+ return unused_range(node, mlhs_node) if unused_variables_only?(first_offense, variables)
119
117
 
120
118
  return range_for_parentheses(first_offense, mlhs_node) if Util.parentheses?(mlhs_node)
121
119
 
@@ -130,13 +128,14 @@ module RuboCop
130
128
  offense.source_range == variables.first.source_range
131
129
  end
132
130
 
133
- def unused_range(node_type, mlhs_node, right)
131
+ def unused_range(node, mlhs_node)
134
132
  start_range = mlhs_node.source_range.begin_pos
135
133
 
136
- end_range = case node_type
137
- when :masgn
138
- right.source_range.begin_pos
139
- when :mlhs
134
+ # `node` can be an `mlhs` when recursing into a nested destructuring
135
+ # group; only a `masgn` has a right-hand side to anchor against.
136
+ end_range = if node.masgn_type?
137
+ node.rhs.source_range.begin_pos
138
+ else
140
139
  mlhs_node.source_range.end_pos
141
140
  end
142
141
 
@@ -234,7 +234,7 @@ module RuboCop
234
234
  def autocorrect_class(corrector, node)
235
235
  kind = trivial_accessor_kind(node)
236
236
 
237
- return unless names_match?(node) && kind
237
+ return unless names_match?(node) && !node.predicate_method? && kind
238
238
 
239
239
  indent = ' ' * node.loc.column
240
240
  corrector.replace(
@@ -33,6 +33,7 @@ module RuboCop
33
33
 
34
34
  def on_while(node)
35
35
  return unless node.multiline? && node.do?
36
+ return if same_line_body?(node)
36
37
 
37
38
  add_offense(node.loc.begin, message: format(MSG, keyword: node.keyword)) do |corrector|
38
39
  do_range = node.condition.source_range.end.join(node.loc.begin)
@@ -41,6 +42,12 @@ module RuboCop
41
42
  end
42
43
  end
43
44
  alias on_until on_while
45
+
46
+ private
47
+
48
+ def same_line_body?(node)
49
+ node.body && same_line?(node.loc.begin, node.body)
50
+ end
44
51
  end
45
52
  end
46
53
  end
@@ -93,6 +93,7 @@ module RuboCop
93
93
 
94
94
  def on_array(node)
95
95
  if bracketed_array_of?(:str, node)
96
+ return if node.values.any?(&:heredoc?)
96
97
  return if complex_content?(node.values)
97
98
  return if within_matrix_of_complex_content?(node)
98
99
 
@@ -9,8 +9,10 @@ module RuboCop
9
9
  # `receiver.length < 1` and `receiver.size == 0` that can be
10
10
  # replaced by `receiver.empty?` and `!receiver.empty?`.
11
11
  #
12
- # NOTE: `File`, `Tempfile`, and `StringIO` do not have `empty?`
13
- # so allow `size == 0` and `size.zero?`.
12
+ # NOTE: `File`, `Tempfile`, `StringIO`, and `File::Stat` do not have `empty?`
13
+ # so allow `size == 0` and `size.zero?`. Note that when a `File::Stat` object
14
+ # is stored in a variable (e.g. `stat = File.stat(path); stat.size.zero?`),
15
+ # the cop cannot detect the type and may still register a false positive.
14
16
  #
15
17
  # @safety
16
18
  # This cop is unsafe because it cannot be guaranteed that the receiver
@@ -146,7 +148,8 @@ module RuboCop
146
148
  # @!method non_polymorphic_collection?(node)
147
149
  def_node_matcher :non_polymorphic_collection?, <<~PATTERN
148
150
  {(send (send (send (const {nil? cbase} :File) :stat _) ...) ...)
149
- (send (send (send (const {nil? cbase} {:File :Tempfile :StringIO}) {:new :open} ...) ...) ...)}
151
+ (send (send (send (const {nil? cbase} {:File :Tempfile :StringIO}) {:new :open} ...) ...) ...)
152
+ (send (send (send (const (const {nil? cbase} :File) :Stat) :new ...) ...) ...)}
150
153
  PATTERN
151
154
  end
152
155
  end
@@ -59,6 +59,16 @@ module RuboCop
59
59
 
60
60
  attr_reader :errors, :warnings, :updated_source_file, :cops
61
61
 
62
+ # When set to true, the corrected source is not written back to the
63
+ # inspected file; it is exposed through `#updated_source` instead.
64
+ # @api private
65
+ attr_accessor :defer_corrections
66
+
67
+ # The corrected source of the last investigation, if corrections were
68
+ # made with `#defer_corrections` enabled.
69
+ # @api private
70
+ attr_reader :updated_source
71
+
62
72
  alias updated_source_file? updated_source_file
63
73
 
64
74
  def initialize(cops, config = nil, options = {})
@@ -135,20 +145,24 @@ module RuboCop
135
145
 
136
146
  def autocorrect(processed_source, corrector)
137
147
  @updated_source_file = false
148
+ @updated_source = nil
138
149
  return unless autocorrect?
139
150
  return unless corrector
140
151
  return if corrector.empty?
141
152
 
142
- new_source = corrector.rewrite
153
+ apply_correction(processed_source, corrector.rewrite)
154
+ @updated_source_file = true
155
+ end
143
156
 
157
+ def apply_correction(processed_source, new_source)
144
158
  if @options[:stdin]
145
159
  # holds source read in from stdin, when --stdin option is used
146
160
  @options[:stdin] = new_source
161
+ elsif defer_corrections
162
+ @updated_source = new_source
147
163
  else
148
- filename = processed_source.file_path
149
- File.write(filename, new_source)
164
+ File.write(processed_source.file_path, new_source)
150
165
  end
151
- @updated_source_file = true
152
166
  end
153
167
 
154
168
  def be_ready
@@ -91,12 +91,9 @@ module RuboCop
91
91
  command = 'rubocop --auto-gen-config'
92
92
 
93
93
  command += ' --auto-gen-only-exclude' if @options[:auto_gen_only_exclude]
94
-
95
- if no_exclude_limit?
96
- command += ' --no-exclude-limit'
97
- elsif @exclude_limit_option
98
- command += format(' --exclude-limit %<limit>d', limit: Integer(@exclude_limit_option))
99
- end
94
+ command += ' --disable-pending-cops' if @options[:disable_pending_cops]
95
+ command += ' --enable-pending-cops' if @options[:enable_pending_cops]
96
+ command += exclude_limit_option
100
97
  command += ' --no-offense-counts' unless show_offense_counts?
101
98
 
102
99
  command += ' --no-auto-gen-timestamp' unless show_timestamp?
@@ -106,6 +103,16 @@ module RuboCop
106
103
  command
107
104
  end
108
105
 
106
+ def exclude_limit_option
107
+ if no_exclude_limit?
108
+ ' --no-exclude-limit'
109
+ elsif @exclude_limit_option
110
+ format(' --exclude-limit %<limit>d', limit: Integer(@exclude_limit_option))
111
+ else
112
+ ''
113
+ end
114
+ end
115
+
109
116
  def timestamp
110
117
  show_timestamp? ? "on #{Time.now.utc} " : ''
111
118
  end
@@ -158,7 +165,7 @@ module RuboCop
158
165
  output_buffer.puts "# Offense count: #{offense_count}" if show_offense_counts?
159
166
 
160
167
  cop_class = Cop::Registry.global.find_by_cop_name(cop_name)
161
- default_cfg = default_config(cop_name)
168
+ default_cfg = default_config(cop_name) || @config_for_pwd[cop_name]
162
169
 
163
170
  if supports_safe_autocorrect?(cop_class, default_cfg)
164
171
  output_buffer.puts '# This cop supports safe autocorrection (--autocorrect).'
@@ -120,7 +120,6 @@ module RuboCop
120
120
  file_iterator(files) do |file|
121
121
  offenses = process_file(file)
122
122
  succeeded = offenses.none? { |o| considered_failure?(o) && offense_displayed?(o) }
123
- raise Parallel::Break if @options[:fail_fast] && !succeeded
124
123
 
125
124
  [offenses, succeeded]
126
125
  end
@@ -211,8 +210,11 @@ module RuboCop
211
210
  on_start.call(file, index)
212
211
  result = yield file
213
212
  on_finish.call(file, index, result)
214
- rescue Parallel::Break
215
- break
213
+
214
+ # Report and count the offending file before stopping so `--fail-fast`
215
+ # still shows its offenses and exits with a failing status.
216
+ _offenses, succeeded = result
217
+ break if @options[:fail_fast] && !succeeded
216
218
  end
217
219
  end
218
220
 
@@ -344,16 +346,18 @@ module RuboCop
344
346
  # inspection iteration. This is used to output meaningful infinite loop
345
347
  # error message.
346
348
  offenses_by_iteration = []
349
+ corrected_source = nil
347
350
 
348
- # When running with --autocorrect, we need to inspect the file (which
349
- # includes writing a corrected version of it) until no more corrections
350
- # are made. This is because automatic corrections can introduce new
351
- # offenses. In the normal case the loop is only executed once.
351
+ # When running with --autocorrect, we need to inspect the file until no
352
+ # more corrections are made. This is because automatic corrections can
353
+ # introduce new offenses. In the normal case the loop is only executed
354
+ # once. The corrections are kept in memory while iterating and written
355
+ # back to the file when the loop is done.
352
356
  iterate_until_no_changes(processed_source, offenses_by_iteration) do
353
357
  # The offenses that couldn't be corrected will be found again so we
354
358
  # only keep the corrected ones in order to avoid duplicate reporting.
355
359
  !offenses_by_iteration.empty? && offenses_by_iteration.last.select!(&:corrected?)
356
- new_offenses, updated_source_file = inspect_file(processed_source)
360
+ team, new_offenses, updated_source_file = inspect_iteration(processed_source)
357
361
  offenses_by_iteration.push(new_offenses)
358
362
 
359
363
  # We have to reprocess the source to pickup the changes. Since the
@@ -362,12 +366,43 @@ module RuboCop
362
366
  break unless updated_source_file
363
367
 
364
368
  # Autocorrect has happened, don't use the prism result since it is stale.
365
- processed_source = get_processed_source(file, nil)
369
+ # With --stdin the corrected source is kept in @options[:stdin] instead.
370
+ corrected_source = team.updated_source
371
+ processed_source = get_processed_source(file, nil, source: corrected_source)
366
372
  end
367
373
 
368
374
  # Return summary of corrected offenses after all iterations
369
- offenses = offenses_by_iteration.flatten.uniq
370
- [processed_source, offenses]
375
+ [processed_source, offenses_by_iteration.flatten.uniq]
376
+ ensure
377
+ # Write the file once, even when the loop was left through an exception
378
+ # (e.g. an infinite correction loop), like the per-iteration writes
379
+ # used to be.
380
+ File.write(file, corrected_source) if corrected_source
381
+ end
382
+
383
+ def inspect_iteration(processed_source)
384
+ team = mobilize_team(processed_source)
385
+ team.defer_corrections = in_memory_corrections_possible?
386
+ offenses, updated_source_file = inspect_file(processed_source, team)
387
+ [team, offenses, updated_source_file]
388
+ end
389
+
390
+ # When corrections were written to disk and read back between iterations,
391
+ # the text-mode write converted LF to CRLF on Windows, and cops like
392
+ # `Layout/EndOfLine` rely on seeing the source as it would be on disk.
393
+ # Apply the same conversion to the in-memory source. The final `File.write`
394
+ # still performs it for the file itself.
395
+ def emulate_write_read_cycle(source)
396
+ return source unless Platform.windows?
397
+
398
+ source.encode(source.encoding, crlf_newline: true)
399
+ end
400
+
401
+ # Custom ruby extractors may derive their fragments from the file on
402
+ # disk rather than from the passed processed source, so corrections can
403
+ # only be kept in memory when the default extractor is used.
404
+ def in_memory_corrections_possible?
405
+ self.class.ruby_extractors.one?
371
406
  end
372
407
 
373
408
  def iterate_until_no_changes(source, offenses_by_iteration)
@@ -550,12 +585,19 @@ module RuboCop
550
585
  end
551
586
 
552
587
  # rubocop:disable Metrics/MethodLength
553
- def get_processed_source(file, prism_result)
588
+ def get_processed_source(file, prism_result, source: nil)
554
589
  config = @config_store.for_file(file)
555
590
  ruby_version = config.target_ruby_version
556
591
  parser_engine = config.parser_engine
557
592
 
558
- processed_source = if @options[:stdin]
593
+ processed_source = if source
594
+ ProcessedSource.new(
595
+ emulate_write_read_cycle(source),
596
+ ruby_version,
597
+ file,
598
+ parser_engine: parser_engine
599
+ )
600
+ elsif @options[:stdin]
559
601
  ProcessedSource.new(
560
602
  @options[:stdin],
561
603
  ruby_version,
@@ -57,6 +57,12 @@ module RuboCop
57
57
  end
58
58
 
59
59
  Process.waitpid(pid)
60
+
61
+ # The daemon writes its pid file asynchronously after forking, so wait until
62
+ # the server is actually running before returning. This prevents a race where
63
+ # a subsequent command (e.g. `--restart-server`) observes an inconsistent
64
+ # state right after `--start-server` returns.
65
+ Server.wait_for_running_status!(true)
60
66
  end
61
67
 
62
68
  def write_port_and_token_files
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.87.0'
6
+ STRING = '1.88.2'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.87.0
4
+ version: 1.88.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -1119,9 +1119,9 @@ licenses:
1119
1119
  - MIT
1120
1120
  metadata:
1121
1121
  homepage_uri: https://rubocop.org/
1122
- changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.87.0
1122
+ changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.88.2
1123
1123
  source_code_uri: https://github.com/rubocop/rubocop/
1124
- documentation_uri: https://docs.rubocop.org/rubocop/1.87/
1124
+ documentation_uri: https://docs.rubocop.org/rubocop/1.88/
1125
1125
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1126
1126
  rubygems_mfa_required: 'true'
1127
1127
  rdoc_options: []