rubocop 1.45.1 → 1.50.1

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 (223) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +46 -15
  4. data/lib/rubocop/cli/command/auto_generate_config.rb +7 -0
  5. data/lib/rubocop/cli/command/execute_runner.rb +7 -2
  6. data/lib/rubocop/cli.rb +6 -6
  7. data/lib/rubocop/comment_config.rb +19 -0
  8. data/lib/rubocop/config.rb +3 -3
  9. data/lib/rubocop/config_loader.rb +8 -8
  10. data/lib/rubocop/cop/autocorrect_logic.rb +29 -13
  11. data/lib/rubocop/cop/base.rb +1 -1
  12. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  13. data/lib/rubocop/cop/cop.rb +2 -2
  14. data/lib/rubocop/cop/corrector.rb +1 -1
  15. data/lib/rubocop/cop/correctors/alignment_corrector.rb +2 -2
  16. data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +3 -3
  17. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +3 -3
  18. data/lib/rubocop/cop/correctors/line_break_corrector.rb +1 -1
  19. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +2 -2
  20. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -1
  21. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
  22. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  23. data/lib/rubocop/cop/gemspec/dependency_version.rb +1 -1
  24. data/lib/rubocop/cop/internal_affairs/cop_description.rb +5 -5
  25. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +3 -3
  26. data/lib/rubocop/cop/internal_affairs/inherit_deprecated_cop_class.rb +1 -1
  27. data/lib/rubocop/cop/internal_affairs/location_expression.rb +37 -0
  28. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  29. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +1 -1
  30. data/lib/rubocop/cop/internal_affairs/processed_source_buffer_name.rb +42 -0
  31. data/lib/rubocop/cop/internal_affairs/redundant_location_argument.rb +1 -1
  32. data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +1 -1
  33. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +66 -0
  34. data/lib/rubocop/cop/internal_affairs.rb +3 -0
  35. data/lib/rubocop/cop/layout/block_end_newline.rb +7 -21
  36. data/lib/rubocop/cop/layout/class_structure.rb +6 -3
  37. data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +1 -1
  38. data/lib/rubocop/cop/layout/empty_comment.rb +3 -3
  39. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +1 -1
  40. data/lib/rubocop/cop/layout/empty_lines.rb +1 -1
  41. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +2 -0
  42. data/lib/rubocop/cop/layout/end_alignment.rb +9 -1
  43. data/lib/rubocop/cop/layout/extra_spacing.rb +6 -1
  44. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  45. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +8 -2
  46. data/lib/rubocop/cop/layout/heredoc_indentation.rb +2 -2
  47. data/lib/rubocop/cop/layout/initial_indentation.rb +1 -1
  48. data/lib/rubocop/cop/layout/leading_comment_space.rb +1 -1
  49. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +1 -3
  50. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -7
  51. data/lib/rubocop/cop/layout/redundant_line_break.rb +6 -7
  52. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +2 -2
  53. data/lib/rubocop/cop/layout/space_before_first_arg.rb +1 -1
  54. data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +2 -2
  55. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -1
  56. data/lib/rubocop/cop/layout/space_inside_parens.rb +2 -2
  57. data/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +1 -1
  58. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  59. data/lib/rubocop/cop/lint/constant_resolution.rb +1 -1
  60. data/lib/rubocop/cop/lint/debugger.rb +3 -0
  61. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  62. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
  63. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +122 -0
  64. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
  65. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -3
  66. data/lib/rubocop/cop/lint/else_layout.rb +1 -1
  67. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  68. data/lib/rubocop/cop/lint/empty_conditional_body.rb +4 -2
  69. data/lib/rubocop/cop/lint/empty_interpolation.rb +1 -1
  70. data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -1
  71. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +46 -4
  72. data/lib/rubocop/cop/lint/missing_super.rb +31 -2
  73. data/lib/rubocop/cop/lint/nested_method_definition.rb +3 -11
  74. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +2 -0
  75. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +6 -10
  76. data/lib/rubocop/cop/lint/percent_string_array.rb +1 -1
  77. data/lib/rubocop/cop/lint/percent_symbol_array.rb +1 -1
  78. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +11 -5
  79. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +5 -5
  80. data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -1
  81. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
  82. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +35 -15
  83. data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -1
  84. data/lib/rubocop/cop/lint/redundant_with_object.rb +1 -1
  85. data/lib/rubocop/cop/lint/refinement_import_methods.rb +2 -1
  86. data/lib/rubocop/cop/lint/rescue_type.rb +3 -3
  87. data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +1 -1
  88. data/lib/rubocop/cop/lint/script_permission.rb +1 -1
  89. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  90. data/lib/rubocop/cop/lint/syntax.rb +4 -0
  91. data/lib/rubocop/cop/lint/to_enum_arguments.rb +13 -3
  92. data/lib/rubocop/cop/lint/unreachable_loop.rb +3 -3
  93. data/lib/rubocop/cop/lint/useless_access_modifier.rb +10 -10
  94. data/lib/rubocop/cop/lint/useless_method_definition.rb +10 -2
  95. data/lib/rubocop/cop/lint/useless_rescue.rb +6 -2
  96. data/lib/rubocop/cop/lint/useless_times.rb +1 -1
  97. data/lib/rubocop/cop/lint/void.rb +7 -3
  98. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  99. data/lib/rubocop/cop/metrics/class_length.rb +1 -0
  100. data/lib/rubocop/cop/metrics/collection_literal_length.rb +76 -0
  101. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +3 -3
  102. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  103. data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
  104. data/lib/rubocop/cop/mixin/code_length.rb +1 -1
  105. data/lib/rubocop/cop/mixin/comments_help.rb +3 -3
  106. data/lib/rubocop/cop/mixin/documentation_comment.rb +1 -1
  107. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +1 -1
  108. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +10 -5
  109. data/lib/rubocop/cop/mixin/hash_transform_method.rb +3 -3
  110. data/lib/rubocop/cop/mixin/min_branches_count.rb +40 -0
  111. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +0 -3
  112. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  113. data/lib/rubocop/cop/mixin/range_help.rb +1 -6
  114. data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -3
  115. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  116. data/lib/rubocop/cop/naming/ascii_identifiers.rb +1 -1
  117. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +1 -1
  118. data/lib/rubocop/cop/naming/inclusive_language.rb +23 -4
  119. data/lib/rubocop/cop/naming/method_name.rb +3 -3
  120. data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
  121. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
  122. data/lib/rubocop/cop/registry.rb +3 -1
  123. data/lib/rubocop/cop/style/accessor_grouping.rb +39 -17
  124. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
  125. data/lib/rubocop/cop/style/array_intersect.rb +1 -1
  126. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  127. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +1 -1
  128. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  129. data/lib/rubocop/cop/style/block_delimiters.rb +11 -2
  130. data/lib/rubocop/cop/style/case_like_if.rb +20 -3
  131. data/lib/rubocop/cop/style/class_and_module_children.rb +1 -1
  132. data/lib/rubocop/cop/style/class_equality_comparison.rb +42 -9
  133. data/lib/rubocop/cop/style/collection_compact.rb +1 -1
  134. data/lib/rubocop/cop/style/comment_annotation.rb +1 -1
  135. data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
  136. data/lib/rubocop/cop/style/concat_array_literals.rb +10 -2
  137. data/lib/rubocop/cop/style/conditional_assignment.rb +6 -6
  138. data/lib/rubocop/cop/style/copyright.rb +1 -1
  139. data/lib/rubocop/cop/style/data_inheritance.rb +75 -0
  140. data/lib/rubocop/cop/style/dir_empty.rb +60 -0
  141. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +2 -2
  142. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +2 -2
  143. data/lib/rubocop/cop/style/documentation.rb +10 -4
  144. data/lib/rubocop/cop/style/documentation_method.rb +4 -4
  145. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  146. data/lib/rubocop/cop/style/each_with_object.rb +1 -1
  147. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  148. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  149. data/lib/rubocop/cop/style/eval_with_location.rb +4 -4
  150. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  151. data/lib/rubocop/cop/style/file_empty.rb +71 -0
  152. data/lib/rubocop/cop/style/file_read.rb +1 -1
  153. data/lib/rubocop/cop/style/file_write.rb +1 -1
  154. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  155. data/lib/rubocop/cop/style/guard_clause.rb +1 -1
  156. data/lib/rubocop/cop/style/hash_except.rb +4 -4
  157. data/lib/rubocop/cop/style/hash_like_case.rb +3 -9
  158. data/lib/rubocop/cop/style/hash_syntax.rb +5 -2
  159. data/lib/rubocop/cop/style/if_unless_modifier.rb +108 -15
  160. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -0
  161. data/lib/rubocop/cop/style/inverse_methods.rb +5 -5
  162. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +2 -2
  163. data/lib/rubocop/cop/style/map_to_hash.rb +4 -1
  164. data/lib/rubocop/cop/style/map_to_set.rb +4 -1
  165. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +3 -7
  166. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +44 -37
  167. data/lib/rubocop/cop/style/min_max.rb +3 -3
  168. data/lib/rubocop/cop/style/mixin_grouping.rb +4 -4
  169. data/lib/rubocop/cop/style/multiline_method_signature.rb +7 -4
  170. data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
  171. data/lib/rubocop/cop/style/negated_if_else_condition.rb +12 -7
  172. data/lib/rubocop/cop/style/nil_lambda.rb +2 -2
  173. data/lib/rubocop/cop/style/parallel_assignment.rb +26 -18
  174. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -3
  175. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  176. data/lib/rubocop/cop/style/redundant_condition.rb +2 -2
  177. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +2 -2
  178. data/lib/rubocop/cop/style/redundant_interpolation.rb +2 -2
  179. data/lib/rubocop/cop/style/redundant_line_continuation.rb +179 -0
  180. data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -2
  181. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  182. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -8
  183. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +11 -4
  184. data/lib/rubocop/cop/style/redundant_sort.rb +3 -3
  185. data/lib/rubocop/cop/style/redundant_string_escape.rb +3 -4
  186. data/lib/rubocop/cop/style/require_order.rb +1 -3
  187. data/lib/rubocop/cop/style/rescue_standard_error.rb +2 -2
  188. data/lib/rubocop/cop/style/safe_navigation.rb +2 -2
  189. data/lib/rubocop/cop/style/slicing_with_range.rb +1 -1
  190. data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -3
  191. data/lib/rubocop/cop/style/stderr_puts.rb +1 -1
  192. data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
  193. data/lib/rubocop/cop/style/trailing_body_on_class.rb +1 -0
  194. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +1 -1
  195. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  196. data/lib/rubocop/cop/style/unless_logical_operators.rb +1 -0
  197. data/lib/rubocop/cop/style/unpack_first.rb +3 -3
  198. data/lib/rubocop/cop/style/word_array.rb +17 -5
  199. data/lib/rubocop/cop/style/yoda_condition.rb +1 -1
  200. data/lib/rubocop/cop/style/zero_length_predicate.rb +9 -5
  201. data/lib/rubocop/cop/team.rb +11 -8
  202. data/lib/rubocop/cop/util.rb +13 -4
  203. data/lib/rubocop/cop/variable_force/variable.rb +5 -3
  204. data/lib/rubocop/cops_documentation_generator.rb +10 -3
  205. data/lib/rubocop/directive_comment.rb +3 -3
  206. data/lib/rubocop/ext/comment.rb +18 -0
  207. data/lib/rubocop/ext/regexp_node.rb +1 -1
  208. data/lib/rubocop/ext/regexp_parser.rb +1 -1
  209. data/lib/rubocop/formatter/junit_formatter.rb +4 -1
  210. data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
  211. data/lib/rubocop/options.rb +4 -1
  212. data/lib/rubocop/result_cache.rb +1 -1
  213. data/lib/rubocop/rspec/cop_helper.rb +1 -1
  214. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  215. data/lib/rubocop/rspec/support.rb +1 -0
  216. data/lib/rubocop/server/cache.rb +1 -1
  217. data/lib/rubocop/server/core.rb +1 -1
  218. data/lib/rubocop/server/helper.rb +1 -1
  219. data/lib/rubocop/server/server_command/exec.rb +1 -1
  220. data/lib/rubocop/target_ruby.rb +1 -1
  221. data/lib/rubocop/version.rb +1 -1
  222. data/lib/rubocop.rb +8 -0
  223. metadata +20 -9
