rubocop 1.32.0 → 1.37.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 (189) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +104 -16
  4. data/config/obsoletion.yml +23 -1
  5. data/lib/rubocop/arguments_env.rb +17 -0
  6. data/lib/rubocop/arguments_file.rb +17 -0
  7. data/lib/rubocop/cache_config.rb +29 -0
  8. data/lib/rubocop/cli/command/{auto_genenerate_config.rb → auto_generate_config.rb} +2 -2
  9. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  10. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  11. data/lib/rubocop/cli/command/suggest_extensions.rb +53 -15
  12. data/lib/rubocop/config.rb +1 -1
  13. data/lib/rubocop/config_finder.rb +68 -0
  14. data/lib/rubocop/config_loader.rb +12 -40
  15. data/lib/rubocop/config_loader_resolver.rb +1 -5
  16. data/lib/rubocop/config_obsoletion/changed_parameter.rb +5 -0
  17. data/lib/rubocop/config_obsoletion/parameter_rule.rb +4 -0
  18. data/lib/rubocop/config_obsoletion.rb +7 -2
  19. data/lib/rubocop/cop/cop.rb +1 -1
  20. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +58 -0
  21. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  22. data/lib/rubocop/cop/generator/require_file_injector.rb +2 -2
  23. data/lib/rubocop/cop/generator.rb +1 -2
  24. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +69 -0
  25. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +62 -0
  26. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  27. data/lib/rubocop/cop/layout/block_alignment.rb +16 -12
  28. data/lib/rubocop/cop/layout/block_end_newline.rb +35 -5
  29. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +5 -2
  30. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +2 -0
  31. data/lib/rubocop/cop/layout/end_of_line.rb +4 -4
  32. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -1
  33. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
  34. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -2
  35. data/lib/rubocop/cop/layout/indentation_width.rb +6 -2
  36. data/lib/rubocop/cop/layout/line_length.rb +4 -1
  37. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  38. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  39. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  40. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  41. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  43. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  44. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +25 -9
  45. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +28 -3
  46. data/lib/rubocop/cop/legacy/corrections_proxy.rb +1 -1
  47. data/lib/rubocop/cop/legacy/corrector.rb +1 -1
  48. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +21 -8
  49. data/lib/rubocop/cop/lint/debugger.rb +26 -16
  50. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  51. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  52. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  53. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  54. data/lib/rubocop/cop/lint/duplicate_require.rb +1 -1
  55. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  56. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  57. data/lib/rubocop/cop/lint/empty_conditional_body.rb +107 -1
  58. data/lib/rubocop/cop/lint/erb_new_arguments.rb +9 -9
  59. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
  60. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  61. data/lib/rubocop/cop/lint/next_without_accumulator.rb +25 -6
  62. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +6 -6
  63. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +12 -0
  64. data/lib/rubocop/cop/lint/number_conversion.rb +24 -8
  65. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  66. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  67. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  68. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  69. data/lib/rubocop/cop/lint/redundant_require_statement.rb +29 -9
  70. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +9 -3
  71. data/lib/rubocop/cop/lint/redundant_with_index.rb +13 -10
  72. data/lib/rubocop/cop/lint/redundant_with_object.rb +12 -11
  73. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  74. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  75. data/lib/rubocop/cop/lint/shadowed_exception.rb +15 -10
  76. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +27 -3
  77. data/lib/rubocop/cop/lint/unreachable_loop.rb +9 -3
  78. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  79. data/lib/rubocop/cop/lint/useless_access_modifier.rb +8 -6
  80. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  81. data/lib/rubocop/cop/lint/void.rb +2 -0
  82. data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
  83. data/lib/rubocop/cop/metrics/block_length.rb +6 -7
  84. data/lib/rubocop/cop/metrics/method_length.rb +8 -8
  85. data/lib/rubocop/cop/mixin/allowed_methods.rb +20 -1
  86. data/lib/rubocop/cop/mixin/allowed_pattern.rb +17 -1
  87. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  88. data/lib/rubocop/cop/mixin/comments_help.rb +17 -1
  89. data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -1
  90. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  91. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +82 -4
  92. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -6
  93. data/lib/rubocop/cop/mixin/method_complexity.rb +8 -13
  94. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  95. data/lib/rubocop/cop/mixin/range_help.rb +4 -5
  96. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  97. data/lib/rubocop/cop/mixin/surrounding_space.rb +6 -5
  98. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  99. data/lib/rubocop/cop/naming/constant_name.rb +2 -2
  100. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  101. data/lib/rubocop/cop/naming/predicate_name.rb +24 -3
  102. data/lib/rubocop/cop/style/access_modifier_declarations.rb +97 -1
  103. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  104. data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -2
  105. data/lib/rubocop/cop/style/block_delimiters.rb +26 -7
  106. data/lib/rubocop/cop/style/case_equality.rb +40 -10
  107. data/lib/rubocop/cop/style/class_and_module_children.rb +4 -4
  108. data/lib/rubocop/cop/style/class_equality_comparison.rb +32 -7
  109. data/lib/rubocop/cop/style/class_methods_definitions.rb +2 -1
  110. data/lib/rubocop/cop/style/collection_compact.rb +6 -1
  111. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  112. data/lib/rubocop/cop/style/combinable_loops.rb +3 -1
  113. data/lib/rubocop/cop/style/double_negation.rb +2 -0
  114. data/lib/rubocop/cop/style/each_for_simple_loop.rb +41 -6
  115. data/lib/rubocop/cop/style/each_with_object.rb +39 -8
  116. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  117. data/lib/rubocop/cop/style/empty_heredoc.rb +15 -1
  118. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  119. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  120. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  121. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  122. data/lib/rubocop/cop/style/for.rb +2 -0
  123. data/lib/rubocop/cop/style/format_string_token.rb +21 -8
  124. data/lib/rubocop/cop/style/guard_clause.rb +27 -16
  125. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -1
  126. data/lib/rubocop/cop/style/hash_except.rb +0 -4
  127. data/lib/rubocop/cop/style/hash_syntax.rb +17 -0
  128. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
  129. data/lib/rubocop/cop/style/inverse_methods.rb +8 -6
  130. data/lib/rubocop/cop/style/magic_comment_format.rb +307 -0
  131. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +15 -4
  132. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -1
  133. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -7
  134. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -6
  135. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +4 -1
  136. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -1
  137. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  138. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  139. data/lib/rubocop/cop/style/next.rb +3 -5
  140. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  141. data/lib/rubocop/cop/style/numeric_literals.rb +16 -1
  142. data/lib/rubocop/cop/style/numeric_predicate.rb +28 -8
  143. data/lib/rubocop/cop/style/object_then.rb +2 -0
  144. data/lib/rubocop/cop/style/operator_method_call.rb +39 -0
  145. data/lib/rubocop/cop/style/perl_backrefs.rb +22 -1
  146. data/lib/rubocop/cop/style/proc.rb +4 -1
  147. data/lib/rubocop/cop/style/redundant_begin.rb +3 -0
  148. data/lib/rubocop/cop/style/redundant_condition.rb +24 -6
  149. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  150. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  151. data/lib/rubocop/cop/style/redundant_parentheses.rb +19 -22
  152. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  153. data/lib/rubocop/cop/style/redundant_self.rb +2 -0
  154. data/lib/rubocop/cop/style/redundant_sort.rb +21 -6
  155. data/lib/rubocop/cop/style/redundant_sort_by.rb +24 -8
  156. data/lib/rubocop/cop/style/redundant_string_escape.rb +173 -0
  157. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  158. data/lib/rubocop/cop/style/safe_navigation.rb +4 -2
  159. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  160. data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -5
  161. data/lib/rubocop/cop/style/static_class.rb +32 -1
  162. data/lib/rubocop/cop/style/symbol_array.rb +3 -1
  163. data/lib/rubocop/cop/style/symbol_proc.rb +38 -12
  164. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -13
  165. data/lib/rubocop/cop/style/top_level_method_definition.rb +3 -1
  166. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  167. data/lib/rubocop/cop/style/word_array.rb +3 -1
  168. data/lib/rubocop/cop/util.rb +1 -1
  169. data/lib/rubocop/ext/range.rb +15 -0
  170. data/lib/rubocop/feature_loader.rb +94 -0
  171. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
  172. data/lib/rubocop/formatter/disabled_config_formatter.rb +9 -3
  173. data/lib/rubocop/formatter/html_formatter.rb +3 -3
  174. data/lib/rubocop/formatter/markdown_formatter.rb +1 -1
  175. data/lib/rubocop/formatter/tap_formatter.rb +1 -1
  176. data/lib/rubocop/options.rb +13 -13
  177. data/lib/rubocop/result_cache.rb +22 -20
  178. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  179. data/lib/rubocop/runner.rb +4 -0
  180. data/lib/rubocop/server/cache.rb +41 -2
  181. data/lib/rubocop/server/cli.rb +26 -2
  182. data/lib/rubocop/server/client_command/exec.rb +5 -0
  183. data/lib/rubocop/server/core.rb +2 -1
  184. data/lib/rubocop/server/socket_reader.rb +5 -1
  185. data/lib/rubocop/server.rb +1 -1
  186. data/lib/rubocop/version.rb +8 -2
  187. data/lib/rubocop.rb +8 -3
  188. metadata +20 -9
  189. data/lib/rubocop/cop/mixin/ignored_methods.rb +0 -52
