rubocop 1.69.2 → 1.71.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +36 -2
  5. data/lib/rubocop/cli/command/execute_runner.rb +3 -3
  6. data/lib/rubocop/cli/command/show_cops.rb +24 -2
  7. data/lib/rubocop/comment_config.rb +1 -1
  8. data/lib/rubocop/config.rb +13 -4
  9. data/lib/rubocop/config_loader.rb +4 -0
  10. data/lib/rubocop/config_loader_resolver.rb +14 -3
  11. data/lib/rubocop/config_validator.rb +18 -8
  12. data/lib/rubocop/cop/autocorrect_logic.rb +1 -1
  13. data/lib/rubocop/cop/base.rb +6 -0
  14. data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
  15. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  16. data/lib/rubocop/cop/internal_affairs/cop_enabled.rb +85 -0
  17. data/lib/rubocop/cop/internal_affairs/location_expression.rb +2 -1
  18. data/lib/rubocop/cop/internal_affairs/node_first_or_last_argument.rb +3 -2
  19. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
  20. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_processor.rb +63 -0
  21. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +131 -0
  22. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +229 -0
  23. data/lib/rubocop/cop/internal_affairs/node_type_multiple_predicates.rb +126 -0
  24. data/lib/rubocop/cop/internal_affairs/node_type_predicate.rb +4 -3
  25. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +90 -0
  26. data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +3 -1
  27. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  28. data/lib/rubocop/cop/internal_affairs.rb +4 -0
  29. data/lib/rubocop/cop/layout/access_modifier_indentation.rb +1 -1
  30. data/lib/rubocop/cop/layout/argument_alignment.rb +2 -8
  31. data/lib/rubocop/cop/layout/block_alignment.rb +1 -1
  32. data/lib/rubocop/cop/layout/class_structure.rb +9 -9
  33. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  34. data/lib/rubocop/cop/layout/else_alignment.rb +1 -1
  35. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +7 -11
  36. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  37. data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +1 -1
  38. data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
  39. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -1
  40. data/lib/rubocop/cop/layout/first_argument_indentation.rb +3 -8
  41. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -7
  42. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -7
  43. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +1 -1
  44. data/lib/rubocop/cop/layout/first_parameter_indentation.rb +2 -2
  45. data/lib/rubocop/cop/layout/hash_alignment.rb +6 -4
  46. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -0
  47. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  48. data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +2 -2
  49. data/lib/rubocop/cop/layout/line_length.rb +1 -0
  50. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +1 -1
  51. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +25 -0
  52. data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +1 -0
  53. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  54. data/lib/rubocop/cop/layout/redundant_line_break.rb +7 -6
  55. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
  56. data/lib/rubocop/cop/layout/single_line_block_chain.rb +1 -1
  57. data/lib/rubocop/cop/layout/space_after_colon.rb +2 -2
  58. data/lib/rubocop/cop/layout/space_after_comma.rb +1 -1
  59. data/lib/rubocop/cop/layout/space_after_method_name.rb +1 -1
  60. data/lib/rubocop/cop/layout/space_after_semicolon.rb +1 -1
  61. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -0
  62. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -3
  63. data/lib/rubocop/cop/layout/space_before_comma.rb +1 -1
  64. data/lib/rubocop/cop/layout/space_before_semicolon.rb +1 -1
  65. data/lib/rubocop/cop/layout/trailing_whitespace.rb +5 -3
  66. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  67. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +119 -0
  68. data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -3
  69. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +1 -1
  70. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +3 -3
  71. data/lib/rubocop/cop/lint/constant_reassignment.rb +148 -0
  72. data/lib/rubocop/cop/lint/debugger.rb +1 -1
  73. data/lib/rubocop/cop/lint/duplicate_match_pattern.rb +1 -1
  74. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +1 -1
  75. data/lib/rubocop/cop/lint/duplicate_set_element.rb +20 -7
  76. data/lib/rubocop/cop/lint/empty_expression.rb +0 -2
  77. data/lib/rubocop/cop/lint/float_comparison.rb +5 -2
  78. data/lib/rubocop/cop/lint/float_out_of_range.rb +1 -1
  79. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  80. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -1
  81. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +24 -6
  82. data/lib/rubocop/cop/lint/missing_super.rb +2 -2
  83. data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
  84. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -1
  85. data/lib/rubocop/cop/lint/nested_method_definition.rb +8 -4
  86. data/lib/rubocop/cop/lint/next_without_accumulator.rb +1 -1
  87. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +4 -3
  88. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +1 -1
  89. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +18 -31
  90. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +2 -1
  91. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -5
  92. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  93. data/lib/rubocop/cop/lint/redundant_string_coercion.rb +2 -2
  94. data/lib/rubocop/cop/lint/rescue_exception.rb +1 -1
  95. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +8 -1
  96. data/lib/rubocop/cop/lint/shared_mutable_default.rb +65 -0
  97. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  98. data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
  99. data/lib/rubocop/cop/lint/syntax.rb +4 -1
  100. data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +1 -4
  101. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +1 -1
  102. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -1
  103. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -1
  104. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  105. data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -4
  106. data/lib/rubocop/cop/lint/useless_assignment.rb +1 -1
  107. data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
  108. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +2 -1
  109. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -2
  110. data/lib/rubocop/cop/lint/void.rb +5 -9
  111. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  112. data/lib/rubocop/cop/metrics/collection_literal_length.rb +7 -0
  113. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  114. data/lib/rubocop/cop/metrics/method_length.rb +8 -1
  115. data/lib/rubocop/cop/metrics/module_length.rb +1 -1
  116. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  117. data/lib/rubocop/cop/mixin/check_line_breakable.rb +11 -11
  118. data/lib/rubocop/cop/mixin/comments_help.rb +3 -1
  119. data/lib/rubocop/cop/mixin/dig_help.rb +1 -1
  120. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  121. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +4 -4
  122. data/lib/rubocop/cop/mixin/hash_subset.rb +188 -0
  123. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -1
  124. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +48 -24
  125. data/lib/rubocop/cop/mixin/space_before_punctuation.rb +1 -1
  126. data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -3
  127. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  128. data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
  129. data/lib/rubocop/cop/mixin/trailing_comma.rb +3 -3
  130. data/lib/rubocop/cop/naming/block_forwarding.rb +19 -15
  131. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +3 -3
  132. data/lib/rubocop/cop/security/compound_hash.rb +1 -0
  133. data/lib/rubocop/cop/style/access_modifier_declarations.rb +34 -5
  134. data/lib/rubocop/cop/style/and_or.rb +1 -1
  135. data/lib/rubocop/cop/style/arguments_forwarding.rb +39 -23
  136. data/lib/rubocop/cop/style/array_first_last.rb +18 -2
  137. data/lib/rubocop/cop/style/block_delimiters.rb +7 -20
  138. data/lib/rubocop/cop/style/class_and_module_children.rb +6 -3
  139. data/lib/rubocop/cop/style/collection_methods.rb +1 -1
  140. data/lib/rubocop/cop/style/combinable_defined.rb +1 -1
  141. data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
  142. data/lib/rubocop/cop/style/concat_array_literals.rb +1 -1
  143. data/lib/rubocop/cop/style/conditional_assignment.rb +6 -4
  144. data/lib/rubocop/cop/style/documentation.rb +1 -1
  145. data/lib/rubocop/cop/style/double_negation.rb +3 -3
  146. data/lib/rubocop/cop/style/each_for_simple_loop.rb +4 -7
  147. data/lib/rubocop/cop/style/each_with_object.rb +2 -3
  148. data/lib/rubocop/cop/style/empty_else.rb +4 -2
  149. data/lib/rubocop/cop/style/empty_literal.rb +1 -1
  150. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  151. data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
  152. data/lib/rubocop/cop/style/exact_regexp_match.rb +3 -10
  153. data/lib/rubocop/cop/style/explicit_block_argument.rb +15 -2
  154. data/lib/rubocop/cop/style/exponential_notation.rb +1 -1
  155. data/lib/rubocop/cop/style/fetch_env_var.rb +1 -1
  156. data/lib/rubocop/cop/style/float_division.rb +8 -4
  157. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +1 -1
  158. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -6
  159. data/lib/rubocop/cop/style/hash_except.rb +24 -148
  160. data/lib/rubocop/cop/style/hash_slice.rb +80 -0
  161. data/lib/rubocop/cop/style/hash_syntax.rb +6 -3
  162. data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
  163. data/lib/rubocop/cop/style/if_unless_modifier.rb +3 -3
  164. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
  165. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -2
  166. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  167. data/lib/rubocop/cop/style/inverse_methods.rb +6 -6
  168. data/lib/rubocop/cop/style/it_assignment.rb +36 -0
  169. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  170. data/lib/rubocop/cop/style/map_into_array.rb +1 -1
  171. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  172. data/lib/rubocop/cop/style/map_to_set.rb +3 -2
  173. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +19 -12
  174. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +2 -0
  175. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -1
  176. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -4
  177. data/lib/rubocop/cop/style/method_def_parentheses.rb +1 -1
  178. data/lib/rubocop/cop/style/missing_else.rb +2 -0
  179. data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
  180. data/lib/rubocop/cop/style/multiple_comparison.rb +26 -20
  181. data/lib/rubocop/cop/style/mutable_constant.rb +3 -3
  182. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -1
  183. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
  184. data/lib/rubocop/cop/style/object_then.rb +13 -15
  185. data/lib/rubocop/cop/style/open_struct_use.rb +5 -5
  186. data/lib/rubocop/cop/style/parallel_assignment.rb +1 -5
  187. data/lib/rubocop/cop/style/parentheses_around_condition.rb +2 -2
  188. data/lib/rubocop/cop/style/percent_literal_delimiters.rb +1 -1
  189. data/lib/rubocop/cop/style/proc.rb +1 -2
  190. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  191. data/lib/rubocop/cop/style/raise_args.rb +6 -4
  192. data/lib/rubocop/cop/style/random_with_offset.rb +3 -3
  193. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  194. data/lib/rubocop/cop/style/redundant_condition.rb +2 -2
  195. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +2 -1
  196. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -10
  197. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  198. data/lib/rubocop/cop/style/redundant_exception.rb +2 -2
  199. data/lib/rubocop/cop/style/redundant_freeze.rb +2 -2
  200. data/lib/rubocop/cop/style/redundant_initialize.rb +12 -3
  201. data/lib/rubocop/cop/style/redundant_line_continuation.rb +34 -13
  202. data/lib/rubocop/cop/style/redundant_parentheses.rb +10 -10
  203. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +3 -0
  204. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +1 -1
  205. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
  206. data/lib/rubocop/cop/style/redundant_self_assignment.rb +14 -28
  207. data/lib/rubocop/cop/style/redundant_sort.rb +2 -2
  208. data/lib/rubocop/cop/style/redundant_string_escape.rb +2 -2
  209. data/lib/rubocop/cop/style/return_nil.rb +1 -1
  210. data/lib/rubocop/cop/style/safe_navigation.rb +2 -2
  211. data/lib/rubocop/cop/style/semicolon.rb +1 -1
  212. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +2 -1
  213. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  214. data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
  215. data/lib/rubocop/cop/style/single_line_methods.rb +3 -4
  216. data/lib/rubocop/cop/style/slicing_with_range.rb +40 -11
  217. data/lib/rubocop/cop/style/sole_nested_conditional.rb +2 -2
  218. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  219. data/lib/rubocop/cop/style/string_literals.rb +1 -1
  220. data/lib/rubocop/cop/style/string_methods.rb +1 -1
  221. data/lib/rubocop/cop/style/super_arguments.rb +65 -17
  222. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  223. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
  224. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -1
  225. data/lib/rubocop/cop/style/while_until_modifier.rb +0 -1
  226. data/lib/rubocop/cop/style/yoda_condition.rb +8 -4
  227. data/lib/rubocop/cop/style/yoda_expression.rb +2 -1
  228. data/lib/rubocop/cop/util.rb +11 -4
  229. data/lib/rubocop/cop/variable_force/variable.rb +14 -2
  230. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -3
  231. data/lib/rubocop/cops_documentation_generator.rb +13 -13
  232. data/lib/rubocop/directive_comment.rb +9 -8
  233. data/lib/rubocop/formatter/formatter_set.rb +1 -1
  234. data/lib/rubocop/lsp/diagnostic.rb +189 -0
  235. data/lib/rubocop/lsp/logger.rb +2 -2
  236. data/lib/rubocop/lsp/routes.rb +7 -23
  237. data/lib/rubocop/lsp/runtime.rb +15 -49
  238. data/lib/rubocop/lsp/stdin_runner.rb +83 -0
  239. data/lib/rubocop/options.rb +2 -1
  240. data/lib/rubocop/path_util.rb +11 -8
  241. data/lib/rubocop/result_cache.rb +13 -13
  242. data/lib/rubocop/rspec/expect_offense.rb +6 -2
  243. data/lib/rubocop/rspec/shared_contexts.rb +4 -1
  244. data/lib/rubocop/rspec/support.rb +1 -2
  245. data/lib/rubocop/runner.rb +5 -6
  246. data/lib/rubocop/target_finder.rb +1 -0
  247. data/lib/rubocop/target_ruby.rb +15 -0
  248. data/lib/rubocop/version.rb +1 -1
  249. data/lib/rubocop.rb +6 -0
  250. data/lib/ruby_lsp/rubocop/addon.rb +78 -0
  251. data/lib/ruby_lsp/rubocop/wraps_built_in_lsp_runtime.rb +50 -0
  252. metadata +23 -11
  253. data/lib/rubocop/rspec/host_environment_simulation_helper.rb +0 -28
