rubocop 1.84.2 → 1.87.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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +106 -16
  3. data/config/obsoletion.yml +5 -0
  4. data/lib/rubocop/cache_config.rb +1 -1
  5. data/lib/rubocop/cli/command/auto_generate_config.rb +28 -2
  6. data/lib/rubocop/cli/command/list_enabled_cops_for.rb +40 -0
  7. data/lib/rubocop/cli/command/mcp.rb +19 -0
  8. data/lib/rubocop/cli/command/show_cops.rb +2 -2
  9. data/lib/rubocop/cli/command/show_docs_url.rb +4 -8
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
  11. data/lib/rubocop/cli.rb +9 -7
  12. data/lib/rubocop/comment_config.rb +12 -15
  13. data/lib/rubocop/config.rb +14 -10
  14. data/lib/rubocop/config_finder.rb +1 -1
  15. data/lib/rubocop/config_loader.rb +17 -2
  16. data/lib/rubocop/config_loader_resolver.rb +13 -4
  17. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -2
  18. data/lib/rubocop/config_store.rb +2 -2
  19. data/lib/rubocop/config_validator.rb +1 -1
  20. data/lib/rubocop/cop/autocorrect_logic.rb +2 -1
  21. data/lib/rubocop/cop/base.rb +8 -2
  22. data/lib/rubocop/cop/correctors/condition_corrector.rb +1 -1
  23. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +1 -5
  24. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +33 -2
  25. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  26. data/lib/rubocop/cop/correctors.rb +28 -0
  27. data/lib/rubocop/cop/documentation.rb +2 -3
  28. data/lib/rubocop/cop/exclude_limit.rb +31 -5
  29. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  30. data/lib/rubocop/cop/gemspec/require_mfa.rb +5 -5
  31. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  32. data/lib/rubocop/cop/internal_affairs/itblock_handler.rb +69 -0
  33. data/lib/rubocop/cop/internal_affairs/location_line_equality_comparison.rb +1 -0
  34. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  35. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -2
  36. data/lib/rubocop/cop/layout/array_alignment.rb +1 -1
  37. data/lib/rubocop/cop/layout/begin_end_alignment.rb +1 -1
  38. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  39. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  40. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +23 -7
  41. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  42. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
  43. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +12 -2
  44. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +16 -2
  45. data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +16 -2
  46. data/lib/rubocop/cop/layout/end_alignment.rb +8 -5
  47. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +7 -1
  48. data/lib/rubocop/cop/layout/hash_alignment.rb +1 -1
  49. data/lib/rubocop/cop/layout/indentation_width.rb +12 -0
  50. data/lib/rubocop/cop/layout/line_length.rb +5 -3
  51. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +9 -2
  52. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -1
  53. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +53 -3
  54. data/lib/rubocop/cop/layout/parameter_alignment.rb +1 -1
  55. data/lib/rubocop/cop/layout/redundant_line_break.rb +3 -1
  56. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  57. data/lib/rubocop/cop/layout/space_around_keyword.rb +3 -1
  58. data/lib/rubocop/cop/layout/space_before_brackets.rb +1 -1
  59. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -0
  60. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  61. data/lib/rubocop/cop/lint/constant_overwritten_in_rescue.rb +1 -1
  62. data/lib/rubocop/cop/lint/constant_reassignment.rb +93 -11
  63. data/lib/rubocop/cop/lint/constant_resolution.rb +6 -6
  64. data/lib/rubocop/cop/lint/data_define_override.rb +63 -0
  65. data/lib/rubocop/cop/lint/deprecated_constants.rb +1 -1
  66. data/lib/rubocop/cop/lint/duplicate_methods.rb +55 -8
  67. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  68. data/lib/rubocop/cop/lint/empty_conditional_body.rb +6 -1
  69. data/lib/rubocop/cop/lint/empty_in_pattern.rb +8 -1
  70. data/lib/rubocop/cop/lint/empty_when.rb +8 -1
  71. data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
  72. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  73. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
  74. data/lib/rubocop/cop/lint/multiple_comparison.rb +2 -2
  75. data/lib/rubocop/cop/lint/next_without_accumulator.rb +2 -0
  76. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +4 -2
  77. data/lib/rubocop/cop/lint/number_conversion.rb +6 -6
  78. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +1 -1
  79. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +3 -13
  80. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +2 -11
  81. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +23 -6
  82. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +3 -3
  83. data/lib/rubocop/cop/lint/require_relative_self_path.rb +3 -1
  84. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +17 -0
  85. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +7 -1
  86. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  87. data/lib/rubocop/cop/lint/syntax.rb +25 -1
  88. data/lib/rubocop/cop/lint/trailing_comma_in_attribute_declaration.rb +1 -0
  89. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -1
  90. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  91. data/lib/rubocop/cop/lint/unreachable_code.rb +2 -2
  92. data/lib/rubocop/cop/lint/unreachable_pattern_branch.rb +113 -0
  93. data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
  94. data/lib/rubocop/cop/lint/useless_assignment.rb +4 -9
  95. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +4 -4
  96. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +2 -0
  97. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  98. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +35 -9
  99. data/lib/rubocop/cop/lint/void.rb +32 -12
  100. data/lib/rubocop/cop/metrics/block_length.rb +1 -1
  101. data/lib/rubocop/cop/metrics/block_nesting.rb +23 -0
  102. data/lib/rubocop/cop/metrics/method_length.rb +1 -1
  103. data/lib/rubocop/cop/metrics/utils/iterating_block.rb +1 -1
  104. data/lib/rubocop/cop/migration/department_name.rb +12 -1
  105. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  106. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
  107. data/lib/rubocop/cop/mixin/configurable_max.rb +6 -5
  108. data/lib/rubocop/cop/mixin/hash_transform_method/autocorrection.rb +63 -0
  109. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -60
  110. data/lib/rubocop/cop/mixin/project_index_help.rb +48 -0
  111. data/lib/rubocop/cop/mixin.rb +86 -0
  112. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +1 -1
  113. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  114. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  115. data/lib/rubocop/cop/naming/predicate_method.rb +2 -2
  116. data/lib/rubocop/cop/naming/predicate_prefix.rb +1 -1
  117. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  118. data/lib/rubocop/cop/offense.rb +8 -0
  119. data/lib/rubocop/cop/registry.rb +62 -38
  120. data/lib/rubocop/cop/security/eval.rb +15 -2
  121. data/lib/rubocop/cop/security/io_methods.rb +1 -1
  122. data/lib/rubocop/cop/style/access_modifier_declarations.rb +14 -2
  123. data/lib/rubocop/cop/style/accessor_grouping.rb +4 -2
  124. data/lib/rubocop/cop/style/alias.rb +14 -2
  125. data/lib/rubocop/cop/style/and_or.rb +1 -0
  126. data/lib/rubocop/cop/style/arguments_forwarding.rb +25 -7
  127. data/lib/rubocop/cop/style/array_join.rb +4 -2
  128. data/lib/rubocop/cop/style/ascii_comments.rb +6 -3
  129. data/lib/rubocop/cop/style/attr.rb +5 -2
  130. data/lib/rubocop/cop/style/bare_percent_literals.rb +3 -1
  131. data/lib/rubocop/cop/style/begin_block.rb +3 -1
  132. data/lib/rubocop/cop/style/block_delimiters.rb +25 -33
  133. data/lib/rubocop/cop/style/case_equality.rb +4 -0
  134. data/lib/rubocop/cop/style/character_literal.rb +2 -2
  135. data/lib/rubocop/cop/style/class_and_module_children.rb +18 -2
  136. data/lib/rubocop/cop/style/collection_compact.rb +36 -16
  137. data/lib/rubocop/cop/style/colon_method_call.rb +3 -1
  138. data/lib/rubocop/cop/style/concat_array_literals.rb +2 -0
  139. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -4
  140. data/lib/rubocop/cop/style/copyright.rb +22 -11
  141. data/lib/rubocop/cop/style/date_time.rb +2 -2
  142. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +1 -1
  143. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +6 -1
  144. data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
  145. data/lib/rubocop/cop/style/each_with_object.rb +2 -0
  146. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  147. data/lib/rubocop/cop/style/empty_class_definition.rb +43 -20
  148. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  149. data/lib/rubocop/cop/style/encoding.rb +7 -1
  150. data/lib/rubocop/cop/style/end_block.rb +3 -1
  151. data/lib/rubocop/cop/style/endless_method.rb +8 -3
  152. data/lib/rubocop/cop/style/file_open.rb +84 -0
  153. data/lib/rubocop/cop/style/file_write.rb +18 -16
  154. data/lib/rubocop/cop/style/for.rb +3 -0
  155. data/lib/rubocop/cop/style/format_string.rb +4 -3
  156. data/lib/rubocop/cop/style/format_string_token.rb +29 -2
  157. data/lib/rubocop/cop/style/global_vars.rb +5 -2
  158. data/lib/rubocop/cop/style/guard_clause.rb +9 -6
  159. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +21 -5
  160. data/lib/rubocop/cop/style/hash_conversion.rb +1 -1
  161. data/lib/rubocop/cop/style/hash_lookup_method.rb +19 -7
  162. data/lib/rubocop/cop/style/hash_transform_keys.rb +17 -7
  163. data/lib/rubocop/cop/style/hash_transform_values.rb +17 -7
  164. data/lib/rubocop/cop/style/if_inside_else.rb +16 -7
  165. data/lib/rubocop/cop/style/if_unless_modifier.rb +14 -3
  166. data/lib/rubocop/cop/style/if_with_semicolon.rb +7 -5
  167. data/lib/rubocop/cop/style/inline_comment.rb +4 -1
  168. data/lib/rubocop/cop/style/ip_addresses.rb +1 -2
  169. data/lib/rubocop/cop/style/magic_comment_format.rb +3 -3
  170. data/lib/rubocop/cop/style/map_join.rb +123 -0
  171. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -3
  172. data/lib/rubocop/cop/style/min_max_comparison.rb +1 -1
  173. data/lib/rubocop/cop/style/module_member_existence_check.rb +7 -14
  174. data/lib/rubocop/cop/style/multiline_if_then.rb +3 -1
  175. data/lib/rubocop/cop/style/mutable_constant.rb +1 -1
  176. data/lib/rubocop/cop/style/nil_comparison.rb +2 -3
  177. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  178. data/lib/rubocop/cop/style/non_nil_check.rb +5 -11
  179. data/lib/rubocop/cop/style/not.rb +2 -0
  180. data/lib/rubocop/cop/style/numeric_literals.rb +3 -2
  181. data/lib/rubocop/cop/style/one_class_per_file.rb +115 -0
  182. data/lib/rubocop/cop/style/one_line_conditional.rb +4 -3
  183. data/lib/rubocop/cop/style/parallel_assignment.rb +4 -0
  184. data/lib/rubocop/cop/style/partition_instead_of_double_select.rb +270 -0
  185. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
  186. data/lib/rubocop/cop/style/predicate_with_kind.rb +84 -0
  187. data/lib/rubocop/cop/style/proc.rb +3 -2
  188. data/lib/rubocop/cop/style/raise_args.rb +1 -1
  189. data/lib/rubocop/cop/style/reduce_to_hash.rb +200 -0
  190. data/lib/rubocop/cop/style/redundant_array_constructor.rb +2 -2
  191. data/lib/rubocop/cop/style/redundant_begin.rb +3 -3
  192. data/lib/rubocop/cop/style/redundant_constant_base.rb +5 -5
  193. data/lib/rubocop/cop/style/redundant_each.rb +3 -3
  194. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  195. data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +26 -10
  196. data/lib/rubocop/cop/style/redundant_line_continuation.rb +16 -0
  197. data/lib/rubocop/cop/style/redundant_min_max_by.rb +93 -0
  198. data/lib/rubocop/cop/style/redundant_parentheses.rb +25 -22
  199. data/lib/rubocop/cop/style/redundant_percent_q.rb +4 -1
  200. data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +2 -2
  201. data/lib/rubocop/cop/style/redundant_return.rb +3 -1
  202. data/lib/rubocop/cop/style/redundant_self.rb +2 -2
  203. data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +0 -5
  204. data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +114 -0
  205. data/lib/rubocop/cop/style/regexp_literal.rb +31 -2
  206. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -3
  207. data/lib/rubocop/cop/style/safe_navigation.rb +7 -7
  208. data/lib/rubocop/cop/style/select_by_kind.rb +158 -0
  209. data/lib/rubocop/cop/style/select_by_range.rb +197 -0
  210. data/lib/rubocop/cop/style/select_by_regexp.rb +51 -21
  211. data/lib/rubocop/cop/style/self_assignment.rb +1 -1
  212. data/lib/rubocop/cop/style/semicolon.rb +2 -0
  213. data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
  214. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -1
  215. data/lib/rubocop/cop/style/single_line_methods.rb +3 -1
  216. data/lib/rubocop/cop/style/sole_nested_conditional.rb +4 -2
  217. data/lib/rubocop/cop/style/special_global_vars.rb +6 -1
  218. data/lib/rubocop/cop/style/struct_inheritance.rb +13 -0
  219. data/lib/rubocop/cop/style/symbol_proc.rb +7 -6
  220. data/lib/rubocop/cop/style/tally_method.rb +181 -0
  221. data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -2
  222. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  223. data/lib/rubocop/cop/style/trailing_method_end_statement.rb +1 -0
  224. data/lib/rubocop/cop/style/unless_logical_operators.rb +3 -3
  225. data/lib/rubocop/cop/style/while_until_modifier.rb +16 -0
  226. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  227. data/lib/rubocop/cop/style/yoda_expression.rb +1 -1
  228. data/lib/rubocop/cop/team.rb +86 -35
  229. data/lib/rubocop/cop/variable_force/branch.rb +2 -2
  230. data/lib/rubocop/directive_comment.rb +2 -1
  231. data/lib/rubocop/file_patterns.rb +9 -1
  232. data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -2
  233. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  234. data/lib/rubocop/formatter/junit_formatter.rb +1 -1
  235. data/lib/rubocop/formatter/simple_text_formatter.rb +0 -2
  236. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  237. data/lib/rubocop/formatter.rb +22 -21
  238. data/lib/rubocop/lsp/diagnostic.rb +1 -0
  239. data/lib/rubocop/lsp/routes.rb +10 -3
  240. data/lib/rubocop/lsp/runtime.rb +1 -2
  241. data/lib/rubocop/mcp/server.rb +200 -0
  242. data/lib/rubocop/options.rb +35 -4
  243. data/lib/rubocop/path_util.rb +14 -2
  244. data/lib/rubocop/plugin/loader.rb +1 -1
  245. data/lib/rubocop/project_index_loader.rb +66 -0
  246. data/lib/rubocop/result_cache.rb +22 -10
  247. data/lib/rubocop/rspec/cop_helper.rb +8 -0
  248. data/lib/rubocop/rspec/shared_contexts.rb +32 -2
  249. data/lib/rubocop/runner.rb +124 -53
  250. data/lib/rubocop/server/cache.rb +5 -7
  251. data/lib/rubocop/server/core.rb +2 -0
  252. data/lib/rubocop/target_finder.rb +14 -7
  253. data/lib/rubocop/target_ruby.rb +18 -12
  254. data/lib/rubocop/version.rb +21 -3
  255. data/lib/rubocop.rb +22 -96
  256. metadata +27 -5