@@ -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)
@@ -148,7 +148,18 @@ module RuboCop
148
148
  )
149
149
  corrector.replace(range, and_operator)
150
150
  corrector.remove(range_by_whole_lines(node.loc.end, include_final_newline: true))
151
- corrector.wrap(if_branch.condition, '(', ')') if wrap_condition?(if_branch.condition)
151
+
152
+ wrap_condition(corrector, if_branch.condition)
153
+ end
154
+
155
+ def wrap_condition(corrector, condition)
156
+ # Handle `send` and `block` nodes that need to be wrapped in parens
157
+ # FIXME: autocorrection prevents syntax errors by wrapping the entire node in parens,
158
+ # but wrapping the argument list would be a more ergonomic correction.
159
+ node_to_check = condition&.block_type? ? condition.send_node : condition
160
+ return unless wrap_condition?(node_to_check)
161
+
162
+ corrector.wrap(condition, '(', ')')
152
163
  end
153
164
 
154
165
  def correct_for_outer_condition_modify_form_style(corrector, node, if_branch)
@@ -162,8 +173,6 @@ module RuboCop
162
173
  end
163
174
 
164
175
  def correct_for_comment(corrector, node, if_branch)
165
- return if config.for_cop('Style/IfUnlessModifier')['Enabled']
166
-
167
176
  comments = processed_source.ast_with_comments[if_branch]