@@ -48,7 +48,7 @@ module RuboCop
48
48
 
49
49
  def on_new_investigation
50
50
  processed_source.lines.each_with_index do |line, index|
51
- next unless line.end_with?(' ', "\t")
51
+ next unless line.match?(/[[:blank:]]\z/)
52
52
 
53
53
  process_line(line, index + 1)
54
54
  end
@@ -84,7 +84,7 @@ module RuboCop
84
84
  end
85
85
 
86
86
  def whitespace_is_indentation?(range, level)
87
- range.source[/[ \t]+/].length <= level
87
+ range.source[/[[:blank:]]+/].length <= level
88
88
  end
89
89
 
90
90
  def whitespace_only?(range)
@@ -123,7 +123,9 @@ module RuboCop
123
123
  end
124
124
 
125
125
  def offense_range(lineno, line)
126
- source_range(processed_source.buffer, lineno, (line.rstrip.length)...(line.length))
126
+ source_range(
127
+ processed_source.buffer, lineno, (line.sub(/[[:blank:]]+\z/, '').length)...(line.length)
128
+ )
127
129
  end
128
130
  end
129
131
  end
@@ -77,7 +77,7 @@ module RuboCop
77
77
  private
78
78
 
79
79
  def ambiguous_block_association?(send_node)