@@ -5,15 +5,26 @@ module RuboCop
5
5
  module Style
6
6
  # Enforces consistent style for empty class definitions.
7
7
  #
8
- # This cop can enforce either a two-line class definition or `Class.new`
8
+ # This cop can enforce either a standard class definition or `Class.new`
9
9
  # for classes with no body.
10
10
  #
11
11
  # The supported styles are:
12
12
  #
13
- # * class_definition (default) - prefer two-line class definition over `Class.new`
13
+ # * class_keyword (default) - prefer standard class definition over `Class.new`
14
14
  # * class_new - prefer `Class.new` over class definition
15
15
  #
16
- # @example EnforcedStyle: class_definition (default)
16
+ # One difference between the two styles is that the `Class.new` form does not make
17
+ # the subclass name available to the base class's `inherited` callback.
18
+ # For this reason, `EnforcedStyle: class_keyword` is set as the default style.
19
+ # Class definitions without a superclass, which are not involved in inheritance,
20
+ # are not detected. This ensures safe detection regardless of the applied style.
21
+ # This avoids overlapping responsibilities with the `Lint/EmptyClass` cop.
22
+ #
23
+ # Use `AllowedParentClasses` to permit both styles for specific parent classes.
24
+ # For example, adding `StandardError` allows both `Error = Class.new(StandardError)`
25
+ # and `class Error < StandardError; end` regardless of the enforced style.
26
+ #
27
+ # @example EnforcedStyle: class_keyword (default)
17
28
  # # bad