168
177
  comment_text = comments.map(&:text).join("\n") << "\n"
169
178
 
@@ -207,7 +216,7 @@ module RuboCop
207
216
  end
208
217
 
209
218
  def require_parentheses?(condition)
210
- condition.send_type? && !condition.arguments.empty? && !condition.parenthesized? &&
219
+ condition.call_type? && !condition.arguments.empty? && !condition.parenthesized? &&
211
220
  !condition.comparison_method?
212
221
  end
213
222
 
@@ -219,7 +228,7 @@ module RuboCop
219
228
 
220
229
  def wrap_condition?(node)
221
230
  node.and_type? || node.or_type? ||
222
- (node.send_type? && node.arguments.any? && !node.parenthesized?)
231
+ (node.call_type? && node.arguments.any? && !node.parenthesized?)
223
232
  end
224
233
 
225
234
  def replace_condition(condition)
@@ -44,18 +44,49 @@ module RuboCop
44
44
  # end
45
45
  #
46
46
  class StaticClass < Base
47
+ include RangeHelp
47
48
  include VisibilityHelp
49
+ extend AutoCorrector
48
50
 
49
51
  MSG = 'Prefer modules to classes with only class methods.'
50
52
 
51
53
  def on_class(class_node)
52
54
  return if class_node.parent_class
