rubocop 1.71.2 → 1.75.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 (205) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/config/default.yml +118 -21
  4. data/config/internal_affairs.yml +20 -0
  5. data/config/obsoletion.yml +3 -1
  6. data/lib/rubocop/cli/command/suggest_extensions.rb +7 -1
  7. data/lib/rubocop/cli.rb +1 -1
  8. data/lib/rubocop/comment_config.rb +1 -1
  9. data/lib/rubocop/config.rb +39 -6
  10. data/lib/rubocop/config_loader.rb +48 -9
  11. data/lib/rubocop/config_loader_resolver.rb +24 -9
  12. data/lib/rubocop/config_obsoletion/extracted_cop.rb +4 -3
  13. data/lib/rubocop/config_obsoletion/renamed_cop.rb +18 -3
  14. data/lib/rubocop/config_obsoletion.rb +46 -2
  15. data/lib/rubocop/config_validator.rb +2 -1
  16. data/lib/rubocop/cop/internal_affairs/example_description.rb +7 -3
  17. data/lib/rubocop/cop/internal_affairs/location_exists.rb +116 -0
  18. data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -1
  19. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +2 -1
  20. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +91 -0
  21. data/lib/rubocop/cop/internal_affairs/plugin.rb +33 -0
  22. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +6 -5
  23. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +7 -1
  24. data/lib/rubocop/cop/internal_affairs.rb +2 -16
  25. data/lib/rubocop/cop/layout/block_alignment.rb +2 -0
  26. data/lib/rubocop/cop/layout/block_end_newline.rb +1 -0
  27. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +4 -4
  28. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  29. data/lib/rubocop/cop/layout/else_alignment.rb +2 -2
  30. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  31. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +3 -3
  32. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +27 -1
  33. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +1 -0
  34. data/lib/rubocop/cop/layout/empty_lines_around_method_body.rb +22 -2
  35. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +1 -1
  36. data/lib/rubocop/cop/layout/indentation_width.rb +1 -0
  37. data/lib/rubocop/cop/layout/line_length.rb +8 -4
  38. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -0
  39. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -2
  40. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +1 -0
  41. data/lib/rubocop/cop/layout/redundant_line_break.rb +9 -5
  42. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -5
  43. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  44. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  45. data/lib/rubocop/cop/layout/space_before_block_braces.rb +1 -0
  46. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +1 -0
  47. data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -0
  48. data/lib/rubocop/cop/lint/debugger.rb +2 -2
  49. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
  50. data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -17
  51. data/lib/rubocop/cop/lint/empty_conditional_body.rb +14 -64
  52. data/lib/rubocop/cop/lint/erb_new_arguments.rb +0 -6
  53. data/lib/rubocop/cop/lint/float_comparison.rb +1 -6
  54. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +1 -1
  55. data/lib/rubocop/cop/lint/literal_as_condition.rb +103 -9
  56. data/lib/rubocop/cop/lint/mixed_case_range.rb +2 -2
  57. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  58. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -3
  59. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
  60. data/lib/rubocop/cop/lint/raise_exception.rb +29 -10
  61. data/lib/rubocop/cop/lint/redundant_require_statement.rb +0 -21
  62. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +261 -0
  63. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  64. data/lib/rubocop/cop/lint/redundant_with_object.rb +3 -0
  65. data/lib/rubocop/cop/lint/return_in_void_context.rb +9 -11
  66. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +8 -1
  67. data/lib/rubocop/cop/lint/shared_mutable_default.rb +12 -1
  68. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  69. data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +111 -0
  70. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
  71. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  72. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +2 -0
  73. data/lib/rubocop/cop/lint/unreachable_code.rb +1 -0
  74. data/lib/rubocop/cop/lint/unreachable_loop.rb +5 -5
  75. data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -0
  76. data/lib/rubocop/cop/lint/useless_constant_scoping.rb +71 -0
  77. data/lib/rubocop/cop/lint/void.rb +7 -0
  78. data/lib/rubocop/cop/metrics/block_length.rb +1 -0
  79. data/lib/rubocop/cop/metrics/method_length.rb +1 -0
  80. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  81. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +7 -7
  82. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  83. data/lib/rubocop/cop/mixin/allowed_pattern.rb +4 -4
  84. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  85. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +2 -2
  86. data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
  87. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  88. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  89. data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +20 -0
  90. data/lib/rubocop/cop/mixin/forbidden_pattern.rb +16 -0
  91. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +0 -1
  92. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +18 -18
  93. data/lib/rubocop/cop/mixin/hash_subset.rb +19 -4
  94. data/lib/rubocop/cop/mixin/hash_transform_method.rb +74 -74
  95. data/lib/rubocop/cop/mixin/method_complexity.rb +1 -0
  96. data/lib/rubocop/cop/mixin/percent_literal.rb +1 -1
  97. data/lib/rubocop/cop/mixin/range_help.rb +15 -3
  98. data/lib/rubocop/cop/mixin/string_help.rb +1 -1
  99. data/lib/rubocop/cop/mixin/target_ruby_version.rb +1 -1
  100. data/lib/rubocop/cop/mixin/trailing_comma.rb +12 -0
  101. data/lib/rubocop/cop/naming/block_forwarding.rb +3 -3
  102. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  103. data/lib/rubocop/cop/naming/method_name.rb +64 -8
  104. data/lib/rubocop/cop/naming/predicate_name.rb +44 -0
  105. data/lib/rubocop/cop/naming/variable_name.rb +51 -6
  106. data/lib/rubocop/cop/registry.rb +9 -6
  107. data/lib/rubocop/cop/style/accessor_grouping.rb +19 -5
  108. data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -3
  109. data/lib/rubocop/cop/style/array_intersect.rb +39 -28
  110. data/lib/rubocop/cop/style/block_delimiters.rb +2 -1
  111. data/lib/rubocop/cop/style/class_and_module_children.rb +29 -7
  112. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  113. data/lib/rubocop/cop/style/collection_methods.rb +1 -0
  114. data/lib/rubocop/cop/style/combinable_loops.rb +1 -0
  115. data/lib/rubocop/cop/style/commented_keyword.rb +10 -3
  116. data/lib/rubocop/cop/style/comparable_between.rb +75 -0
  117. data/lib/rubocop/cop/style/conditional_assignment.rb +3 -0
  118. data/lib/rubocop/cop/style/double_negation.rb +2 -2
  119. data/lib/rubocop/cop/style/empty_literal.rb +4 -0
  120. data/lib/rubocop/cop/style/endless_method.rb +163 -18
  121. data/lib/rubocop/cop/style/expand_path_arguments.rb +2 -7
  122. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  123. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  124. data/lib/rubocop/cop/style/for.rb +1 -0
  125. data/lib/rubocop/cop/style/format_string_token.rb +38 -11
  126. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
  127. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  128. data/lib/rubocop/cop/style/guard_clause.rb +2 -1
  129. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -2
  130. data/lib/rubocop/cop/style/hash_fetch_chain.rb +105 -0
  131. data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
  132. data/lib/rubocop/cop/style/if_inside_else.rb +10 -13
  133. data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -2
  134. data/lib/rubocop/cop/style/inverse_methods.rb +9 -5
  135. data/lib/rubocop/cop/style/invertible_unless_condition.rb +2 -2
  136. data/lib/rubocop/cop/style/ip_addresses.rb +2 -2
  137. data/lib/rubocop/cop/style/it_block_parameter.rb +100 -0
  138. data/lib/rubocop/cop/style/keyword_parameters_order.rb +13 -7
  139. data/lib/rubocop/cop/style/lambda.rb +1 -0
  140. data/lib/rubocop/cop/style/line_end_concatenation.rb +10 -4
  141. data/lib/rubocop/cop/style/map_into_array.rb +1 -0
  142. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -4
  143. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +2 -1
  144. data/lib/rubocop/cop/style/multiline_block_chain.rb +2 -1
  145. data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -9
  146. data/lib/rubocop/cop/style/next.rb +44 -0
  147. data/lib/rubocop/cop/style/object_then.rb +1 -0
  148. data/lib/rubocop/cop/style/proc.rb +1 -0
  149. data/lib/rubocop/cop/style/raise_args.rb +8 -8
  150. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  151. data/lib/rubocop/cop/style/redundant_condition.rb +57 -0
  152. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +14 -4
  153. data/lib/rubocop/cop/style/redundant_format.rb +257 -0
  154. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  155. data/lib/rubocop/cop/style/redundant_parentheses.rb +20 -5
  156. data/lib/rubocop/cop/style/redundant_self.rb +1 -0
  157. data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -1
  158. data/lib/rubocop/cop/style/redundant_sort_by.rb +17 -1
  159. data/lib/rubocop/cop/style/rescue_modifier.rb +3 -0
  160. data/lib/rubocop/cop/style/return_nil.rb +2 -2
  161. data/lib/rubocop/cop/style/select_by_regexp.rb +4 -1
  162. data/lib/rubocop/cop/style/single_line_do_end_block.rb +3 -1
  163. data/lib/rubocop/cop/style/single_line_methods.rb +3 -3
  164. data/lib/rubocop/cop/style/sole_nested_conditional.rb +41 -106
  165. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  166. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  167. data/lib/rubocop/cop/style/symbol_proc.rb +2 -0
  168. data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -0
  169. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +47 -6
  170. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +48 -6
  171. data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
  172. data/lib/rubocop/cop/util.rb +2 -2
  173. data/lib/rubocop/cop/utils/format_string.rb +10 -5
  174. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  175. data/lib/rubocop/cop/variable_force/variable.rb +2 -7
  176. data/lib/rubocop/cop/variable_force.rb +1 -1
  177. data/lib/rubocop/cops_documentation_generator.rb +12 -1
  178. data/lib/rubocop/directive_comment.rb +36 -3
  179. data/lib/rubocop/ext/regexp_node.rb +0 -1
  180. data/lib/rubocop/lsp/runtime.rb +6 -4
  181. data/lib/rubocop/lsp/server.rb +0 -2
  182. data/lib/rubocop/lsp/stdin_runner.rb +3 -1
  183. data/lib/rubocop/magic_comment.rb +8 -0
  184. data/lib/rubocop/options.rb +26 -11
  185. data/lib/rubocop/path_util.rb +4 -0
  186. data/lib/rubocop/plugin/configuration_integrator.rb +143 -0
  187. data/lib/rubocop/plugin/load_error.rb +26 -0
  188. data/lib/rubocop/plugin/loader.rb +100 -0
  189. data/lib/rubocop/plugin/not_supported_error.rb +29 -0
  190. data/lib/rubocop/plugin.rb +46 -0
  191. data/lib/rubocop/rake_task.rb +4 -1
  192. data/lib/rubocop/rspec/cop_helper.rb +13 -1
  193. data/lib/rubocop/rspec/shared_contexts.rb +35 -0
  194. data/lib/rubocop/rspec/support.rb +3 -0
  195. data/lib/rubocop/runner.rb +5 -1
  196. data/lib/rubocop/server/cache.rb +47 -11
  197. data/lib/rubocop/server/cli.rb +2 -2
  198. data/lib/rubocop/target_finder.rb +1 -1
  199. data/lib/rubocop/target_ruby.rb +1 -1
  200. data/lib/rubocop/version.rb +30 -8
  201. data/lib/rubocop.rb +10 -1
  202. data/lib/ruby_lsp/rubocop/addon.rb +7 -10
  203. data/lib/ruby_lsp/rubocop/{wraps_built_in_lsp_runtime.rb → runtime_adapter.rb} +25 -10
  204. metadata +43 -12
  205. data/lib/rubocop/cop/utils/regexp_ranges.rb +0 -113