18
29
  # FooError = Class.new(StandardError)
19
30
  #
@@ -35,33 +46,43 @@ module RuboCop
35
46
  # # good
36
47
  # FooError = Class.new(StandardError)
37
48
  #
49
+ # @example AllowedParentClasses: ['StandardError']
50
+ # # good - allowed regardless of EnforcedStyle
51
+ # FooError = Class.new(StandardError)
52
+ #
53
+ # # good - allowed regardless of EnforcedStyle
54
+ # class FooError < StandardError
55
+ # end
56
+ #
38
57
  class EmptyClassDefinition < Base
39
58
  include ConfigurableEnforcedStyle
40
- include RangeHelp
41
59
  extend AutoCorrector
42
60
 
43
- MSG_CLASS_DEFINITION =
44
- 'Prefer a two-line class definition over `Class.new` for classes with no body.'
45
- MSG_CLASS_NEW = 'Prefer `Class.new` over class definition for classes with no body.'
61
+ MSG_CLASS_KEYWORD =
62
+ 'Use the `class` keyword instead of `Class.new` to define an empty class.'
63
+ MSG_CLASS_NEW = 'Use `Class.new` instead of the `class` keyword to define an empty class.'
46
64
 
47
65
  # @!method class_new_assignment(node)
48
66
  def_node_matcher :class_new_assignment, <<~PATTERN