55
+ return unless class_convertible_to_module?(class_node)
53
56
 
54
- add_offense(class_node) if class_convertible_to_module?(class_node)
57
+ add_offense(class_node) do |corrector|
58
+ autocorrect(corrector, class_node)
59
+ end
55
60
  end
56
61
 
57
62
  private
58
63
 
64
+ def autocorrect(corrector, class_node)
65
+ corrector.replace(class_node.loc.keyword, 'module')
66
+ corrector.insert_after(class_node.loc.name, "\nmodule_function\n")
67
+
68
+ class_elements(class_node).each do |node|
69
+ if node.defs_type?
70
+ autocorrect_def(corrector, node)
71
+ elsif node.sclass_type?
72
+ autocorrect_sclass(corrector, node)
73
+ end
74
+ end
75
+ end
76
+
77
+ def autocorrect_def(corrector, node)
78
+ corrector.remove(
79
+ range_between(node.receiver.source_range.begin_pos, node.loc.name.begin_pos)
80
+ )
81
+ end
82
+
83
+ def autocorrect_sclass(corrector, node)
84
+ corrector.remove(
85
+ range_between(node.loc.keyword.begin_pos, node.identifier.source_range.end_pos)
86
+ )
87
+ corrector.remove(node.loc.end)
88
+ end
89
+
59
90
  def class_convertible_to_module?(class_node)
60
91
  nodes = class_elements(class_node)
61
92
  return false if nodes.empty?
@@ -65,11 +65,13 @@ module RuboCop
65
65
  end
66
66
 
67
67
  def build_bracketed_array(node)
68
+ return '[]' if node.children.empty?
69
+
68
70
  syms = node.children.map do |c|
69
71
  if c.dsym_type?
70
72
  string_literal = to_string_literal(c.source)
71
73
 
72
- ":#{trim_string_interporation_escape_character(string_literal)}"
74
+ ":#{trim_string_interpolation_escape_character(string_literal)}"
73
75
  else
74
76
  to_symbol_literal(c.value.to_s)
75
77
  end
@@ -7,14 +7,15 @@ module RuboCop
7
7
  #
8
8
  # If you prefer a style that allows block for method with arguments,
9
9
  # please set `true` to `AllowMethodsWithArguments`.
10
- # respond_to , and `define_method?` methods are ignored by default.
11
- # These are customizable with `IgnoredMethods` option.
10
+ # respond_to , and `define_method?` methods are allowed by default.
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
- # ignore 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
  #
@@ -71,15 +72,25 @@ module RuboCop
71
72
  # # some comment
72
73
  # end
73
74
  #
74
- # @example IgnoredMethods: [respond_to, define_method] (default)
75
+ # @example AllowedMethods: [respond_to, define_method] (default)
75
76
  # # good
76
77
  # respond_to { |foo| foo.bar }
77
78
  # define_method(:foo) { |foo| foo.bar }
78
79
  #
80
+ #
81
+ # @example AllowedPatterns: [] (default)
82
+ # # bad
83
+ # something.map { |s| s.upcase }
84
+ #
85
+ # @example AllowedPatterns: ['map'] (default)
86
+ # # good
87
+ # something.map { |s| s.upcase }
88
+ #
79
89
  class SymbolProc < Base
80
90
  include CommentsHelp