@@ -6,14 +6,31 @@ module RuboCop
6
6
  # Makes sure that all methods use the configured style,
7
7
  # snake_case or camelCase, for their names.
8
8
  #
9
- # This cop has `AllowedPatterns` configuration option.
9
+ # Method names matching patterns are always allowed.
10
10
  #
11
- # Naming/MethodName:
12
- # AllowedPatterns:
13
- # - '\AonSelectionBulkChange\z'
14
- # - '\AonSelectionCleared\z'
11
+ # The cop can be configured with `AllowedPatterns` to allow certain regexp patterns:
15
12
  #
16
- # Method names matching patterns are always allowed.
13
+ # [source,yaml]
14
+ # ----
15
+ # Naming/MethodName:
16
+ # AllowedPatterns:
17
+ # - '\AonSelectionBulkChange\z'
18
+ # - '\AonSelectionCleared\z'
19
+ # ----
20
+ #
21
+ # As well, you can also forbid specific method names or regexp patterns
22
+ # using `ForbiddenIdentifiers` or `ForbiddenPatterns`:
23
+ #
24
+ # [source,yaml]
25
+ # ----
26
+ # Naming/MethodName:
27
+ # ForbiddenIdentifiers:
28
+ # - 'def'
29
+ # - 'super'
30
+ # ForbiddenPatterns:
31
+ # - '_v1\z'
32
+ # - '_gen1\z'
33
+ # ----
17
34
  #