80
- send_node.last_argument.block_type? && !send_node.last_argument.send_node.arguments?
80
+ send_node.last_argument.any_block_type? && !send_node.last_argument.send_node.arguments?
81
81
  end
82
82
 
83
83
  def allowed_method_pattern?(node)
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for an array literal interpolated inside a regexp.
7
+ #
8
+ # When interpolating an array literal, it is converted to a string. This means
9
+ # that when inside a regexp, it acts as a character class but with additional
10
+ # quotes, spaces and commas that are likely not intended. For example,
11
+ # `/#{%w[a b c]}/` parses as `/["a", "b", "c"]/` (or `/["a, bc]/` without
12
+ # repeated characters).
13
+ #
14
+ # The cop can autocorrect to a character class (if all items in the array are a
15
+ # single character) or alternation (if the array contains longer items).
16
+ #
17
+ # NOTE: This only considers interpolated arrays that contain only strings, symbols,
18
+ # integers, and floats. Any other type is not easily convertible to a character class
19
+ # or regexp alternation.
20
+ #
21
+ # @safety
22
+ # Autocorrection is unsafe because it will change the regexp pattern, by
23
+ # removing the additional quotes, spaces and commas from the character class.
24
+ #
25
+ # @example
26
+ # # bad
27
+ # /#{%w[a b c]}/
28
+ #
29
+ # # good
30
+ # /[abc]/
31
+ #
32
+ # # bad
33
+ # /#{%w[foo bar baz]}/
34
+ #
35
+ # # good
36
+ # /(?:foo|bar|baz)/
37
+ #
38
+ # # bad - construct a regexp rather than interpolate an array of identifiers
39
+ # /#{[foo, bar]}/
40
+ #
41
+ class ArrayLiteralInRegexp < Base
42
+ include Interpolation
43
+ extend AutoCorrector
44
+
45
+ LITERAL_TYPES = %i[str sym int float true false nil].freeze
46
+ private_constant :LITERAL_TYPES
47
+
48
+ MSG_CHARACTER_CLASS = 'Use a character class instead of interpolating an array in a regexp.'
49
+ MSG_ALTERNATION = 'Use alternation instead of interpolating an array in a regexp.'
50
+ MSG_UNKNOWN = 'Use alternation or a character class instead of interpolating an array ' \
51
+ 'in a regexp.'
52
+
53
+ def on_interpolation(begin_node)
54
+ final_node = begin_node.children.last
55
+
56
+ return unless begin_node.parent.regexp_type?
57
+ return unless final_node.array_type?
58
+
59
+ if array_of_literal_values?(final_node)
60
+ register_array_of_literal_values(begin_node, final_node)
61
+ else
62
+ register_array_of_nonliteral_values(begin_node)
63
+ end
64
+ end
65
+
66
+ private
67
+
68
+ def array_of_literal_values?(node)
69
+ node.each_value.all? { |value| value.type?(*LITERAL_TYPES) }
70
+ end
71
+
72
+ def register_array_of_literal_values(begin_node, node)
73
+ array_values = array_values(node)
74
+
75
+ if character_class?(array_values)
76
+ message = MSG_CHARACTER_CLASS
77
+ replacement = character_class_for(array_values)
78
+ else
79
+ message = MSG_ALTERNATION
80
+ replacement = alternation_for(array_values)
81
+ end
82
+
83
+ add_offense(begin_node, message: message) do |corrector|
84
+ corrector.replace(begin_node, replacement)
85
+ end
86
+ end
87
+
88
+ def register_array_of_nonliteral_values(node)
89
+ # Add offense but do not correct if the array contains any nonliteral values.
90
+ add_offense(node, message: MSG_UNKNOWN)
91
+ end
92
+
93
+ def array_values(node)
94
+ node.each_value.map do |value|
95
+ value.respond_to?(:value) ? value.value : value.source
96
+ end
97
+ end
98
+
99
+ def character_class?(values)
100
+ values.all? { |v| v.to_s.length == 1 }
101
+ end
102
+
103
+ def character_class_for(values)
104
+ "[#{escape_values(values).join}]"
105
+ end
106
+
107
+ def alternation_for(values)
108
+ "(?:#{escape_values(values).join('|')})"
109
+ end
110
+
111
+ def escape_values(values)
112
+ # This may add extraneous escape characters, but they can be cleaned up
113
+ # by `Style/RedundantRegexpEscape`.
114
+ values.map { |value| Regexp.escape(value.to_s) }
115
+ end
116
+ end
117
+ end
118
+ end
119
+ end
@@ -53,8 +53,6 @@ module RuboCop
53
53
  ASGN_TYPES = [:begin, *AST::Node::EQUALS_ASSIGNMENTS, :send, :csend].freeze