81
91
  include RangeHelp
82
- include IgnoredMethods
92
+ include AllowedMethods
93
+ include AllowedPattern
83
94
  extend AutoCorrector
84
95
 
85
96
  MSG = 'Pass `&:%<method>s` as an argument to `%<block_method>s` instead of a block.'
@@ -103,15 +114,17 @@ module RuboCop
103
114
  [Layout::SpaceBeforeBlockBraces]
104
115
  end
105
116
 
106
- # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
117
+ # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
107
118
  def on_block(node)
108
119
  symbol_proc?(node) do |dispatch_node, arguments_node, method_name|
109
120
  # TODO: Rails-specific handling that we should probably make
110
121
  # configurable - https://github.com/rubocop/rubocop/issues/1485
111
- # we should ignore lambdas & procs
122
+ # we should allow lambdas & procs
112
123
  return if proc_node?(dispatch_node)
124
+ return if unsafe_hash_usage?(dispatch_node)
125
+ return if unsafe_array_usage?(dispatch_node)
113
126
  return if %i[lambda proc].include?(dispatch_node.method_name)
114
- return if ignored_method?(dispatch_node.method_name)
127
+ return if allowed_method_name?(dispatch_node.method_name)
115
128
  return if allow_if_method_has_argument?(node.send_node)
116
129
  return if node.block_type? && destructuring_block_argument?(arguments_node)
117
130
  return if allow_comments? && contains_comments?(node)
@@ -119,7 +132,7 @@ module RuboCop
119
132
  register_offense(node, method_name, dispatch_node.method_name)
120
133
  end
121
134
  end
122
- # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
135
+ # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
123
136
  alias on_numblock on_block
124
137
 
125
138
  def destructuring_block_argument?(argument_node)
@@ -128,6 +141,19 @@ module RuboCop
128
141
 
129
142
  private
130
143
 
144
+ # See: https://github.com/rubocop/rubocop/issues/10864
145
+ def unsafe_hash_usage?(node)
146
+ node.receiver&.hash_type? && %i[reject select].include?(node.method_name)
147
+ end
148
+
149
+ def unsafe_array_usage?(node)
150
+ node.receiver&.array_type? && %i[min max].include?(node.method_name)
151
+ end
152
+
153
+ def allowed_method_name?(name)
154
+ allowed_method?(name) || matches_allowed_pattern?(name)
155
+ end
156
+
131
157
  def register_offense(node, method_name, block_method_name)
132
158
  block_start = node.loc.begin.begin_pos
133
159
  block_end = node.loc.end.end_pos
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  return if only_closing_parenthesis_is_last_line?(condition)
73
73
  return if condition_as_parenthesized_one_line_pattern_matching?(condition)
74
- return unless node.ternary? && !infinite_loop? && offense?(node)
74
+ return unless node.ternary? && offense?(node)
75
75
 
76
76
  message = message(node)
77
77
 
@@ -166,22 +166,10 @@ module RuboCop
166
166
  style == :require_parentheses_when_complex
167
167
  end
168
168
 
169
- def redundant_parentheses_enabled?
170
- @config.for_cop('Style/RedundantParentheses').fetch('Enabled')
171
- end
172
-
173
169
  def parenthesized?(node)
174
170
  node.begin_type?
175
171
  end
176
172
 
177
- # When this cop is configured to enforce parentheses and the
178
- # `RedundantParentheses` cop is enabled, it will cause an infinite loop
179
- # as they compete to add and remove the parentheses respectively.
180
- def infinite_loop?
181
- (require_parentheses? || require_parentheses_when_complex?) &&
182
- redundant_parentheses_enabled?
183
- end
184
-
185
173
  def unsafe_autocorrect?(condition)
186
174
  condition.children.any? do |child|
187
175
  unparenthesized_method_call?(child) || below_ternary_precedence?(child)