@@ -34,6 +34,7 @@ module RuboCop
34
34
 
35
35
  def on_send(node)
36
36
  return unless regular_method_call_with_arguments?(node)
37
+ return if node.parenthesized?
37
38
 
38
39
  first_arg = node.first_argument.source_range
39
40
  first_arg_with_space = range_with_surrounding_space(first_arg, side: :left)
@@ -52,7 +53,6 @@ module RuboCop
52
53
  end
53
54
 
54
55
  def expect_params_after_method_name?(node)
55
- return false if node.parenthesized?
56
56
  return true if no_space_between_method_name_and_first_argument?(node)
57
57
 
58
58
  first_arg = node.first_argument
@@ -64,8 +64,8 @@ module RuboCop
64
64
 
65
65
  def range_of_offense(node)
66
66
  range_between(
67
- node.parent.loc.expression.begin_pos,
68
- node.parent.arguments.loc.expression.end_pos
67
+ node.parent.source_range.begin_pos,
68
+ node.parent.arguments.source_range.end_pos
69
69
  )
70
70
  end
71
71
 
@@ -146,7 +146,7 @@ module RuboCop
146
146
  if single_line && /\S$/.match?(inner)
147
147
  no_space(right_brace.begin_pos, right_brace.end_pos, 'Space missing inside }.')