49
- (casgn _ _ $(send (const _ :Class) :new ...))
67
+ (casgn _ _ $(send (const _ :Class) :new _))
50
68
  PATTERN
51
69
 
52
70
  def on_casgn(node)
53
- return unless style == :class_definition
71
+ return unless %i[class_keyword class_definition].include?(style)
54
72
  return unless (class_new_node = class_new_assignment(node))
55
73
  return if (arg = class_new_node.first_argument) && !arg.const_type?
74
+ return if allowed_parent_class?(class_new_node.first_argument.source)
56
75
 
57
- add_offense(node, message: MSG_CLASS_DEFINITION) do |corrector|
76
+ add_offense(node, message: MSG_CLASS_KEYWORD) do |corrector|
58
77
  autocorrect_class_new(corrector, node, class_new_node)
59
78
  end
60
79
  end
61
80
 
62
81
  def on_class(node)
63
82
  return unless style == :class_new
83
+ return unless node.parent_class
64
84
  return if (body = node.body) && !body.children.empty?
85
+ return if allowed_parent_class?(node.parent_class.source)
65
86
 
66
87
  add_offense(node, message: MSG_CLASS_NEW) do |corrector|
67
88
  autocorrect_class_definition(corrector, node)
@@ -70,25 +91,27 @@ module RuboCop
70
91
 
71
92
  private
72
93
 
94
+ def allowed_parent_class?(parent_class_name)
95
+ allowed_parent_classes.include?(parent_class_name)
96
+ end
97
+
98
+ def allowed_parent_classes
99
+ cop_config.fetch('AllowedParentClasses', [])
100
+ end
101
+
73
102
  def autocorrect_class_new(corrector, node, class_new_node)
74
103
  indent = ' ' * node.loc.column
75
104
  class_name = node.name
76
- if (parent_class = class_new_node.first_argument)
77
- parent_class_name = " < #{parent_class.source}"
78
- end
105
+ parent_class_name = class_new_node.first_argument.source
79
106
 
80
- corrector.replace(node, "class #{class_name}#{parent_class_name}\n#{indent}end")
107
+ corrector.replace(node, "class #{class_name} < #{parent_class_name}\n#{indent}end")
81
108
  end
82
109
 
83
110
  def autocorrect_class_definition(corrector, node)
84
- indent = ' ' * node.loc.column
85
111
  class_name = node.identifier.source
86
- if (parent_class = node.parent_class)
87
- parent_class_name = "(#{parent_class.source})"
88
- end
89
- range = range_by_whole_lines(node.source_range, include_final_newline: true)
112
+ parent_class_name = node.parent_class.source
90
113
 
91
- corrector.replace(range, "#{indent}#{class_name} = Class.new#{parent_class_name}\n")
114
+ corrector.replace(node, "#{class_name} = Class.new(#{parent_class_name})")
92
115
  end
93
116
  end
94
117
  end
@@ -23,7 +23,7 @@ module RuboCop
23
23
 
24
24
  MSG = 'Omit parentheses for the empty lambda parameters.'
25
25
 
26
- def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
26
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
27
27
  send_node = node.send_node
28
28
  return unless send_node.send_type?
29
29
 