@@ -63,6 +63,8 @@ module RuboCop
63
63
  add_offense(node)
64
64
  end
65
65
 
66
+ alias on_numblock on_block
67
+
66
68
  private
67
69
 
68
70
  def top_level_method_definition?(node)
@@ -75,7 +77,7 @@ module RuboCop
75
77
 
76
78
  # @!method define_method_block?(node)
77
79
  def_node_matcher :define_method_block?, <<~PATTERN
78
- (block (send _ {:define_method} _) ...)
80
+ ({block numblock} (send _ {:define_method} _) ...)
79
81
  PATTERN
80
82
  end
81
83
  end
@@ -64,7 +64,7 @@ module RuboCop
64
64
 
65
65
  MSG = 'Useless trailing comma present in block arguments.'
66
66
 
67
- def on_block(node)
67
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
68
68
  # lambda literal (`->`) never has block arguments.
69
69
  return if node.send_node.lambda_literal?
70
70
  return unless useless_trailing_comma?(node)
@@ -83,11 +83,13 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def build_bracketed_array(node)
86
+ return '[]' if node.children.empty?
87
+
86
88
  words = node.children.map do |word|
87
89
  if word.dstr_type?
88
90
  string_literal = to_string_literal(word.source)
89
91
 
90
- trim_string_interporation_escape_character(string_literal)
92
+ trim_string_interpolation_escape_character(string_literal)
91
93
  else
92
94
  to_string_literal(word.children[0])
93
95
  end
@@ -126,7 +126,7 @@ module RuboCop
126
126
  end
127
127
  end
128
128
 