54
54
 
55
55
  def on_if(node)
56
- return if node.condition.block_type?
57
-
58
56
  traverse_node(node.condition) do |asgn_node|
59
57
  next :skip_children if skip_children?(asgn_node)
60
58
  next if allowed_construct?(asgn_node)
@@ -95,7 +93,7 @@ module RuboCop
95
93
 
96
94
  def traverse_node(node, &block)
97
95
  # if the node is a block, any assignments are irrelevant
98
- return if node.block_type?
96
+ return if node.any_block_type?
99
97
 
100
98
  result = yield node if ASGN_TYPES.include?(node.type)
101
99
 
@@ -14,7 +14,7 @@ module RuboCop
14
14
  # Although these can be rewritten in a different way, it should not be necessary to
15
15
  # do so. Operations such as `-` or `/` where the result will always be the same
16
16
  # (`x - x` will always be 0; `x / x` will always be 1) are offenses, but these
17
- # are covered by Lint/NumericOperationWithConstantResult instead.
17
+ # are covered by `Lint/NumericOperationWithConstantResult` instead.
18
18
  #
19
19
  # @safety
20
20
  # This cop is unsafe as it does not consider side effects when calling methods
@@ -68,12 +68,12 @@ module RuboCop
68
68
 
69
69
  # @!method constant_assigned_in_block?(node)