@@ -3,12 +3,18 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks ensures source files have no utf-8 encoding comments.
6
+ # Checks that source files have no utf-8 encoding comments.
7
+ # Since Ruby 2.0, UTF-8 is the default source encoding, so
8
+ # these comments are no longer necessary and just add noise.
9
+ #
7
10
  # @example
8
11
  # # bad
9
12
  # # encoding: UTF-8
10
13
  # # coding: UTF-8
11
14
  # # -*- coding: UTF-8 -*-
15
+ #
16
+ # # good
17
+ # # No encoding comment needed
12
18
  class Encoding < Base
13
19
  include RangeHelp
14
20
  extend AutoCorrector
@@ -3,7 +3,9 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for END blocks.
6
+ # Checks for `END` blocks. `END` blocks are Perl-style constructs
7
+ # and `Kernel#at_exit` is the idiomatic Ruby alternative, as it's
8
+ # explicit and can be used anywhere.
7
9
  #
8
10
  # @example
9
11
  # # bad
@@ -157,6 +157,7 @@ module RuboCop
157
157
  handle_require_always_style(node)
158
158
  end
159
159
  end
160
+ alias on_defs on_def
160
161
 
161
162
  private
162
163
 
@@ -170,7 +171,7 @@ module RuboCop
170
171
  end
171
172
 
172
173
  def handle_require_single_line_style(node)
173
- if node.endless? && !node.single_line?
174
+ if node.endless? && node.multiline?
174
175
  add_offense(node, message: MSG_MULTI_LINE) do |corrector|
175
176
  correct_to_multiline(corrector, node)
176
177
  end
@@ -207,7 +208,7 @@ module RuboCop
207
208
 
208
209
  def correct_to_multiline(corrector, node)
209
210
  replacement = <<~RUBY.strip
210
- def #{node.method_name}#{arguments(node)}
211
+ def #{receiver(node)}#{node.method_name}#{arguments(node)}
211
212
  #{node.body.source}
212
213
  end
213
214
  RUBY
@@ -217,10 +218,14 @@ module RuboCop
217
218
 
218
219
  def endless_replacement(node)
219
220
  <<~RUBY.strip
220
- def #{node.method_name}#{arguments(node)} = #{node.body.source}
221
+ def #{receiver(node)}#{node.method_name}#{arguments(node)} = #{node.body.source}
221
222
  RUBY
222
223
  end
223
224
 
225
+ def receiver(node)
226
+ node.receiver ? "#{node.receiver.source}#{node.loc.operator.source}" : ''
227
+ end
228
+
224
229
  def arguments(node, missing = '')
225
230
  node.arguments.any? ? node.arguments.source : missing
226
231
  end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for `File.open` without a block, which can leak file descriptors.
7
+ #
8
+ # When `File.open` is called without a block, the caller is responsible
9
+ # for closing the file descriptor. If it is not explicitly closed, it
10
+ # will only be closed when the garbage collector runs, which may lead
11
+ # to resource exhaustion. Using the block form ensures the file is
12
+ # automatically closed when the block exits.
13
+ #
14
+ # This cop only registers an offense when the result of `File.open` is
15
+ # assigned to a variable or has a method chained on it, as those are the
16
+ # clearest indicators that the block form should be used instead. When
17
+ # `File.open` is used as a return value or passed as an argument, the
18
+ # caller is likely managing the file descriptor intentionally.
19
+ #
20
+ # @safety
21
+ # This cop is unsafe because it relies on syntax heuristics and cannot
22
+ # verify whether the file descriptor is safely managed. For example, it
23
+ # still flags intentional one-shot reads (`File.open("f").read`) where
24
+ # the file descriptor is closed by the garbage collector.
25
+ #
26
+ # @example
27
+ # # bad
28
+ # f = File.open('file')
29
+ #
30
+ # # bad
31
+ # File.open('file').read
32
+ #
33
+ # # good
34
+ # File.open('file') do |f|
35
+ # f.read
36
+ # end
37
+ #
38
+ # # good
39
+ # File.open('file', &:read)
40
+ #
41
+ # # good - pass an open file object to an API that manages its lifecycle
42
+ # process(io: File.open('file'))
43
+ #
44
+ # # good - return an open file object for the caller to manage
45
+ # def json_key_io
46
+ # File.open('file')
47
+ # end
48
+ #
49
+ # # good - use File.read for one-shot reads
50
+ # File.read('file')
51
+ #
52
+ class FileOpen < Base
53
+ MSG = '`File.open` without a block may leak a file descriptor; use the block form.'
54
+ RESTRICT_ON_SEND = %i[open].freeze
55
+
56
+ # @!method file_open?(node)
57
+ def_node_matcher :file_open?, <<~PATTERN
58
+ (send (const {nil? cbase} :File) :open ...)
59
+ PATTERN
60
+
61
+ def on_send(node)
62
+ return unless file_open?(node)
63
+ return if node.block_argument?
64
+ return unless offensive_usage?(node)
65
+
66
+ add_offense(node)
67
+ end
68
+ alias on_csend on_send
69
+
70
+ private
71
+
72
+ def offensive_usage?(node)
73
+ return true unless node.value_used?
74
+
75
+ node.parent.lvasgn_type? || receiver_of_chained_call?(node)
76
+ end
77
+
78
+ def receiver_of_chained_call?(node)
79
+ node.parent.receiver == node
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -104,28 +104,30 @@ module RuboCop
104
104
 