129
- def trim_string_interporation_escape_character(str)
129
+ def trim_string_interpolation_escape_character(str)
130
130
  str.gsub(/\\\#\{(.*?)\}/) { "\#{#{Regexp.last_match(1)}}" }
131
131
  end
132
132
 
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Ext
5
+ # Extensions to Parser::Source::Range
6
+ module Range
7
+ # Adds `Range#single_line?` to parallel `Node#single_line?`
8
+ def single_line?
9
+ first_line == last_line
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ Parser::Source::Range.include RuboCop::Ext::Range
@@ -0,0 +1,94 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This class handles loading files (a.k.a. features in Ruby) specified
5
+ # by `--require` command line option and `require` directive in the config.
6
+ #
7
+ # Normally, the given string is directly passed to `require`. If a string
8
+ # beginning with `.` is given, it is assumed to be relative to the given
9
+ # directory.
10
+ #
11
+ # If a string containing `-` is given, it will be used as is, but if we
12
+ # cannot find the file to load, we will replace `-` with `/` and try it
13
+ # again as when Bundler loads gems.
14
+ #
15
+ # @api private
16
+ class FeatureLoader
17
+ class << self
18
+ # @param [String] config_directory_path
19
+ # @param [String] feature
20
+ def load(config_directory_path:, feature:)
21
+ new(config_directory_path: config_directory_path, feature: feature).load
22
+ end
23
+ end
24
+
25
+ # @param [String] config_directory_path
26
+ # @param [String] feature
27
+ def initialize(config_directory_path:, feature:)
28
+ @config_directory_path = config_directory_path
29
+ @feature = feature
30
+ end
31
+
32
+ def load
33
+ # Don't use `::Kernel.require(target)` to prevent the following error:
34
+ # https://github.com/rubocop/rubocop/issues/10893
35
+ require(target)
36
+ rescue ::LoadError => e
37
+ raise if e.path != target
38
+
39
+ begin
40
+ # Don't use `::Kernel.require(target)` to prevent the following error:
41
+ # https://github.com/rubocop/rubocop/issues/10893
42
+ require(namespaced_target)
43
+ rescue ::LoadError => error_for_namespaced_target
44
+ # NOTE: This wrap is necessary due to JRuby 9.3.4.0 incompatibility:
45
+ # https://github.com/jruby/jruby/issues/7316
46
+ raise LoadError, e if error_for_namespaced_target.path == namespaced_target
47
+
48
+ raise error_for_namespaced_target
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ # @return [String]
55
+ def namespaced_feature
56
+ @feature.tr('-', '/')
57
+ end
58
+
59
+ # @return [String]
60
+ def namespaced_target
61
+ if relative?
62
+ relative(namespaced_feature)
63
+ else
64
+ namespaced_feature
65
+ end
66
+ end
67
+
68
+ # @param [String]
69
+ # @return [String]
70
+ def relative(feature)
71
+ ::File.join(@config_directory_path, feature)
72
+ end
73
+
74
+ # @return [Boolean]
75
+ def relative?
76
+ @feature.start_with?('.')
77
+ end
78
+
79
+ # @param [LoadError] error
80
+ # @return [Boolean]
81
+ def seems_cannot_load_such_file_error?(error)
82
+ error.path == target
83
+ end
84
+
85
+ # @return [String]
86
+ def target
87
+ if relative?
88
+ relative(@feature)
89
+ else
90
+ @feature
91
+ end
92
+ end
93
+ end
94
+ end
@@ -41,7 +41,7 @@ module RuboCop
41
41
  def report_line(location)
42
42
  source_line = location.source_line
43
43
 
44
- if location.first_line == location.last_line
44
+ if location.single_line?
45
45
  output.puts(source_line)
46
46
  else
47
47
  output.puts("#{source_line} #{yellow(ELLIPSES)}")
@@ -70,7 +70,9 @@ module RuboCop
70
70
 
71
71
  command += ' --auto-gen-only-exclude' if @options[:auto_gen_only_exclude]
72
72
 
73
- if @exclude_limit_option
73
+ if no_exclude_limit?
74
+ command += ' --no-exclude-limit'
75
+ elsif @exclude_limit_option
74
76
  command += format(' --exclude-limit %<limit>d', limit: Integer(@exclude_limit_option))
75
77
  end
76
78
  command += ' --no-offense-counts' unless show_offense_counts?
@@ -161,7 +163,7 @@ module RuboCop
161
163
  next unless value.is_a?(Array)
162
164
  next if value.empty?
163
165
 
164
- output_buffer.puts "# #{param}: #{value.join(', ')}"
166
+ output_buffer.puts "# #{param}: #{value.uniq.join(', ')}"
165
167
  end
166
168
  end
167
169
 
@@ -187,7 +189,7 @@ module RuboCop
187
189
  return unless cfg.empty?
188
190
 
189
191
  offending_files = @files_with_offenses[cop_name].sort
190
- if offending_files.count > @exclude_limit
192
+ if !no_exclude_limit? && offending_files.count > @exclude_limit
191
193
  output_buffer.puts ' Enabled: false'
192
194
  else
193
195
  output_exclude_list(output_buffer, offending_files, cop_name)
@@ -245,6 +247,10 @@ module RuboCop
245
247
  def safe_autocorrect?(config)
246
248
  config.fetch('Safe', true) && config.fetch('SafeAutoCorrect', true)
247
249
  end
250
+
251
+ def no_exclude_limit?
252
+ @options[:no_exclude_limit] == false
253
+ end
248
254
  end
249
255
  end
250
256
  end
@@ -93,12 +93,12 @@ module RuboCop
93
93
 
94
94
  def highlighted_source_line(offense)
95
95
  source_before_highlight(offense) +
96
- hightlight_source_tag(offense) +
96
+ highlight_source_tag(offense) +
97
97
  source_after_highlight(offense) +
98
98
  possible_ellipses(offense.location)
99
99
  end
100
100
 
101
- def hightlight_source_tag(offense)
101
+ def highlight_source_tag(offense)
102
102
  "<span class=\"highlight #{offense.severity}\">" \
103
103
  "#{escape(offense.highlighted_area.source)}" \
104
104
  '</span>'
@@ -115,7 +115,7 @@ module RuboCop
115
115
  end
116
116
 
117
117
  def possible_ellipses(location)
118
- location.first_line == location.last_line ? '' : " #{ELLIPSES}"
118
+ location.single_line? ? '' : " #{ELLIPSES}"
119
119
  end
120
120
 
121
121
  def escape(string)
@@ -71,7 +71,7 @@ module RuboCop
71
71
  end
72
72
 
73
73
  def possible_ellipses(location)
74
- location.first_line == location.last_line ? '' : ' ...'
74
+ location.single_line? ? '' : ' ...'
75
75
  end
76
76
  end
77
77
  end
@@ -29,7 +29,7 @@ module RuboCop
29
29
  def report_line(location)
30
30
  source_line = location.source_line
31
31
 
32
- if location.first_line == location.last_line
32
+ if location.single_line?
33
33
  output.puts("# #{source_line}")
34
34
  else
35
35
  output.puts("# #{source_line} #{yellow(ELLIPSES)}")
@@ -1,7 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'optparse'
4
- require 'shellwords'
4
+ require_relative 'arguments_env'
5
+ require_relative 'arguments_file'
5
6
 
6
7
  module RuboCop
7
8
  class IncorrectCopNameError < StandardError; end
@@ -24,7 +25,10 @@ module RuboCop
24
25
  end
25
26
 
26
27
  def parse(command_line_args)
28
+ args_from_file = ArgumentsFile.read_as_arguments
29
+ args_from_env = ArgumentsEnv.read_as_arguments
27
30
  args = args_from_file.concat(args_from_env).concat(command_line_args)
31
+
28
32
  define_options.parse!(args)
29
33
 
30
34
  @validator.validate_compatibility
@@ -45,18 +49,6 @@ module RuboCop
45
49
 
46
50
  private
47
51
 
48
- def args_from_file
49
- if File.exist?('.rubocop') && !File.directory?('.rubocop')
50
- File.read('.rubocop').shellsplit
51
- else
52
- []
53
- end
54
- end
55
-
56
- def args_from_env
57
- Shellwords.split(ENV.fetch('RUBOCOP_OPTS', ''))
58
- end
59
-
60
52
  def define_options
61
53
  OptionParser.new do |opts|
62
54
  opts.banner = rainbow.wrap('Usage: rubocop [options] [file1, file2, ...]').bright
@@ -169,6 +161,7 @@ module RuboCop
169
161
  end
170
162
 
171
163
  option(opts, '--exclude-limit COUNT') { @validator.validate_exclude_limit_option }
164
+ option(opts, '--no-exclude-limit')
172
165
 
173
166
  option(opts, '--[no-]offense-counts')
174
167
  option(opts, '--[no-]auto-gen-only-exclude')
@@ -400,6 +393,12 @@ module RuboCop
400
393
  end
401
394
 
402
395
  def validate_autocorrect
396
+ if @options.key?(:safe_autocorrect) && @options.key?(:autocorrect_all)
397
+ message = Rainbow(<<~MESSAGE).red
398
+ Error: Both safe and unsafe autocorrect options are specified, use only one.
399
+ MESSAGE
400
+ raise OptionArgumentError, message
401
+ end
403
402
  return if @options.key?(:autocorrect)
404
403
  return unless @options.key?(:disable_uncorrectable)
405
404
 
@@ -497,6 +496,7 @@ module RuboCop
497
496
  disable_uncorrectable: ['Used with --autocorrect to annotate any',
498
497
  'offenses that do not support autocorrect',
499
498
  'with `rubocop:todo` comments.'],
499
+ no_exclude_limit: ['Do not set the limit for how many files to exclude.'],
500
500
  force_exclusion: ['Any files excluded by `Exclude` in configuration',
501
501
  'files will be excluded, even if given explicitly',
502
502
  'as arguments.'],