70
70
  def_node_matcher :constant_assigned_in_block?, <<~PATTERN
71
- ({^block_type? [^begin_type? ^^block_type?]} nil? ...)
71
+ ({^any_block [^begin ^^any_block]} nil? ...)
72
72
  PATTERN
73
73
 
74
74
  # @!method module_defined_in_block?(node)
75
75
  def_node_matcher :module_defined_in_block?, <<~PATTERN
76
- ({^block_type? [^begin_type? ^^block_type?]} ...)
76
+ ({^any_block [^begin ^^any_block]} ...)
77
77
  PATTERN
78
78
 
79
79
  def on_casgn(node)
@@ -92,7 +92,7 @@ module RuboCop
92
92
  private
93
93
 
94
94
  def method_name(node)
95
- node.ancestors.find(&:block_type?).method_name
95
+ node.ancestors.find(&:any_block_type?).method_name
96
96
  end
97
97
  end
98
98
  end
@@ -0,0 +1,148 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Lint
6
+ # Checks for constant reassignments.
7
+ #
8
+ # Emulates Ruby's runtime warning "already initialized constant X"
9
+ # when a constant is reassigned in the same file and namespace using the
10
+ # `NAME = value` syntax.
11
+ #
12
+ # The cop cannot catch all offenses, like, for example, when a constant
13
+ # is reassigned in another file, or when using metaprogramming (`Module#const_set`).
14
+ #
15
+ # The cop only takes into account constants assigned in a "simple" way: directly
16
+ # inside class/module definition, or within another constant. Other type of assignments
17
+ # (e.g., inside a conditional) are disregarded.
18
+ #
19
+ # The cop also tracks constant removal using `Module#remove_const` with symbol
20
+ # or string argument.
21
+ #
22
+ # @example
23
+ # # bad
24
+ # X = :foo
25
+ # X = :bar
26
+ #
27
+ # # bad
28
+ # class A
29
+ # X = :foo
30
+ # X = :bar
31
+ # end
32
+ #
33
+ # # bad
34
+ # module A
35
+ # X = :foo
36
+ # X = :bar
37
+ # end
38
+ #
39
+ # # good - keep only one assignment
40
+ # X = :bar
41
+ #
42
+ # class A
43
+ # X = :bar
44
+ # end
45
+ #
46
+ # module A
47
+ # X = :bar
48
+ # end
49
+ #
50
+ # # good - use OR assignment
51
+ # X = :foo
52
+ # X ||= :bar
53
+ #
54
+ # # good - use conditional assignment
55
+ # X = :foo
56
+ # X = :bar unless defined?(X)
57
+ #
58
+ # # good - remove the assigned constant first
59
+ # class A
60
+ # X = :foo
61
+ # remove_const :X
62
+ # X = :bar
63
+ # end
64
+ #
65
+ class ConstantReassignment < Base
66
+ MSG = 'Constant `%<constant>s` is already assigned in this namespace.'
67
+
68
+ RESTRICT_ON_SEND = %i[remove_const].freeze
69
+
70
+ # @!method remove_constant(node)
71
+ def_node_matcher :remove_constant, <<~PATTERN
72
+ (send _ :remove_const
73
+ ({sym str} $_))
74
+ PATTERN
75
+
76
+ def on_casgn(node)
77
+ return unless fixed_constant_path?(node)
78
+ return unless simple_assignment?(node)
79
+ return if constant_names.add?(fully_qualified_constant_name(node))
80
+
81
+ add_offense(node, message: format(MSG, constant: node.name))
82
+ end
83
+
84
+ def on_send(node)
85
+ constant = remove_constant(node)
86
+
87
+ return unless constant
88
+
89
+ namespaces = ancestor_namespaces(node)
90
+
91
+ return if namespaces.none?
92
+
93
+ constant_names.delete(fully_qualified_name_for(namespaces, constant))
94
+ end
95
+
96
+ private
97
+
98
+ def fixed_constant_path?(node)
99
+ node.each_path.all? { |path| path.type?(:cbase, :const, :self) }
100
+ end
101
+
102
+ def simple_assignment?(node)
103
+ node.ancestors.all? do |ancestor|
104
+ return true if ancestor.type?(:module, :class)
105
+
106
+ ancestor.begin_type? || ancestor.literal? || ancestor.casgn_type? ||
107
+ freeze_method?(ancestor)
108
+ end
109
+ end
110
+
111
+ def freeze_method?(node)
112
+ node.send_type? && node.method?(:freeze)
113
+ end
114
+
115
+ def fully_qualified_constant_name(node)
116
+ if node.absolute?
117
+ namespace = node.namespace.const_type? ? node.namespace.source : nil
118
+
119
+ "#{namespace}::#{node.name}"
120
+ else
121
+ constant_namespaces = ancestor_namespaces(node) + constant_namespaces(node)
122
+
123
+ fully_qualified_name_for(constant_namespaces, node.name)
124
+ end
125
+ end
126
+
127
+ def fully_qualified_name_for(namespaces, constant)
128
+ ['', *namespaces, constant].join('::')
129
+ end
130
+
131
+ def constant_namespaces(node)
132
+ node.each_path.select(&:const_type?).map(&:short_name)
133
+ end
134
+
135
+ def ancestor_namespaces(node)
136
+ node
137
+ .each_ancestor(:class, :module)
138
+ .map { |ancestor| ancestor.identifier.short_name }
139
+ .reverse
140
+ end
141
+
142
+ def constant_names
143
+ @constant_names ||= Set.new
144
+ end
145
+ end
146
+ end
147
+ end
148
+ end
@@ -116,7 +116,7 @@ module RuboCop
116
116
 