148
148
  else
149
- column = node.loc.expression.column
149
+ column = node.source_range.column
150
150
  return if multiline_block?(left_brace, right_brace) &&
151
151
  aligned_braces?(inner, right_brace, column)
152
152
 
@@ -91,7 +91,7 @@ module RuboCop
91
91
  if !left_parens?(token1, token2) && !right_parens?(token1, token2)
92
92
  correct_missing_space(token1, token2)
93
93
  else
94
- correct_extaneus_space_between_consecutive_parens(token1, token2)
94
+ correct_extraneous_space_between_consecutive_parens(token1, token2)
95
95
  end
96
96
  end
97
97
  end
@@ -112,7 +112,7 @@ module RuboCop
112
112
  end
113
113
  end
114
114
 
115
- def correct_extaneus_space_between_consecutive_parens(token1, token2)
115
+ def correct_extraneous_space_between_consecutive_parens(token1, token2)
116
116
  return if range_between(token1.end_pos, token2.begin_pos).source != ' '
117
117
 
118
118
  range = range_between(token1.end_pos, token2.begin_pos)
@@ -83,7 +83,7 @@ module RuboCop
83
83
  end
84
84
 
85
85
  def body_range(node)
86
- node.location.expression.with(
86
+ node.source_range.with(
87
87
  begin_pos: node.location.begin.end_pos,
88
88
  end_pos: node.location.end.begin_pos
89
89
  )
@@ -93,7 +93,7 @@ module RuboCop
93
93
  end
94
94
 
95
95
  def static?(heredoc)
96
- heredoc.loc.expression.source.end_with? "'"
96
+ heredoc.source.end_with? "'"
97
97
  end
98
98
 
99
99
  def skip_heredoc?
@@ -68,7 +68,7 @@ module RuboCop
68
68
  PATTERN
69
69
 
70
70
  def on_const(node)
71
- return if !unqualified_const?(node) || node.parent&.defined_module
71
+ return if !unqualified_const?(node) || node.parent&.defined_module || node.loc.nil?
72
72
 
73
73
  add_offense(node)
74
74
  end
@@ -70,6 +70,9 @@ module RuboCop
70
70
  def on_send(node)
71
71
  return unless debugger_method?(node)
72
72
 
73
+ # Basically, debugger methods are not used as a method argument without arguments.
74
+ return if node.arguments.empty? && node.each_ancestor(:send, :csend).any?
75
+
73
76
  add_offense(node)
74
77
  end
75
78
 
@@ -38,7 +38,7 @@ module RuboCop
38
38
  attr clone dup exists? freeze gethostbyaddr gethostbyname iterator?
39
39
  ].freeze