18
35
  # @example EnforcedStyle: snake_case (default)
19
36
  # # bad
@@ -28,12 +45,26 @@ module RuboCop
28
45
  #
29
46
  # # good
30
47
  # def fooBar; end
48
+ #
49
+ # @example ForbiddenIdentifiers: ['def', 'super']
50
+ # # bad
51
+ # def def; end
52
+ # def super; end
53
+ #
54
+ # @example ForbiddenPatterns: ['_v1\z', '_gen1\z']
55
+ # # bad
56
+ # def release_v1; end
57
+ # def api_gen1; end
58
+ #
31
59
  class MethodName < Base
32
60
  include ConfigurableNaming
33
61
  include AllowedPattern
34
62
  include RangeHelp
63
+ include ForbiddenIdentifiers
64
+ include ForbiddenPattern
35
65
 
36
66
  MSG = 'Use %<style>s for method names.'
67
+ MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another method name instead.'
37
68
 
38
69
  # @!method sym_name(node)
39
70
  def_node_matcher :sym_name, '(sym $_name)'
@@ -48,19 +79,44 @@ module RuboCop
48
79
  name = attr_name(name_item)
49
80
  next if !name || matches_allowed_pattern?(name)
50
81
 
51
- check_name(node, name, range_position(node))
82
+ if forbidden_name?(name.to_s)
83
+ register_forbidden_name(node)
84
+ else
85
+ check_name(node, name, range_position(node))
86
+ end
52
87
  end
53
88
  end
54
89
 
55
90
  def on_def(node)