117
117
  def assumed_usage_context?(node)
118
118
  # Basically, debugger methods are not used as a method argument without arguments.
119
- return false unless node.arguments.empty? && node.each_ancestor(:send, :csend).any?
119
+ return false unless node.arguments.empty? && node.each_ancestor(:call).any?
120
120
  return true if assumed_argument?(node)
121
121
 
122
122
  node.each_ancestor.none? do |ancestor|
@@ -106,7 +106,7 @@ module RuboCop
106
106
  private
107
107
 
108
108
  def pattern_identity(pattern)
109
- pattern_source = if pattern.hash_pattern_type? || pattern.match_alt_type?
109
+ pattern_source = if pattern.type?(:hash_pattern, :match_alt)
110
110
  pattern.children.map(&:source).sort.to_s
111
111
  else
112
112
  pattern.source
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for duplicate elements in Regexp character classes.
6
+ # Checks for duplicate elements in `Regexp` character classes.
7
7
  #
8
8
  # @example
9
9
  #
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for duplicate literal, constant, or variable elements in Set.
6
+ # Checks for duplicate literal, constant, or variable elements in `Set` and `SortedSet`.
7
7
  #
8
8
  # @example
9
9
  #
@@ -25,17 +25,28 @@ module RuboCop
25
25
  # # good
26
26
  # [:foo, :bar].to_set
27
27
  #
28
+ # # bad
29
+ # SortedSet[:foo, :bar, :foo]
30
+ #
31
+ # # good
32
+ # SortedSet[:foo, :bar]
33
+ #
34
+ # # bad
35
+ # SortedSet.new([:foo, :bar, :foo])
36
+ #
37
+ # # good
38
+ # SortedSet.new([:foo, :bar])
28
39
  class DuplicateSetElement < Base
29
40
  extend AutoCorrector
30
41
 
31
- MSG = 'Remove the duplicate element in Set.'
42
+ MSG = 'Remove the duplicate element in %<class_name>s.'
32
43
  RESTRICT_ON_SEND = %i[\[\] new to_set].freeze
33
44
 
34
45
  # @!method set_init_elements(node)
35
46
  def_node_matcher :set_init_elements, <<~PATTERN
36
47
  {
37
- (send (const {nil? cbase} :Set) :[] $...)
38
- (send (const {nil? cbase} :Set) :new (array $...))
48
+ (send (const {nil? cbase} {:Set :SortedSet}) :[] $...)
49
+ (send (const {nil? cbase} {:Set :SortedSet}) :new (array $...))
39
50
  (call (array $...) :to_set)
40
51
  }
41
52
  PATTERN
@@ -51,7 +62,7 @@ module RuboCop
51
62
  next if !set_element.literal? && !set_element.const_type? && !set_element.variable?
52
63
 
53
64
  if seen_elements.include?(set_element)
54
- register_offense(set_element, set_elements[index - 1])
65
+ register_offense(set_element, set_elements[index - 1], node)
55
66
  else