105
105
  def replacement(mode, filename, content, write_node)
106
106
  replacement = "#{write_method(mode)}(#{filename.source}, #{content.source})"
107
+ heredoc = heredoc_in_write(write_node)
108
+ return replacement unless heredoc
107
109
 
108
- if heredoc?(write_node)
109
- first_argument = write_node.body.first_argument
110
+ <<~REPLACEMENT.chomp
111
+ #{replacement}
112
+ #{heredoc_range(heredoc).source}
113
+ REPLACEMENT
114
+ end
110
115
 
111
- <<~REPLACEMENT.chomp
112
- #{replacement}
113
- #{heredoc_range(first_argument).source}
114
- REPLACEMENT
115
- else
116
- replacement
117
- end
116
+ def heredoc_in_write(write_node)
117
+ return unless write_node.block_type? && (first_argument = write_node.body.first_argument)
118
+
119
+ find_heredoc(first_argument)
118
120
  end
119
121
 
120
- def heredoc?(write_node)
121
- write_node.block_type? && (first_argument = write_node.body.first_argument) &&
122
- first_argument.respond_to?(:heredoc?) && first_argument.heredoc?
122
+ def heredoc_range(heredoc)
123
+ range_between(heredoc.loc.heredoc_body.begin_pos, heredoc.loc.heredoc_end.end_pos)
123
124
  end
124
125
 
125
- def heredoc_range(first_argument)
126
- range_between(
127
- first_argument.loc.heredoc_body.begin_pos, first_argument.loc.heredoc_end.end_pos
128
- )
126
+ def find_heredoc(node)
127
+ return node if node.respond_to?(:heredoc?) && node.heredoc?
128
+ return if node.send_type? && !(receiver = node.receiver)
129
+
130
+ find_heredoc(receiver)
129
131
  end
130
132
  end
131
133
  end
@@ -8,6 +8,9 @@ module RuboCop
8
8
  # parameter. An `each` call with a block on a single line is always
9
9
  # allowed.
10
10
  #
11
+ # NOTE: `each` is preferred in idiomatic Ruby because `for` leaks
12
+ # its loop variable into the surrounding scope.
13
+ #
11
14
  # @example EnforcedStyle: each (default)
12
15
  # # bad
13
16
  # def foo
@@ -11,9 +11,10 @@ module RuboCop
11
11
  # if the first argument is a string literal and if the second
12
12
  # argument is an array literal.
13
13
  #
14
- # Autocorrection will be applied when using argument is a literal or known built-in conversion
15
- # methods such as `to_d`, `to_f`, `to_h`, `to_i`, `to_r`, `to_s`, and `to_sym` on variables,
16
- # provided that their return value is not an array. For example, when using `to_s`,
14
+ # Autocorrection will be applied when the argument is a literal or uses a known
15
+ # built-in conversion method such as `to_d`, `to_f`, `to_h`, `to_i`, `to_r`, `to_s`,
16
+ # and `to_sym` on variables, provided that their return value is not an array.
17
+ # For example, when using `to_s`,
17
18
  # `'%s' % [1, 2, 3].to_s` can be autocorrected without any incompatibility:
18
19
  #
19
20
  # [source,ruby]
@@ -18,6 +18,11 @@ module RuboCop
18
18
  # of `EnforcedStyle`) are only considered if used in the format string argument to the
19
19
  # methods `printf`, `sprintf`, `format` and `%`.
20
20
  #
21
+ # NOTE: In `aggressive` mode, offenses are registered for all strings containing tokens,
22
+ # but autocorrection is only applied when the string appears in a known formatting context
23
+ # (`format`, `sprintf`, `printf`, or `%`). This is done in order to prevent false
24
+ # autocorrections for strings that are not actually format strings.
25
+ #
21
26
  # NOTE: Tokens in the `unannotated` style (eg. `%s`) are always treated as if
22
27
  # configured with `Conservative: true`. This is done in order to prevent false positives,
23
28
  # because this format is very similar to encoded URLs or Date/Time formatting strings.
@@ -90,9 +95,25 @@ module RuboCop
90
95
  # # good
91
96
  # redirect('foo/%{bar_id}')
92
97
  #
98
+ # @example Mode: aggressive (default), EnforcedStyle: annotated
99
+ #
100
+ # # bad
101
+ # "%{greeting}"
102
+ # foo("%{greeting}")
103
+ #
104
+ # # bad
105
+ # format("%{greeting}", greeting: 'Hello')
106
+ # printf("%{greeting}", greeting: 'Hello')
107
+ # sprintf("%{greeting}", greeting: 'Hello')
108
+ # "%{greeting}" % { greeting: 'Hello' }
109
+ #
110
+ # # good
111
+ # format("%<greeting>s", greeting: 'Hello')
112
+ # printf("%<greeting>s", greeting: 'Hello')
113
+ # sprintf("%<greeting>s", greeting: 'Hello')
114
+ # "%<greeting>s" % { greeting: 'Hello' }
115
+ #
93
116
  # @example Mode: conservative, EnforcedStyle: annotated
94
- # # In `conservative` mode, offenses are only registered for strings
95
- # # given to a known formatting method.
96
117
  #
97
118
  # # good
98
119
  # "%{greeting}"
@@ -104,6 +125,12 @@ module RuboCop
104
125
  # sprintf("%{greeting}", greeting: 'Hello')
105
126
  # "%{greeting}" % { greeting: 'Hello' }
106
127
  #