40
40
 
41
- PREFERRED_METHDOS = {
41
+ PREFERRED_METHODS = {
42
42
  clone: 'to_h',
43
43
  dup: 'to_h',
44
44
  exists?: 'exist?',
@@ -82,7 +82,7 @@ module RuboCop
82
82
 
83
83
  def offense_range(node)
84
84
  if socket_const?(node.receiver) || dir_env_file_const?(node.receiver)
85
- node.loc.expression.begin.join(node.loc.selector.end)
85
+ node.source_range.begin.join(node.loc.selector.end)
86
86
  elsif node.method?(:attr)
87
87
  node
88
88
  else
@@ -97,11 +97,11 @@ module RuboCop
97
97
 
98
98
  "#{preferred_attr_method} #{node.first_argument.source}"
99
99
  elsif dir_env_file_const?(node.receiver)
100
- prefer = PREFERRED_METHDOS[node.method_name]
100
+ prefer = PREFERRED_METHODS[node.method_name]
101
101
 
102
102
  prefer ? "#{node.receiver.source}.#{prefer}" : 'ENV'
103
103
  else
104
- PREFERRED_METHDOS[node.method_name]
104
+ PREFERRED_METHODS[node.method_name]
105
105
  end
106
106
  end
107
107
 
@@ -100,7 +100,7 @@ module RuboCop
100
100
  end
101
101
 
102
102
  def correction_range(node)
103
- range_between(node.loc.dot.end_pos, node.loc.expression.end_pos)
103
+ range_between(node.loc.dot.end_pos, node.source_range.end_pos)
104
104
  end
105
105
 
106
106
  def openssl_class(node)
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks that there are no repeated patterns used in `in` keywords.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # case x
12
+ # in 'first'
13
+ # do_something
14
+ # in 'first'
15
+ # do_something_else
16
+ # end
17
+ #
18
+ # # good
19
+ # case x
20
+ # in 'first'
21
+ # do_something
22
+ # in 'second'
23
+ # do_something_else
24
+ # end
25
+ #
26
+ # # bad - repeated alternate patterns with the same conditions don't depend on the order
27
+ # case x
28
+ # in foo | bar
29
+ # first_method
30
+ # in bar | foo
31
+ # second_method
32
+ # end
33
+ #
34
+ # # good
35
+ # case x
36
+ # in foo | bar
37
+ # first_method
38
+ # in bar | baz
39
+ # second_method
40
+ # end
41
+ #
42
+ # # bad - repeated hash patterns with the same conditions don't depend on the order
43
+ # case x
44
+ # in foo: a, bar: b
45
+ # first_method
46
+ # in bar: b, foo: a
47
+ # second_method
48
+ # end
49
+ #
50
+ # # good
51
+ # case x
52
+ # in foo: a, bar: b
53
+ # first_method
54
+ # in bar: b, baz: c
55
+ # second_method
56
+ # end
57
+ #
58
+ # # bad - repeated array patterns with elements in the same order
59
+ # case x
60
+ # in [foo, bar]
61
+ # first_method
62
+ # in [foo, bar]
63
+ # second_method
64
+ # end
65
+ #
66
+ # # good
67
+ # case x
68
+ # in [foo, bar]
69
+ # first_method
70
+ # in [bar, foo]
71
+ # second_method
72
+ # end
73
+ #
74
+ # # bad - repeated the same patterns and guard conditions
75
+ # case x
76
+ # in foo if bar
77
+ # first_method
78
+ # in foo if bar
79
+ # second_method
80
+ # end
81
+ #
82
+ # # good
83
+ # case x
84
+ # in foo if bar
85
+ # first_method
86
+ # in foo if baz
87
+ # second_method
88
+ # end
89
+ #
90
+ class DuplicateMatchPattern < Base
91
+ extend TargetRubyVersion
92
+
93
+ MSG = 'Duplicate `in` pattern detected.'
94
+
95
+ minimum_target_ruby_version 2.7
96
+
97
+ def on_case_match(case_node)
98
+ case_node.in_pattern_branches.each_with_object(Set.new) do |in_pattern_node, previous|
99
+ pattern = in_pattern_node.pattern
100
+ next if previous.add?(pattern_identity(pattern))
101
+
102
+ add_offense(pattern)
103
+ end
104
+ end
105
+
106
+ private
107
+
108
+ def pattern_identity(pattern)
109
+ pattern_source = if pattern.hash_pattern_type? || pattern.match_alt_type?
110
+ pattern.children.map(&:source).sort
111
+ else
112
+ pattern.source
113
+ end
114
+
115
+ return pattern_source unless (guard = pattern.parent.children[1])
116
+
117
+ pattern_source + guard.source
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -187,7 +187,7 @@ module RuboCop
187
187
  if DEF_TYPES.include?(node.type)
188
188
  node.loc.keyword.join(node.loc.name)
189
189
  else
190
- node.loc.expression
190
+ node.source_range
191
191
  end
192
192
  end
193
193
 
@@ -258,7 +258,7 @@ module RuboCop
258
258
  end
259
259
 
260
260
  def source_location(node)
261
- range = node.location.expression
261
+ range = node.source_range
262
262
  path = smart_path(range.source_buffer.name)
263
263
  "#{path}:#{range.line}"
264
264
  end
@@ -86,9 +86,7 @@ module RuboCop
86
86
  # Cache by loc, not by regexp content, as content can be repeated in multiple patterns
87
87
  key = node.loc
88
88
 
89
- @interpolation_locs[key] ||= node.children.select(&:begin_type?).map do |interpolation|
90
- interpolation.loc.expression
91
- end
89
+ @interpolation_locs[key] ||= node.children.select(&:begin_type?).map(&:source_range)
92
90
  end
93
91
  end
94
92
  end
@@ -81,7 +81,7 @@ module RuboCop
81
81
  def autocorrect(corrector, node, first_else)
82
82
  corrector.insert_after(node.loc.else, "\n")
83
83
 
84
- blank_range = range_between(node.loc.else.end_pos, first_else.loc.expression.begin_pos)
84
+ blank_range = range_between(node.loc.else.end_pos, first_else.source_range.begin_pos)
85
85
  corrector.replace(blank_range, indentation(node))
86
86
  end
87
87
  end
@@ -77,7 +77,7 @@ module RuboCop
77
77
  return false unless processed_source.contains_comment?(node.source_range)
78
78
 
79
79
  line_comment = processed_source.comment_at_line(node.source_range.line)
80
- !line_comment || !comment_disables_cop?(line_comment.loc.expression.source)
80
+ !line_comment || !comment_disables_cop?(line_comment.source)
81
81
  end
82
82
 
83
83
  def allow_empty_lambdas?
@@ -72,6 +72,8 @@ module RuboCop
72
72
  return if cop_config['AllowComments'] && contains_comments?(node)
73
73
 
74
74
  add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
75
+ next if node.parent&.call_type?
76
+
75
77
  autocorrect(corrector, node)
76
78
  end
77
79
  end
@@ -86,7 +88,7 @@ module RuboCop
86
88
 
87
89
  def remove_comments(corrector, node)
88
90
  comments_in_range(node).each do |comment|
89
- range = range_by_whole_lines(comment.loc.expression, include_final_newline: true)
91
+ range = range_by_whole_lines(comment.source_range, include_final_newline: true)
90
92
  corrector.remove(range)
91
93
  end
92
94
  end
@@ -141,7 +143,7 @@ module RuboCop
141
143
  if empty_if_branch?(node) && else_branch?(node)
142
144
  node.source_range.with(end_pos: node.loc.else.begin_pos)
143
145
  elsif node.loc.else
144
- node.source_range.with(end_pos: node.condition.loc.expression.end_pos)
146
+ node.source_range.with(end_pos: node.condition.source_range.end_pos)
145
147
  elsif all_branches_body_missing?(node)
146
148
  if_node = node.ancestors.detect(&:if?)
147
149
  node.source_range.with(end_pos: if_node.loc.end.end_pos)
@@ -25,7 +25,7 @@ module RuboCop
25
25
  def on_interpolation(begin_node)
26
26
  return unless begin_node.children.empty?
27
27
 
28
- add_offense(begin_node) { |corrector| corrector.remove(begin_node.loc.expression) }
28
+ add_offense(begin_node) { |corrector| corrector.remove(begin_node) }
29
29
  end
30
30
  end
31
31
  end
@@ -83,7 +83,7 @@ module RuboCop
83
83
  ALTERNATIVE_PROTECTED
84
84
  end
85
85
  format(MSG, modifier: visibility,
86
- line: modifier.location.expression.line,
86
+ line: modifier.source_range.line,
87
87
  alternative: alternative)
88
88
  end
89
89
 
@@ -58,7 +58,7 @@ module RuboCop
58
58
  (node.str_type? && !node.loc.respond_to?(:begin)) || node.source_range.is?('__LINE__')
59
59
  end
60
60
 
61
- # rubocop:disable Metrics/MethodLength
61
+ # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
62
62
  def autocorrected_value(node)
63
63
  case node.type
64
64
  when :int
@@ -71,13 +71,15 @@ module RuboCop
71
71
  autocorrected_value_for_symbol(node)
72
72
  when :array
73
73
  autocorrected_value_for_array(node)
74
+ when :hash
75
+ autocorrected_value_for_hash(node)
74
76
  when :nil
75
77
  ''
76
78
  else
77
79
  node.source.gsub('"', '\"')
78
80
  end
79
81
  end
80
- # rubocop:enable Metrics/MethodLength
82
+ # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
81
83
 
82
84
  def autocorrected_value_for_string(node)
83
85
  if node.source.start_with?("'", '%q')
@@ -89,9 +91,18 @@ module RuboCop
89
91
 
90
92
  def autocorrected_value_for_symbol(node)
91
93
  end_pos =
92
- node.loc.end ? node.loc.end.begin_pos : node.loc.expression.end_pos
94
+ node.loc.end ? node.loc.end.begin_pos : node.source_range.end_pos
93
95
 
94
- range_between(node.loc.begin.end_pos, end_pos).source
96
+ range_between(node.loc.begin.end_pos, end_pos).source.gsub('"', '\"')
97
+ end
98
+
99
+ def autocorrected_value_in_hash_for_symbol(node)
100
+ # TODO: We need to detect symbol unacceptable names more correctly
101
+ if / |"|'/.match?(node.value.to_s)
102
+ ":\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\""
103
+ else
104
+ ":#{node.value}"
105
+ end
95
106
  end
96
107
 
97
108
  def autocorrected_value_for_array(node)
@@ -100,6 +111,37 @@ module RuboCop
100
111
  contents_range(node).source.split.to_s.gsub('"', '\"')
101
112
  end
102
113
 
114
+ def autocorrected_value_for_hash(node)
115
+ hash_string = node.children.map do |child|
116
+ key = autocorrected_value_in_hash(child.key)
117
+ value = autocorrected_value_in_hash(child.value)
118
+ "#{key}=>#{value}"
119
+ end.join(', ')
120
+
121
+ "{#{hash_string}}"
122
+ end
123
+
124
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
125
+ def autocorrected_value_in_hash(node)
126
+ case node.type
127
+ when :int
128
+ node.children.last.to_i.to_s
129
+ when :float
130
+ node.children.last.to_f.to_s
131
+ when :str
132
+ "\\\"#{node.value.to_s.gsub('"') { '\\\\\"' }}\\\""
133
+ when :sym
134
+ autocorrected_value_in_hash_for_symbol(node)
135
+ when :array
136
+ autocorrected_value_for_array(node)
137
+ when :hash
138
+ autocorrected_value_for_hash(node)
139
+ else
140
+ node.source.gsub('"', '\"')
141
+ end
142
+ end
143
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
144
+
103
145
  # Does node print its own source when converted to a string?
104
146
  def prints_as_self?(node)
105
147
  node.basic_literal? ||
@@ -28,6 +28,21 @@ module RuboCop
28
28
  # end
29
29
  #
30
30
  # # bad
31
+ # Employee = Class.new(Person) do
32
+ # def initialize(name, salary)
33
+ # @salary = salary
34
+ # end
35
+ # end
36
+ #
37
+ # # good
38
+ # Employee = Class.new(Person) do
39
+ # def initialize(name, salary)
40
+ # super(name)
41
+ # @salary = salary
42
+ # end
43
+ # end
44
+ #
45
+ # # bad
31
46
  # class Parent
32
47
  # def self.inherited(base)
33
48
  # do_something
@@ -55,6 +70,13 @@ module RuboCop
55
70
 
56
71
  CALLBACKS = (CLASS_LIFECYCLE_CALLBACKS + METHOD_LIFECYCLE_CALLBACKS).to_set.freeze
57
72
 
73
+ # @!method class_new_block(node)
74
+ def_node_matcher :class_new_block, <<~RUBY
75
+ ({block numblock}
76
+ (send
77
+ (const {nil? cbase} :Class) :new $_) ...)
78
+ RUBY
79
+
58
80
  def on_def(node)
59
81
  return unless offender?(node)
60
82
 
@@ -88,8 +110,15 @@ module RuboCop
88
110
  end
89
111
 
90
112
  def inside_class_with_stateful_parent?(node)
91
- class_node = node.each_ancestor(:class).first
92
- class_node&.parent_class && !stateless_class?(class_node.parent_class)
113
+ if (block_node = node.each_ancestor(:block, :numblock).first)
114
+ return false unless (super_class = class_new_block(block_node))
115
+
116
+ !stateless_class?(super_class)
117
+ elsif (class_node = node.each_ancestor(:class).first)
118
+ class_node.parent_class && !stateless_class?(class_node.parent_class)
119
+ else
120
+ false
121
+ end
93
122
  end
94
123
 
95
124
  def stateless_class?(node)
@@ -120,7 +120,7 @@ module RuboCop
120
120
 
121
121
  def scoping_method_call?(child)
122
122
  child.sclass_type? || eval_call?(child) || exec_call?(child) ||
123
- class_constructor?(child) || allowed_method_name?(child)
123
+ child.class_constructor? || allowed_method_name?(child)
124
124
  end
125
125
 
126
126
  def allowed_method_name?(node)
@@ -131,20 +131,12 @@ module RuboCop
131
131
 
132
132
  # @!method eval_call?(node)
133
133
  def_node_matcher :eval_call?, <<~PATTERN
134
- (block (send _ {:instance_eval :class_eval :module_eval} ...) ...)
134
+ ({block numblock} (send _ {:instance_eval :class_eval :module_eval} ...) ...)
135
135
  PATTERN
136
136
 
137
137
  # @!method exec_call?(node)
138
138
  def_node_matcher :exec_call?, <<~PATTERN
139
- (block (send _ {:instance_exec :class_exec :module_exec} ...) ...)
140
- PATTERN
141
-
142
- # @!method class_constructor?(node)
143
- def_node_matcher :class_constructor?, <<~PATTERN
144
- ({block numblock} {
145
- (send (const {nil? cbase} {:Class :Module :Struct}) :new ...)
146
- (send (const {nil? cbase} :Data) :define ...)
147
- } ...)
139
+ ({block numblock} (send _ {:instance_exec :class_exec :module_exec} ...) ...)
148
140
  PATTERN
149
141
  end
150
142
  end
@@ -31,6 +31,8 @@ module RuboCop
31
31
  return unless lhs&.casgn_type?
32
32
 
33
33
  add_offense(node.loc.operator) do |corrector|
34
+ next if node.each_ancestor(:def, :defs).any?
35
+
34
36
  corrector.replace(node.loc.operator, '=')
35
37
  end
36
38
  end
@@ -69,7 +69,7 @@ module RuboCop
69
69
  end
70
70
 
71
71
  def on_in_pattern(node)
72
- regexp_patterns = patterns(node).select(&:regexp_type?)
72
+ regexp_patterns = regexp_patterns(node)
73
73
 
74
74
  @valid_ref = regexp_patterns.map { |pattern| check_regexp(pattern) }.compact.max
75
75
  end
@@ -90,16 +90,12 @@ module RuboCop
90
90
 
91
91
  private
92
92
 
93
- def patterns(pattern_node)
94
- pattern = pattern_node.node_parts[0]
95
-
96
- case pattern.type
97
- when :array_pattern, :match_alt
98
- pattern.children
99
- when :match_as
100
- patterns(pattern)
101
- else
93
+ def regexp_patterns(in_node)
94
+ pattern = in_node.pattern
95
+ if pattern.regexp_type?
102
96
  [pattern]
97
+ else
98
+ pattern.each_descendant(:regexp).to_a
103
99
  end
104
100
  end
105
101
 
@@ -50,7 +50,7 @@ module RuboCop
50
50
 
51
51
  add_offense(node) do |corrector|
52
52
  node.each_value do |value|
53
- range = value.loc.expression
53
+ range = value.source_range
54
54
 
55
55
  match = range.source.match(TRAILING_QUOTE)
56
56
  corrector.remove_trailing(range, match[0].length) if match
@@ -41,7 +41,7 @@ module RuboCop
41
41
 
42
42
  def autocorrect(corrector, node)
43
43
  node.children.each do |child|
44
- range = child.loc.expression
44
+ range = child.source_range
45
45
 
46
46
  corrector.remove_trailing(range, 1) if range.source.end_with?(',')
47
47
  corrector.remove_leading(range, 1) if
@@ -231,7 +231,7 @@ module RuboCop
231
231
  cop_names = cops.sort.map { |c| describe(c) }.join(', ')
232
232
 
233
233
  add_offense(location, message: message(cop_names)) do |corrector|
234
- range = comment_range_with_surrounding_space(location, comment.loc.expression)
234
+ range = comment_range_with_surrounding_space(location, comment.source_range)
235
235
 
236
236
  if leave_free_comment?(comment, range)
237
237
  corrector.replace(range, ' # ')
@@ -263,8 +263,8 @@ module RuboCop
263
263
 
264
264
  def cop_range(comment, cop)
265
265
  cop = remove_department_marker(cop)
266
- matching_range(comment.loc.expression, cop) ||
267
- matching_range(comment.loc.expression, Badge.parse(cop).cop_name) ||
266
+ matching_range(comment.source_range, cop) ||
267
+ matching_range(comment.source_range, Badge.parse(cop).cop_name) ||
268
268
  raise("Couldn't find #{cop} in comment: #{comment.text}")
269
269
  end
270
270
 
@@ -281,15 +281,21 @@ module RuboCop
281
281
  .drop_while { |r| !r.equal?(range) }
282
282
  .each_cons(2)
283
283
  .map { |range1, range2| range1.end.join(range2.begin).source }
284
- .all? { |intervening| /\A\s*,\s*\Z/.match?(intervening) }
284
+ .all?(/\A\s*,\s*\z/)
285
285
  end
286
286
 
287
+ SIMILAR_COP_NAMES_CACHE = Hash.new do |hash, cop_name|
288
+ hash[:all_cop_names] = Registry.global.names unless hash.key?(:all_cop_names)
289
+ hash[cop_name] = NameSimilarity.find_similar_name(cop_name, hash[:all_cop_names])
290
+ end
291
+ private_constant :SIMILAR_COP_NAMES_CACHE
292
+
287
293
  def describe(cop)
288
294
  return 'all cops' if cop == 'all'
289
295
  return "`#{remove_department_marker(cop)}` department" if department_marker?(cop)
290
296
  return "`#{cop}`" if all_cop_names.include?(cop)
291
297
 
292
- similar = NameSimilarity.find_similar_name(cop, all_cop_names)
298
+ similar = SIMILAR_COP_NAMES_CACHE[cop]
293
299
  similar ? "`#{cop}` (did you mean `#{similar}`?)" : "`#{cop}` (unknown cop)"
294
300
  end
295
301