56
67
  seen_elements << set_element
57
68
  end
@@ -61,8 +72,10 @@ module RuboCop
61
72
 
62
73
  private
63
74
 
64
- def register_offense(current_element, prev_element)
65
- add_offense(current_element) do |corrector|
75
+ def register_offense(current_element, prev_element, node)
76
+ class_name = node.receiver.const_type? ? node.receiver.const_name : 'Set'
77
+
78
+ add_offense(current_element, message: format(MSG, class_name: class_name)) do |corrector|
66
79
  range = prev_element.source_range.end.join(current_element.source_range.end)
67
80
 
68
81
  corrector.remove(range)
@@ -14,8 +14,6 @@ module RuboCop
14
14
  # bar
15
15
  # end
16
16
  #
17
- # @example
18
- #
19
17
  # # good
20
18
  #
21
19
  # foo = (some_expression)
@@ -36,7 +36,8 @@ module RuboCop
36
36
  # # https://www.embeddeduse.com/2019/08/26/qt-compare-two-floats/
37
37
  #
38
38
  class FloatComparison < Base
39
- MSG = 'Avoid (in)equality comparisons of floats as they are unreliable.'
39
+ MSG_EQUALITY = 'Avoid equality comparisons of floats as they are unreliable.'
40
+ MSG_INEQUALITY = 'Avoid inequality comparisons of floats as they are unreliable.'
40
41
 
41
42
  EQUALITY_METHODS = %i[== != eql? equal?].freeze
42
43
  FLOAT_RETURNING_METHODS = %i[to_f Float fdiv].freeze
@@ -52,8 +53,10 @@ module RuboCop
52
53
 
53
54
  return if literal_safe?(lhs) || literal_safe?(rhs)
54
55
 
55
- add_offense(node) if float?(lhs) || float?(rhs)
56
+ message = node.method?(:!=) ? MSG_INEQUALITY : MSG_EQUALITY
57
+ add_offense(node, message: message) if float?(lhs) || float?(rhs)
56
58
  end
59
+ alias on_csend on_send
57
60
 
58
61
  private
59
62
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Identifies Float literals which are, like, really really really
6
+ # Identifies `Float` literals which are, like, really really really
7
7
  # really really really really really big. Too big. No-one needs Floats
8
8
  # that big. If you need a float that big, something is wrong with you.
9
9
  #
@@ -73,7 +73,7 @@ module RuboCop
73
73
 
74
74
  first_arg = node.first_argument
75
75
  return false if num_of_expected_fields.zero? &&
76
- (first_arg.dstr_type? || first_arg.array_type?)
76
+ first_arg.type?(:dstr, :array)
77
77
 
78
78
  matched_arguments_count?(num_of_expected_fields, num_of_format_args)
79
79
  end
@@ -82,7 +82,7 @@ module RuboCop
82
82
  end
83
83
 
84
84
  def string_literal?(node)
85
- node.str_type? || node.dstr_type?
85
+ node.type?(:str, :dstr)
86
86
  end
87
87
 
88
88
  def string_literals?(node1, node2)
@@ -5,6 +5,9 @@ module RuboCop
5
5
  module Lint
6
6
  # Checks for interpolated literals.
7
7
  #
8
+ # NOTE: Array literals interpolated in regexps are not handled by this cop, but
9
+ # by `Lint/ArrayLiteralInRegexp` instead.
10
+ #
8
11
  # @example
9
12
  #
10
13
  # # bad
@@ -21,6 +24,7 @@ module RuboCop
21
24
  MSG = 'Literal interpolation detected.'
22
25
  COMPOSITE = %i[array hash pair irange erange].freeze
23
26
 
27
+ # rubocop:disable Metrics/AbcSize
24
28
  def on_interpolation(begin_node)
25
29
  final_node = begin_node.children.last
26
30
  return unless offending?(final_node)
@@ -35,11 +39,18 @@ module RuboCop
35
39
  return if in_array_percent_literal?(begin_node) && /\s|\A\z/.match?(expanded_value)
36
40
 
37
41
  add_offense(final_node) do |corrector|
38
- return if final_node.dstr_type? # nested, fixed in next iteration
42
+ next if final_node.dstr_type? # nested, fixed in next iteration
43
+
44
+ replacement = if final_node.str_type? && !final_node.value.valid_encoding?
45
+ final_node.source.delete_prefix('"').delete_suffix('"')
46
+ else
47
+ expanded_value
48
+ end
39
49
 
40
- corrector.replace(final_node.parent, expanded_value)
50
+ corrector.replace(final_node.parent, replacement)
41
51
  end
42
52
  end
53
+ # rubocop:enable Metrics/AbcSize
43
54
 
44
55
  private
45
56
 
@@ -47,8 +58,10 @@ module RuboCop
47
58
  node &&
48
59
  !special_keyword?(node) &&
49
60
  prints_as_self?(node) &&