56
91
  return if node.operator_method? || matches_allowed_pattern?(node.method_name)
57
92
 
58
- check_name(node, node.method_name, node.loc.name)
93
+ if forbidden_name?(node.method_name.to_s)
94
+ register_forbidden_name(node)
95
+ else
96
+ check_name(node, node.method_name, node.loc.name)
97
+ end
59
98
  end
60
99
  alias on_defs on_def
61
100
 
62
101
  private
63
102
 
103
+ def forbidden_name?(name)
104
+ forbidden_identifier?(name) || forbidden_pattern?(name)
105
+ end
106
+
107
+ def register_forbidden_name(node)
108
+ if node.any_def_type?
109
+ name_node = node.loc.name
110
+ method_name = node.method_name
111
+ else
112
+ attrs = node.attribute_accessor?
113
+ name_node = attrs.last.last
114
+ method_name = attr_name(name_node)
115
+ end
116
+ message = format(MSG_FORBIDDEN, identifier: method_name)
117
+ add_offense(name_node, message: message)
118
+ end
119
+
64
120
  def attr_name(name_item)
65
121
  sym_name(name_item) || str_name(name_item)
66
122
  end
@@ -17,6 +17,10 @@ module RuboCop
17
17
  # they end with a `?`. These methods should be changed to remove the
18
18
  # prefix.
19
19
  #
20
+ # When `UseSorbetSigs` set to true (optional), the cop will only report
21
+ # offenses if the method has a Sorbet `sig` with a return type of
22
+ # `T::Boolean`. Dynamic methods are not supported with this configuration.
23
+ #
20
24
  # @example NamePrefix: ['is_', 'has_', 'have_'] (default)
21
25
  # # bad
22
26
  # def is_even(value)
@@ -58,6 +62,30 @@ module RuboCop
58
62
  # def is_even?(value)
59
63
  # end
60
64
  #
65
+ # @example UseSorbetSigs: false (default)
66
+ # # bad
67
+ # sig { returns(String) }
68
+ # def is_this_thing_on
69
+ # "yes"
70
+ # end
71
+ #
72
+ # # good - Sorbet signature is not evaluated
73
+ # sig { returns(String) }
74
+ # def is_this_thing_on?
75
+ # "yes"
76
+ # end
77
+ #
78
+ # @example UseSorbetSigs: true
79
+ # # bad
80
+ # sig { returns(T::Boolean) }
81
+ # def odd(value)
82
+ # end
83
+ #
84
+ # # good
85
+ # sig { returns(T::Boolean) }
86
+ # def odd?(value)
87
+ # end
88
+ #
61
89
  # @example MethodDefinitionMacros: ['define_method', 'define_singleton_method'] (default)
62
90
  # # bad
63
91
  # define_method(:is_even) { |value| }
@@ -100,6 +128,7 @@ module RuboCop
100
128
  method_name = node.method_name.to_s
101
129
 
102
130
  next if allowed_method_name?(method_name, prefix)
131
+ next if use_sorbet_sigs? && !sorbet_sig?(node, return_type: 'T::Boolean')
103
132
 