128
+ # # good
129
+ # format("%<greeting>s", greeting: 'Hello')
130
+ # printf("%<greeting>s", greeting: 'Hello')
131
+ # sprintf("%<greeting>s", greeting: 'Hello')
132
+ # "%<greeting>s" % { greeting: 'Hello' }
133
+ #
107
134
  class FormatStringToken < Base
108
135
  include ConfigurableEnforcedStyle
109
136
  include AllowedMethods
@@ -3,7 +3,10 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Looks for uses of global variables.
6
+ # Looks for uses of global variables. Global variables introduce
7
+ # shared mutable state that makes code harder to test, debug,
8
+ # and reason about, since any part of the program can read or modify them.
9
+ #
7
10
  # It does not report offenses for built-in global variables.
8
11
  # Built-in global variables are allowed by default. Additionally
9
12
  # users can allow additional variables via the AllowedVariables option.
@@ -54,7 +57,7 @@ module RuboCop
54
57
  ].map(&:to_sym)
55
58
 
56
59
  def user_vars
57
- cop_config['AllowedVariables'].map(&:to_sym)
60
+ @user_vars ||= cop_config['AllowedVariables'].map(&:to_sym).freeze
58
61
  end
59
62
 
60
63
  def allowed_var?(global_var)
@@ -224,15 +224,18 @@ module RuboCop
224
224
  end
225
225
 
226
226
  def find_heredoc_argument(node)
227
- return unless node&.call_type?
227
+ return unless node
228
228
 
229
- last_arg = node.last_argument
229
+ node = node.children.first while node.begin_type?
230
+ return node if heredoc?(node)
231
+ return unless node.call_type?
230
232
 
231
- if heredoc?(last_arg)
232
- last_arg
233
- elsif last_arg&.call_type?
234
- find_heredoc_argument(last_arg)
233
+ node.arguments.reverse_each do |argument|
234
+ heredoc_argument = find_heredoc_argument(argument)
235
+ return heredoc_argument if heredoc_argument
235
236
  end
237
+
238
+ find_heredoc_argument(node.receiver)
236
239
  end
237
240
 
238
241
  def autocorrect_heredoc_argument(corrector, node, heredoc_node, leave_branch, guard)
@@ -6,8 +6,13 @@ module RuboCop
6
6
  # Checks for presence or absence of braces around hash literal as a last
7
7
  # array item depending on configuration.
8
8
  #
9
- # NOTE: This cop will ignore arrays where all items are hashes, regardless of
10
- # EnforcedStyle.
9
+ # NOTE: This cop will ignore arrays where multiple items are all hashes,
10
+ # regardless of `EnforcedStyle`.
11
+ #
12
+ # [source,ruby]
13
+ # ----
14
+ # [{ one: 1 }, { two: 2 }]
15
+ # ----
11
16
  #
12
17
  # @example EnforcedStyle: braces (default)
13
18
  # # bad
@@ -16,8 +21,11 @@ module RuboCop
16
21
  # # good
17
22
  # [1, 2, { one: 1, two: 2 }]
18
23
  #
24
+ # # bad
25
+ # [one: 1, two: 2]
26
+ #
19
27
  # # good
20
- # [{ one: 1 }, { two: 2 }]
28
+ # [{ one: 1, two: 2 }]
21
29
  #
22
30
  # @example EnforcedStyle: no_braces
23
31
  # # bad
@@ -26,8 +34,11 @@ module RuboCop
26
34
  # # good
27
35
  # [1, 2, one: 1, two: 2]
28
36
  #
37
+ # # bad
38
+ # [{ one: 1, two: 2 }]
39
+ #
29
40
  # # good
30
- # [{ one: 1 }, { two: 2 }]
41
+ # [one: 1, two: 2]
31
42
  class HashAsLastArrayItem < Base
32
43
  include RangeHelp
33
44
  include ConfigurableEnforcedStyle
@@ -69,7 +80,12 @@ module RuboCop
69
80
  return if node.braces?
70
81
 
71
82
  add_offense(node, message: 'Wrap hash in `{` and `}`.') do |corrector|
72
- corrector.wrap(node, '{', '}')
83
+ if node.single_line? || same_line?(node, node.parent)
84
+ corrector.wrap(node, '{', '}')
85
+ else
86
+ indent = indent(node)
87
+ corrector.wrap(node, "{\n#{indent}", "\n#{indent}}")
88
+ end
73
89
  end
74
90
  end
75
91
 
@@ -73,7 +73,7 @@ module RuboCop
73
73
  first_argument = node.first_argument
74
74
  if first_argument.hash_type?
75
75
  register_offense_for_hash(node, first_argument)
76
- elsif first_argument.splat_type?
76
+ elsif first_argument.type?(:splat, :forwarded_restarg)
77
77
  add_offense(node, message: MSG_SPLAT) unless allowed_splat_argument?
78
78
  elsif use_zip_method_without_argument?(first_argument)
79
79
  register_offense_for_zip_method(node, first_argument)
@@ -41,8 +41,13 @@ module RuboCop
41
41
  # # good
42
42
  # hash.fetch(key)
43
43
  #
44
+ # @example AllowedReceivers: ['Rails.cache']
45
+ # # good
46
+ # Rails.cache.fetch(name, options) { block }
47
+ #
44
48
  class HashLookupMethod < Base
45
49
  include ConfigurableEnforcedStyle
50
+ include AllowedReceivers
46
51
  extend AutoCorrector