50
- # Special case for Layout/TrailingWhitespace
51
- !(space_literal?(node) && ends_heredoc_line?(node))
61
+ # Special case for `Layout/TrailingWhitespace`
62
+ !(space_literal?(node) && ends_heredoc_line?(node)) &&
63
+ # Handled by `Lint/ArrayLiteralInRegexp`
64
+ !array_in_regexp?(node)
52
65
  end
53
66
 
54
67
  def special_keyword?(node)
@@ -56,6 +69,11 @@ module RuboCop
56
69
  (node.str_type? && !node.loc.respond_to?(:begin)) || node.source_range.is?('__LINE__')
57
70
  end
58
71
 
72
+ def array_in_regexp?(node)
73
+ grandparent = node.parent.parent
74
+ node.array_type? && grandparent.regexp_type?
75
+ end
76
+
59
77
  # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
60
78
  def autocorrected_value(node)
61
79
  case node.type
@@ -168,7 +186,7 @@ module RuboCop
168
186
  end
169
187
 
170
188
  def space_literal?(node)
171
- node.str_type? && node.value.blank?
189
+ node.str_type? && node.value.valid_encoding? && node.value.blank?
172
190
  end
173
191
 
174
192
  def ends_heredoc_line?(node)
@@ -181,7 +199,7 @@ module RuboCop
181
199
 
182
200
  def in_array_percent_literal?(node)
183
201
  parent = node.parent
184
- return false unless parent.dstr_type? || parent.dsym_type?
202
+ return false unless parent.type?(:dstr, :dsym)
185
203
 
186
204
  grandparent = parent.parent
187
205
  grandparent&.array_type? && grandparent.percent_literal?
@@ -97,7 +97,7 @@ module RuboCop
97
97
 
98
98
  # @!method class_new_block(node)
99
99
  def_node_matcher :class_new_block, <<~RUBY
100
- ({block numblock}
100
+ (any_block
101
101
  (send
102
102
  (const {nil? cbase} :Class) :new $_) ...)
103
103
  RUBY
@@ -135,7 +135,7 @@ module RuboCop
135
135
  end
136
136
 
137
137
  def inside_class_with_stateful_parent?(node)
138
- if (block_node = node.each_ancestor(:block, :numblock).first)
138
+ if (block_node = node.each_ancestor(:any_block).first)
139
139
  return false unless (super_class = class_new_block(block_node))
140
140
 
141
141
  !allowed_class?(super_class)
@@ -8,7 +8,7 @@ module RuboCop
8
8
  # Offenses are registered for regexp character classes like `/[A-z]/`
9
9
  # as well as range objects like `('A'..'z')`.
10
10
  #
11
- # NOTE: Range objects cannot be autocorrected.
11
+ # NOTE: `Range` objects cannot be autocorrected.
12
12
  #
13
13
  # @safety
14
14
  # The cop autocorrects regexp character classes
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Do not mix named captures and numbered captures in a Regexp literal
6
+ # Do not mix named captures and numbered captures in a `Regexp` literal
7
7
  # because numbered capture is ignored if they're mixed.
8
8
  # Replace numbered captures with non-capturing groupings or
9
9
  # named captures.
@@ -96,13 +96,13 @@ module RuboCop
96
96
 
97
97
  def on_def(node)
98
98
  subject, = *node # rubocop:disable InternalAffairs/NodeDestructuring
99
- return if node.defs_type? && subject.variable?
99
+ return if node.defs_type? && allowed_subject_type?(subject)
100
100
 
101
101
  def_ancestor = node.each_ancestor(:def, :defs).first
102
102
  return unless def_ancestor
103
103
 
104
104
  within_scoping_def =
105
- node.each_ancestor(:block, :numblock, :sclass).any? do |ancestor|
105
+ node.each_ancestor(:any_block, :sclass).any? do |ancestor|
106
106
  scoping_method_call?(ancestor)
107
107
  end
108
108
 
@@ -117,6 +117,10 @@ module RuboCop
117
117
  child.class_constructor? || allowed_method_name?(child)
118
118
  end
119
119
 
120
+ def allowed_subject_type?(subject)
121
+ subject.variable? || subject.const_type? || subject.call_type?
122
+ end
123
+
120
124
  def allowed_method_name?(node)
121
125
  name = node.method_name
122
126
 
@@ -125,12 +129,12 @@ module RuboCop
125
129
 
126
130
  # @!method eval_call?(node)
127
131
  def_node_matcher :eval_call?, <<~PATTERN
128
- ({block numblock} (send _ {:instance_eval :class_eval :module_eval} ...) ...)
132
+ (any_block (send _ {:instance_eval :class_eval :module_eval} ...) ...)
129
133
  PATTERN
130
134
 
131
135
  # @!method exec_call?(node)
132
136
  def_node_matcher :exec_call?, <<~PATTERN
133
- ({block numblock} (send _ {:instance_exec :class_exec :module_exec} ...) ...)
137
+ (any_block (send _ {:instance_exec :class_exec :module_exec} ...) ...)
134
138
  PATTERN
135
139
  end
136
140
  end