104
133
  add_offense(
105
134
  node.loc.name,
@@ -121,6 +150,17 @@ module RuboCop
121
150
 
122
151
  private
123
152
 
153
+ # @!method sorbet_return_type(node)
154
+ def_node_matcher :sorbet_return_type, <<~PATTERN
155
+ (block (send nil? :sig) args (send _ :returns $_type))
156
+ PATTERN
157
+
158
+ def sorbet_sig?(node, return_type: nil)
159
+ return false unless (type = sorbet_return_type(node.left_sibling))
160
+
161
+ type.source == return_type
162
+ end
163
+
124
164
  def allowed_method_name?(method_name, prefix)
125
165
  !(method_name.start_with?(prefix) && # cheap check to avoid allocating Regexp
126
166
  method_name.match?(/^#{prefix}[^0-9]/)) ||
@@ -151,6 +191,10 @@ module RuboCop
151
191
  cop_config['NamePrefix']
152
192
  end
153
193
 
194
+ def use_sorbet_sigs?
195
+ cop_config['UseSorbetSigs']
196
+ end
197
+
154
198
  def method_definition_macros(macro_name)
155
199
  cop_config['MethodDefinitionMacros'].include?(macro_name.to_s)
156
200
  end
@@ -3,8 +3,15 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Naming
6
- # Makes sure that all variables use the configured style,
7
- # snake_case or camelCase, for their names.
6
+ # Checks that the configured style (snake_case or camelCase) is used for all variable names.
7
+ # This includes local variables, instance variables, class variables, method arguments
8
+ # (positional, keyword, rest or block), and block arguments.
9
+ #
10
+ # The cop can also be configured to forbid using specific names for variables, using
11
+ # `ForbiddenIdentifiers` or `ForbiddenPatterns`. In addition to the above, this applies
12
+ # to global variables as well.
13
+ #
14
+ # Method definitions and method calls are not affected by this cop.
8
15
  #
9
16
  # @example EnforcedStyle: snake_case (default)
10
17
  # # bad
@@ -26,24 +33,45 @@ module RuboCop
26
33
  #
27
34
  # @example AllowedPatterns: ['_v\d+\z']
28
35
  # # good (with EnforcedStyle: camelCase)
29
- # :release_v1
36
+ # release_v1 = true
37
+ #
38
+ # @example ForbiddenIdentifiers: ['fooBar']
39
+ # # bad (in all cases)
40
+ # fooBar = 1
41
+ # @fooBar = 1
42
+ # @@fooBar = 1
43
+ # $fooBar = 1
44
+ #
45
+ # @example ForbiddenPatterns: ['_v\d+\z']
46
+ # # bad (in all cases)
47
+ # release_v1 = true
48
+ # @release_v1 = true
49
+ # @@release_v1 = true
50
+ # $release_v1 = true
30
51
  #
31
52
  class VariableName < Base
32
53
  include AllowedIdentifiers
33
54
  include ConfigurableNaming
34
55
  include AllowedPattern
56
+ include ForbiddenIdentifiers
57
+ include ForbiddenPattern
35
58
 
36
59
  MSG = 'Use %<style>s for variable names.'
60
+ MSG_FORBIDDEN = '`%<identifier>s` is forbidden, use another name instead.'
37
61
 
38
62
  def valid_name?(node, name, given_style = style)
39
63
  super || matches_allowed_pattern?(name)
40
64
  end
41
65
 
42
66
  def on_lvasgn(node)
43
- return unless node.name
44
- return if allowed_identifier?(node.name)
67
+ return unless (name = node.name)
68
+ return if allowed_identifier?(name)
45
69
 
46
- check_name(node, node.name, node.loc.name)
70
+ if forbidden_name?(name)
71
+ register_forbidden_name(node)
72
+ else
73
+ check_name(node, name, node.loc.name)
74
+ end
47
75
  end
48
76
  alias on_ivasgn on_lvasgn
49
77
  alias on_cvasgn on_lvasgn
@@ -56,11 +84,28 @@ module RuboCop
56
84
  alias on_blockarg on_lvasgn
57
85
  alias on_lvar on_lvasgn
58
86
 
87
+ # Only forbidden names are checked for global variable assignment
88
+ def on_gvasgn(node)
89
+ return unless (name = node.name)
90
+ return unless forbidden_name?(name)
91
+
92
+ register_forbidden_name(node)
93
+ end
94
+
59
95
  private
60
96
 
97
+ def forbidden_name?(name)
98
+ forbidden_identifier?(name) || forbidden_pattern?(name)
99
+ end
100
+
61
101
  def message(style)
62
102
  format(MSG, style: style)
63
103
  end
104
+
105
+ def register_forbidden_name(node)
106
+ message = format(MSG_FORBIDDEN, identifier: node.name)
107
+ add_offense(node.loc.name, message: message)
108
+ end
64
109
  end
65
110
  end
66
111
  end
@@ -23,8 +23,8 @@ module RuboCop
23
23
  global.without_department(:Test).cops
24
24
  end
25
25
 
26
- def self.qualified_cop_name(name, origin)
27
- global.qualified_cop_name(name, origin)
26
+ def self.qualified_cop_name(name, origin, warn: true)
27
+ global.qualified_cop_name(name, origin, warn: warn)
28
28
  end
29
29
 
30
30
  # Changes momentarily the global registry
@@ -139,7 +139,7 @@ module RuboCop
139
139
 
140
140
  case potential_badges.size
141
141
  when 0 then name # No namespace found. Deal with it later in caller.
142
- when 1 then resolve_badge(badge, potential_badges.first, path)
142
+ when 1 then resolve_badge(badge, potential_badges.first, path, warn: warn)
143
143
  else raise AmbiguousCopName.new(badge, path, potential_badges)
144
144
  end
145
145
  end
@@ -296,11 +296,14 @@ module RuboCop
296
296
  self.class.new(cops)
297
297
  end
298
298
 
299
- def resolve_badge(given_badge, real_badge, source_path)
299
+ def resolve_badge(given_badge, real_badge, source_path, warn: true)
300
300
  unless given_badge.match?(real_badge)
301
301
  path = PathUtil.smart_path(source_path)
302
- warn "#{path}: #{given_badge} has the wrong namespace - " \
303
- "replace it with #{given_badge.with_department(real_badge.department)}"
302
+
303
+ if warn
304
+ warn("#{path}: #{given_badge} has the wrong namespace - " \
305
+ "replace it with #{given_badge.with_department(real_badge.department)}")
306
+ end
304
307
  end
305
308
 
306
309
  real_badge.to_s
@@ -139,12 +139,16 @@ module RuboCop
139
139
  style == :separated
140
140
  end
141
141
 
142
+ def groupable_sibling_accessor?(node, sibling)
143
+ sibling.attribute_accessor? &&
144
+ sibling.method?(node.method_name) &&
145
+ node_visibility(sibling) == node_visibility(node) &&
146
+ groupable_accessor?(sibling) && !previous_line_comment?(sibling)
147
+ end
148
+
142
149
  def groupable_sibling_accessors(send_node)
143
150
  send_node.parent.each_child_node(:send).select do |sibling|
144
- sibling.attribute_accessor? &&
145
- sibling.method?(send_node.method_name) &&
146
- node_visibility(sibling) == node_visibility(send_node) &&
147
- groupable_accessor?(sibling) && !previous_line_comment?(sibling)
151
+ groupable_sibling_accessor?(send_node, sibling)
148
152
  end
149
153
  end
150
154
 
@@ -155,13 +159,23 @@ module RuboCop
155
159
 
156
160
  def preferred_accessors(node)
157
161
  if grouped_style?
162
+ return if skip_for_grouping?(node)
163
+
158
164
  accessors = groupable_sibling_accessors(node)
159
- group_accessors(node, accessors) if node.loc == accessors.first.loc
165
+ if node.loc == accessors.first.loc || skip_for_grouping?(accessors.first)
166
+ group_accessors(node, accessors)
167
+ end
160
168
  else
161
169
  separate_accessors(node)
162
170
  end
163
171
  end
164
172
 
173
+ # Group after constants
174
+ def skip_for_grouping?(node)
175
+ node.right_siblings.any?(&:casgn_type?) &&
176
+ node.right_siblings.any? { |n| n.send_type? && groupable_sibling_accessor?(node, n) }
177
+ end
178
+
165
179
  def group_accessors(node, accessors)
166
180
  accessor_names = accessors.flat_map { |accessor| accessor.arguments.map(&:source) }.uniq
167
181
 
@@ -32,10 +32,10 @@ module RuboCop
32
32
  # This cop handles not only method forwarding but also forwarding to `super`.
33
33
  #
34
34
  # [NOTE]
35
- # --
35
+ # ====
36
36
  # Because of a bug in Ruby 3.3.0, when a block is referenced inside of another block,
37
37
  # no offense will be registered until Ruby 3.4:
38
-
38
+ #
39
39
  # [source,ruby]
40
40
  # ----
41
41
  # def foo(&block)
@@ -43,7 +43,7 @@ module RuboCop
43
43
  # block_method { bar(&block) }
44
44
  # end
45
45
  # ----
46
- # --
46
+ # ====
47
47
  #
48
48
  # @example
49
49
  # # bad
@@ -6,7 +6,8 @@ module RuboCop
6
6
  # In Ruby 3.1, `Array#intersect?` has been added.
7
7
  #
8
8
  # This cop identifies places where `(array1 & array2).any?`
9
- # can be replaced by `array1.intersect?(array2)`.
9
+ # or `(array1.intersection(array2)).any?` can be replaced by
10
+ # `array1.intersect?(array2)`.
10
11
  #
11
12
  # The `array1.intersect?(array2)` method is faster than
12
13
  # `(array1 & array2).any?` and is more readable.
@@ -20,6 +21,10 @@ module RuboCop
20
21
  # [1].intersect?([1,2]) { |x| false } # => true
21
22
  # ----
22
23
  #
24
+ # NOTE: Although `Array#intersection` can take zero or multiple arguments,
25
+ # only cases where exactly one argument is provided can be replaced with
26
+ # `Array#intersect?` and are handled by this cop.
27
+ #
23
28
  # @safety
24
29
  # This cop cannot guarantee that `array1` and `array2` are
25
30
  # actually arrays while method `intersect?` is for arrays only.
@@ -30,6 +35,11 @@ module RuboCop
30
35
  # (array1 & array2).empty?
31
36
  # (array1 & array2).none?
32
37
  #
38
+ # # bad
39
+ # array1.intersection(array2).any?
40
+ # array1.intersection(array2).empty?
41
+ # array1.intersection(array2).none?
42
+ #
33
43
  # # good
34
44
  # array1.intersect?(array2)
35
45
  # !array1.intersect?(array2)
@@ -53,65 +63,66 @@ module RuboCop
53
63
 
54
64
  minimum_target_ruby_version 3.1
55
65
 
56
- # @!method regular_bad_intersection_check?(node)
57
- def_node_matcher :regular_bad_intersection_check?, <<~PATTERN
58
- (send
59
- (begin
60
- (send $(...) :& $(...))
61
- ) ${:any? :empty? :none?}
62
- )
63
- PATTERN
66
+ PREDICATES = %i[any? empty? none?].to_set.freeze
67
+ ACTIVE_SUPPORT_PREDICATES = (PREDICATES + %i[present? blank?]).freeze
64
68
 
65
- # @!method active_support_bad_intersection_check?(node)
66
- def_node_matcher :active_support_bad_intersection_check?, <<~PATTERN
67
- (send
68
- (begin
69
- (send $(...) :& $(...))
70
- ) ${:present? :any? :blank? :empty? :none?}
69
+ # @!method bad_intersection_check?(node, predicates)
70
+ def_node_matcher :bad_intersection_check?, <<~PATTERN
71
+ (call
72
+ {
73
+ (begin (send $_ :& $_))
74
+ (call $_ :intersection $_)
75
+ }
76
+ $%1
71
77
  )
72
78
  PATTERN
73
79
 
74
- MSG = 'Use `%<negated>s%<receiver>s.intersect?(%<argument>s)` ' \
75
- 'instead of `(%<receiver>s & %<argument>s).%<method_name>s`.'
80
+ MSG = 'Use `%<negated>s%<receiver>s%<dot>sintersect?(%<argument>s)` ' \
81
+ 'instead of `%<existing>s`.'
76
82
  STRAIGHT_METHODS = %i[present? any?].freeze
77
83
  NEGATED_METHODS = %i[blank? empty? none?].freeze
78
84
  RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
79
85
 
80
86
  def on_send(node)
81
87
  return if node.block_literal?
82
- return unless (receiver, argument, method_name = bad_intersection_check?(node))
88
+ return unless (receiver, argument, method_name = bad_intersection?(node))
83
89
 
84
- message = message(receiver.source, argument.source, method_name)
90
+ dot = node.loc.dot.source
91
+ message = message(receiver.source, argument.source, method_name, dot, node.source)
85
92
 
86
93
  add_offense(node, message: message) do |corrector|
87
94
  bang = straight?(method_name) ? '' : '!'
88
95
 
89
- corrector.replace(node, "#{bang}#{receiver.source}.intersect?(#{argument.source})")
96
+ corrector.replace(node, "#{bang}#{receiver.source}#{dot}intersect?(#{argument.source})")
90
97
  end
91
98
  end
99
+ alias on_csend on_send
92
100
 
93
101
  private
94
102
 
95
- def bad_intersection_check?(node)
96
- if active_support_extensions_enabled?
97
- active_support_bad_intersection_check?(node)
98
- else
99
- regular_bad_intersection_check?(node)
100
- end
103
+ def bad_intersection?(node)
104
+ predicates = if active_support_extensions_enabled?
105
+ ACTIVE_SUPPORT_PREDICATES
106
+ else
107
+ PREDICATES
108
+ end
109
+
110
+ bad_intersection_check?(node, predicates)
101
111
  end
102
112
 
103
113
  def straight?(method_name)
104
114
  STRAIGHT_METHODS.include?(method_name.to_sym)
105
115
  end
106
116
 
107
- def message(receiver, argument, method_name)
117
+ def message(receiver, argument, method_name, dot, existing)
108
118
  negated = straight?(method_name) ? '' : '!'
109
119
  format(
110
120
  MSG,
111
121
  negated: negated,
112
122
  receiver: receiver,
113
123
  argument: argument,
114
- method_name: method_name
124
+ dot: dot,
125
+ existing: existing
115
126
  )
116
127
  end
117
128
  end
@@ -208,6 +208,7 @@ module RuboCop
208
208
  end
209
209
 
210
210
  alias on_numblock on_block
211
+ alias on_itblock on_block
211
212
 
212
213
  private
213
214
 
@@ -347,7 +348,7 @@ module RuboCop
347
348
  # rubocop:disable Metrics/CyclomaticComplexity
348
349
  def get_blocks(node, &block)
349
350
  case node.type
350
- when :block, :numblock
351
+ when :block, :numblock, :itblock
351
352
  yield node
352
353
  when :send, :csend
353
354
  # When a method has an argument which is another method with a block,
@@ -3,14 +3,26 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks the style of children definitions at classes and
7
- # modules. Basically there are two different styles:
6
+ # Checks that namespaced classes and modules are defined with a consistent style.
7
+ #
8
+ # With `nested` style, classes and modules should be defined separately (one constant
9
+ # on each line, without `::`). With `compact` style, classes and modules should be
10
+ # defined with fully qualified names (using `::` for namespaces).
11
+ #
12
+ # NOTE: The style chosen will affect `Module.nesting` for the class or module. Using
13
+ # `nested` style will result in each level being added, whereas `compact` style will
14
+ # only include the fully qualified class or module name.
15
+ #
16
+ # By default, `EnforcedStyle` applies to both classes and modules. If desired, separate
17
+ # styles can be defined for classes and modules by using `EnforcedStyleForClasses` and
18
+ # `EnforcedStyleForModules` respectively. If not set, or set to nil, the `EnforcedStyle`
19
+ # value will be used.
8
20
  #
9
21
  # @safety
10
22
  # Autocorrection is unsafe.
11
23
  #
12
- # Moving from compact to nested children requires knowledge of whether the
13
- # outer parent is a module or a class. Moving from nested to compact requires
24
+ # Moving from `compact` to `nested` children requires knowledge of whether the
25
+ # outer parent is a module or a class. Moving from `nested` to `compact` requires
14
26
  # verification that the outer parent is defined elsewhere. RuboCop does not
15
27
  # have the knowledge to perform either operation safely and thus requires
16
28
  # manual oversight.
@@ -42,16 +54,18 @@ module RuboCop
42
54
  def on_class(node)
43
55
  return if node.parent_class && style != :nested
44
56
 
45
- check_style(node, node.body)
57
+ check_style(node, node.body, style_for_classes)
46
58
  end
47
59
 
48
60
  def on_module(node)
49
- check_style(node, node.body)
61
+ check_style(node, node.body, style_for_modules)
50
62
  end
51
63
 
52
64
  private
53
65
 
54
66
  def nest_or_compact(corrector, node)
67
+ style = node.class_type? ? style_for_classes : style_for_modules
68
+
55
69
  if style == :nested
56
70
  nest_definition(corrector, node)
57
71
  else
@@ -141,7 +155,7 @@ module RuboCop
141
155
  node.source_range.source_line[/\A\s*/]
142
156
  end
143
157
 
144
- def check_style(node, body)
158
+ def check_style(node, body, style)
145
159
  return if node.identifier.namespace&.cbase_type?
146
160
 
147
161
  if style == :nested
@@ -183,6 +197,14 @@ module RuboCop
183
197
  def compact_node_name?(node)
184
198
  node.identifier.source.include?('::')
185
199
  end
200
+
201
+ def style_for_classes
202
+ cop_config['EnforcedStyleForClasses'] || style
203
+ end
204
+
205
+ def style_for_modules
206
+ cop_config['EnforcedStyleForModules'] || style
207
+ end
186
208
  end
187
209
  end
188
210
  end
@@ -68,7 +68,7 @@ module RuboCop
68
68
  PATTERN
69
69
 
70
70
  def on_send(node)
71
- def_node = node.each_ancestor(:def, :defs).first
71
+ def_node = node.each_ancestor(:any_def).first
72
72
  return if def_node &&
73
73
  (allowed_method?(def_node.method_name) ||
74
74
  matches_allowed_pattern?(def_node.method_name))
@@ -50,6 +50,7 @@ module RuboCop
50
50
  check_method_node(node.send_node)
51
51
  end
52
52
  alias on_numblock on_block
53
+ alias on_itblock on_block
53
54
 
54
55
  def on_send(node)
55
56
  return unless implicit_block?(node)
@@ -80,6 +80,7 @@ module RuboCop
80
80
  # rubocop:enable Metrics/CyclomaticComplexity
81
81
 
82
82
  alias on_numblock on_block
83
+ alias on_itblock on_block
83
84
 
84
85
  def on_for(node)
85
86
  return unless node.parent&.begin_type?
@@ -9,8 +9,8 @@ module RuboCop
9
9
  # These keywords are: `class`, `module`, `def`, `begin`, `end`.
10
10
  #
11
11
  # Note that some comments
12
- # (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`)
13
- # and RBS::Inline annotation comments are allowed.
12
+ # (`:nodoc:`, `:yields:`, `rubocop:disable` and `rubocop:todo`),
13
+ # RBS::Inline annotation, and Steep annotation (`steep:ignore`) are allowed.
14
14
  #
15
15
  # Autocorrection removes comments from `end` keyword and keeps comments
16
16
  # for `class`, `module`, `def` and `begin` above the keyword.
@@ -57,9 +57,11 @@ module RuboCop
57
57
 
58
58
  REGEXP = /(?<keyword>\S+).*#/.freeze
59
59
 
60
- SUBCLASS_DEFINITION = /\A\s*class\s+\w+\s*<\s*\w+/.freeze
60
+ SUBCLASS_DEFINITION = /\A\s*class\s+(\w|::)+\s*<\s*(\w|::)+/.freeze
61
61
  METHOD_DEFINITION = /\A\s*def\s/.freeze
62
62
 
63
+ STEEP_REGEXP = /#\ssteep:ignore(\s|\z)/.freeze
64
+
63
65
  def on_new_investigation
64
66
  processed_source.comments.each do |comment|
65
67
  next unless offensive?(comment) && (match = source_line(comment).match(REGEXP))
@@ -86,6 +88,7 @@ module RuboCop
86
88
  def offensive?(comment)
87
89
  line = source_line(comment)
88
90
  return false if rbs_inline_annotation?(line, comment)
91
+ return false if steep_annotation?(comment)
89
92
 
90
93
  KEYWORD_REGEXES.any? { |r| r.match?(line) } &&
91
94
  ALLOWED_COMMENT_REGEXES.none? { |r| r.match?(line) }
@@ -105,6 +108,10 @@ module RuboCop
105
108
  false
106
109
  end
107
110
  end
111
+
112
+ def steep_annotation?(comment)
113
+ comment.text.match?(STEEP_REGEXP)
114
+ end
108
115
  end
109
116
  end
110
117
  end