47
52
 
48
53
  BRACKET_MSG = 'Use `Hash#[]` instead of `Hash#fetch`.'
@@ -51,6 +56,8 @@ module RuboCop
51
56
  RESTRICT_ON_SEND = %i[[] fetch].freeze
52
57
 
53
58
  def on_send(node)
59
+ return if (receiver = node.receiver) && allowed_receiver?(receiver)
60
+
54
61
  if offense_for_brackets?(node)
55
62
  add_offense(node.loc.selector, message: BRACKET_MSG) do |corrector|
56
63
  correct_fetch_to_brackets(corrector, node)
@@ -75,18 +82,23 @@ module RuboCop
75
82
  end
76
83
 
77
84
  def correct_fetch_to_brackets(corrector, node)
78
- receiver = node.receiver.source
79
85
  key = node.first_argument.source
80
- replacement = "#{receiver}[#{key}]"
81
- replacement = "(#{replacement})" if node.csend_type?
82
- corrector.replace(node, replacement)
86
+
87
+ if node.csend_type?
88
+ corrector.replace(node, "(#{node.receiver.source}[#{key}])")
89
+ else
90
+ corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
91
+ end
83
92
  end
84
93
 
85
94
  def correct_brackets_to_fetch(corrector, node)
86
- receiver = node.receiver.source
87
95
  key = node.first_argument.source
88
- operator = node.csend_type? ? '&.' : '.'
89
- corrector.replace(node, "#{receiver}#{operator}fetch(#{key})")
96
+
97
+ if node.csend_type?
98
+ corrector.replace(node.loc.dot.join(node.source_range.end), "&.fetch(#{key})")
99
+ else
100
+ corrector.replace(node.loc.selector.join(node.source_range.end), ".fetch(#{key})")
101
+ end
90
102
  end
91
103
  end
92
104
  end
@@ -11,9 +11,11 @@ module RuboCop
11
11
  # (`transform_keys` was added in Ruby 2.5.)
12
12
  #
13
13
  # @safety
14
- # This cop is unsafe, as it can produce false positives if we are
15
- # transforming an enumerable of key-value-like pairs that isn't actually
16
- # a hash, e.g.: `[[k1, v1], [k2, v2], ...]`
14
+ # This cop identifies the receiver as a hash by checking for literal hash
15
+ # syntax and common methods that are known to return hashes (e.g. `to_h`,
16
+ # `merge`, `invert`, `group_by`, etc.). However, it is unsafe because it
17
+ # is possible for a custom class to define one of these methods and return
18
+ # something other than a hash.
17
19
  #
18
20
  # @example
19
21
  # # bad
@@ -21,10 +23,18 @@ module RuboCop
21
23
  # Hash[{a: 1, b: 2}.collect { |k, v| [foo(k), v] }]
22
24
  # {a: 1, b: 2}.map { |k, v| [k.to_s, v] }.to_h
23
25
  # {a: 1, b: 2}.to_h { |k, v| [k.to_s, v] }
26
+ # foo.to_h.each_with_object({}) { |(k, v), h| h[k.to_sym] = v }
27
+ # foo.merge(bar).map { |k, v| [k.to_s, v] }.to_h
24
28
  #
25
29
  # # good
26
30
  # {a: 1, b: 2}.transform_keys { |k| foo(k) }
27
31
  # {a: 1, b: 2}.transform_keys { |k| k.to_s }
32
+ # foo.to_h.transform_keys { |k| k.to_sym }
33
+ # foo.merge(bar).transform_keys { |k| k.to_s }
34
+ #
35
+ # # Won't register an offense - receiver is not known to be a hash
36
+ # foo.bar.each_with_object({}) { |(k, v), h| h[k.to_s] = v }
37
+ # baz.map { |k, v| [k.to_s, v] }.to_h
28
38
  class HashTransformKeys < Base
29
39
  include HashTransformMethod
30
40
  extend AutoCorrector
@@ -35,7 +45,7 @@ module RuboCop
35
45
  # @!method on_bad_each_with_object(node)
36
46
  def_node_matcher :on_bad_each_with_object, <<~PATTERN
37
47
  (block
38
- (call !#array_receiver? :each_with_object (hash))
48
+ (call #hash_receiver? :each_with_object (hash))
39
49
  (args
40
50
  (mlhs
41
51
  (arg $_)
@@ -50,7 +60,7 @@ module RuboCop
50
60
  (const _ :Hash)
51
61
  :[]
52
62
  (block
53
- (call !#array_receiver? {:map :collect})
63
+ (call #hash_receiver? {:map :collect})
54
64
  (args
55
65
  (arg $_)
56
66
  (arg _val))
@@ -61,7 +71,7 @@ module RuboCop
61
71
  def_node_matcher :on_bad_map_to_h, <<~PATTERN
62
72
  (call
63
73
  (block
64
- (call !#array_receiver? {:map :collect})
74
+ (call #hash_receiver? {:map :collect})
65
75
  (args
66
76
  (arg $_)
67
77
  (arg _val))
@@ -72,7 +82,7 @@ module RuboCop
72
82
  # @!method on_bad_to_h(node)
73
83
  def_node_matcher :on_bad_to_h, <<~PATTERN
74
84
  (block
75
- (call !#array_receiver? :to_h)
85
+ (call #hash_receiver? :to_h)
76
86
  (args
77
87
  (arg $_)
78
88
  (arg _val))