rubocop 1.75.8 → 1.81.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 (164) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -16
  3. data/config/default.yml +117 -26
  4. data/config/obsoletion.yml +6 -3
  5. data/exe/rubocop +1 -8
  6. data/lib/rubocop/cli/command/auto_generate_config.rb +2 -2
  7. data/lib/rubocop/cli.rb +18 -3
  8. data/lib/rubocop/config_loader.rb +4 -39
  9. data/lib/rubocop/config_store.rb +5 -0
  10. data/lib/rubocop/cop/autocorrect_logic.rb +4 -4
  11. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  12. data/lib/rubocop/cop/correctors/alignment_corrector.rb +7 -4
  13. data/lib/rubocop/cop/correctors/for_to_each_corrector.rb +7 -2
  14. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  15. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  16. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +0 -22
  17. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  18. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  19. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  20. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  21. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +4 -1
  22. data/lib/rubocop/cop/internal_affairs/node_type_group.rb +3 -2
  23. data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
  24. data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +1 -1
  25. data/lib/rubocop/cop/layout/class_structure.rb +1 -1
  26. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  27. data/lib/rubocop/cop/layout/dot_position.rb +1 -1
  28. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +30 -12
  29. data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +101 -0
  30. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -1
  31. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +8 -29
  32. data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +1 -1
  33. data/lib/rubocop/cop/layout/line_length.rb +35 -6
  34. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +8 -4
  35. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +8 -0
  36. data/lib/rubocop/cop/layout/space_around_keyword.rb +6 -1
  37. data/lib/rubocop/cop/layout/space_around_operators.rb +8 -0
  38. data/lib/rubocop/cop/layout/space_before_brackets.rb +2 -9
  39. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +7 -2
  40. data/lib/rubocop/cop/layout/trailing_whitespace.rb +1 -1
  41. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  42. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +4 -1
  43. data/lib/rubocop/cop/lint/duplicate_methods.rb +25 -4
  44. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +5 -42
  45. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  46. data/lib/rubocop/cop/lint/float_comparison.rb +4 -4
  47. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  48. data/lib/rubocop/cop/lint/literal_as_condition.rb +34 -28
  49. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -2
  50. data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +1 -0
  51. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  52. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +101 -2
  53. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +4 -4
  54. data/lib/rubocop/cop/lint/require_range_parentheses.rb +1 -1
  55. data/lib/rubocop/cop/lint/rescue_type.rb +1 -1
  56. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  57. data/lib/rubocop/cop/lint/self_assignment.rb +30 -4
  58. data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -7
  59. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  60. data/lib/rubocop/cop/lint/uri_escape_unescape.rb +2 -0
  61. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  62. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  63. data/lib/rubocop/cop/lint/useless_numeric_operation.rb +1 -0
  64. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  65. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  66. data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +121 -0
  67. data/lib/rubocop/cop/lint/void.rb +7 -0
  68. data/lib/rubocop/cop/message_annotator.rb +1 -1
  69. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  70. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  71. data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -7
  72. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -1
  73. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  74. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  75. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  76. data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
  77. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  78. data/lib/rubocop/cop/naming/method_name.rb +127 -13
  79. data/lib/rubocop/cop/naming/predicate_method.rb +319 -0
  80. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  81. data/lib/rubocop/cop/security/eval.rb +2 -1
  82. data/lib/rubocop/cop/security/open.rb +1 -0
  83. data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -1
  84. data/lib/rubocop/cop/style/accessor_grouping.rb +13 -1
  85. data/lib/rubocop/cop/style/arguments_forwarding.rb +11 -17
  86. data/lib/rubocop/cop/style/array_intersect.rb +98 -34
  87. data/lib/rubocop/cop/style/array_intersect_with_single_element.rb +47 -0
  88. data/lib/rubocop/cop/style/bitwise_predicate.rb +8 -1
  89. data/lib/rubocop/cop/style/block_delimiters.rb +1 -1
  90. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  91. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  92. data/lib/rubocop/cop/style/conditional_assignment.rb +4 -2
  93. data/lib/rubocop/cop/style/dig_chain.rb +1 -1
  94. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  95. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  96. data/lib/rubocop/cop/style/explicit_block_argument.rb +1 -1
  97. data/lib/rubocop/cop/style/exponential_notation.rb +3 -2
  98. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  99. data/lib/rubocop/cop/style/hash_conversion.rb +16 -8
  100. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  101. data/lib/rubocop/cop/style/if_unless_modifier.rb +13 -6
  102. data/lib/rubocop/cop/style/infinite_loop.rb +1 -1
  103. data/lib/rubocop/cop/style/inverse_methods.rb +1 -1
  104. data/lib/rubocop/cop/style/it_assignment.rb +69 -12
  105. data/lib/rubocop/cop/style/it_block_parameter.rb +36 -15
  106. data/lib/rubocop/cop/style/map_to_hash.rb +1 -3
  107. data/lib/rubocop/cop/style/map_to_set.rb +1 -3
  108. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +4 -6
  109. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +16 -0
  110. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  111. data/lib/rubocop/cop/style/nil_comparison.rb +9 -7
  112. data/lib/rubocop/cop/style/parallel_assignment.rb +32 -20
  113. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  114. data/lib/rubocop/cop/style/redundant_begin.rb +34 -0
  115. data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
  116. data/lib/rubocop/cop/style/redundant_exception.rb +1 -1
  117. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -9
  118. data/lib/rubocop/cop/style/redundant_format.rb +18 -3
  119. data/lib/rubocop/cop/style/redundant_freeze.rb +1 -1
  120. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  121. data/lib/rubocop/cop/style/redundant_line_continuation.rb +1 -1
  122. data/lib/rubocop/cop/style/redundant_parentheses.rb +55 -16
  123. data/lib/rubocop/cop/style/redundant_regexp_argument.rb +4 -0
  124. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -0
  125. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  126. data/lib/rubocop/cop/style/safe_navigation.rb +44 -12
  127. data/lib/rubocop/cop/style/single_line_methods.rb +7 -4
  128. data/lib/rubocop/cop/style/sole_nested_conditional.rb +32 -2
  129. data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +1 -1
  130. data/lib/rubocop/cop/style/string_concatenation.rb +17 -13
  131. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  132. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  133. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +45 -0
  134. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  135. data/lib/rubocop/cop/style/unless_else.rb +10 -9
  136. data/lib/rubocop/cop/utils/format_string.rb +10 -0
  137. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  138. data/lib/rubocop/cop/variable_force.rb +25 -8
  139. data/lib/rubocop/cops_documentation_generator.rb +1 -0
  140. data/lib/rubocop/formatter/disabled_config_formatter.rb +18 -5
  141. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  142. data/lib/rubocop/formatter/markdown_formatter.rb +1 -0
  143. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  144. data/lib/rubocop/formatter/pacman_formatter.rb +1 -0
  145. data/lib/rubocop/lsp/diagnostic.rb +25 -24
  146. data/lib/rubocop/lsp/routes.rb +65 -9
  147. data/lib/rubocop/lsp/runtime.rb +2 -2
  148. data/lib/rubocop/lsp/server.rb +2 -2
  149. data/lib/rubocop/lsp/stdin_runner.rb +0 -16
  150. data/lib/rubocop/pending_cops_reporter.rb +56 -0
  151. data/lib/rubocop/result_cache.rb +14 -12
  152. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  153. data/lib/rubocop/runner.rb +6 -4
  154. data/lib/rubocop/server/cache.rb +4 -2
  155. data/lib/rubocop/server/client_command/base.rb +10 -0
  156. data/lib/rubocop/server/client_command/exec.rb +2 -1
  157. data/lib/rubocop/server/client_command/start.rb +11 -1
  158. data/lib/rubocop/target_finder.rb +9 -9
  159. data/lib/rubocop/target_ruby.rb +10 -1
  160. data/lib/rubocop/version.rb +1 -1
  161. data/lib/rubocop.rb +12 -1
  162. data/lib/ruby_lsp/rubocop/addon.rb +25 -10
  163. data/lib/ruby_lsp/rubocop/runtime_adapter.rb +49 -15
  164. metadata +22 -8
@@ -22,14 +22,6 @@ module RuboCop
22
22
  class << self
23
23
  include FileFinder
24
24
 
25
- PENDING_BANNER = <<~BANNER
26
- The following cops were added to RuboCop, but are not configured. Please set Enabled to either `true` or `false` in your `.rubocop.yml` file.
27
-
28
- Please also note that you can opt-in to new cops by default by adding this to your config:
29
- AllCops:
30
- NewCops: enable
31
- BANNER
32
-
33
25
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
34
26
  :ignore_unrecognized_cops
35
27
  attr_writer :default_configuration
@@ -83,7 +75,9 @@ module RuboCop
83
75
 
84
76
  puts "configuration from #{absolute_path}" if debug?
85
77
 
86
- raise(TypeError, "Malformed configuration in #{absolute_path}") unless hash.is_a?(Hash)
78
+ unless hash.is_a?(Hash)
79
+ raise(ValidationError, "Malformed configuration in #{absolute_path}")
80
+ end
87
81
 
88
82
  hash
89
83
  end
@@ -132,21 +126,7 @@ module RuboCop
132
126
  add_excludes_from_files(config, config_file)
133
127
  end
134
128
 
135
- merge_with_default(config, config_file).tap do |merged_config|
136
- unless possible_new_cops?(merged_config)
137
- pending_cops = pending_cops_only_qualified(merged_config.pending_cops)
138
- warn_on_pending_cops(pending_cops) unless pending_cops.empty?
139
- end
140
- end
141
- end
142
-
143
- def pending_cops_only_qualified(pending_cops)
144
- pending_cops.select { |cop| Cop::Registry.qualified_cop?(cop.name) }
145
- end
146
-
147
- def possible_new_cops?(config)
148
- disable_pending_cops || enable_pending_cops ||
149
- config.disabled_new_cops? || config.enabled_new_cops?
129
+ merge_with_default(config, config_file)
150
130
  end
151
131
 
152
132
  def add_excludes_from_files(config, config_file)
@@ -208,21 +188,6 @@ module RuboCop
208
188
  ConfigFinder.project_root
209
189
  end
210
190
 
211
- def warn_on_pending_cops(pending_cops)
212
- warn Rainbow(PENDING_BANNER).yellow
213
-
214
- pending_cops.each { |cop| warn_pending_cop cop }
215
-
216
- warn Rainbow('For more information: https://docs.rubocop.org/rubocop/versioning.html').yellow
217
- end
218
-
219
- def warn_pending_cop(cop)
220
- version = cop.metadata['VersionAdded'] || 'N/A'
221
-
222
- warn Rainbow("#{cop.name}: # new in #{version}").yellow
223
- warn Rainbow(' Enabled: true').yellow
224
- end
225
-
226
191
  # Merges the given configuration with the default one.
227
192
  def merge_with_default(config, config_file, unset_nil: true)
228
193
  resolver.merge_with_default(config, config_file, unset_nil: unset_nil)
@@ -25,6 +25,11 @@ module RuboCop
25
25
  @validated = true
26
26
  end
27
27
 
28
+ def apply_options!(options)
29
+ self.options_config = options[:config] if options[:config]
30
+ force_default_config! if options[:force_default_config]
31
+ end
32
+
28
33
  def options_config=(options_config)
29
34
  loaded_config = ConfigLoader.load_file(options_config)
30
35
  @options_config = ConfigLoader.merge_with_default(loaded_config, options_config)
@@ -35,8 +35,8 @@ module RuboCop
35
35
  # `false` is the same as `disabled` for backward compatibility.
36
36
  return false if ['disabled', false].include?(cop_config['AutoCorrect'])
37
37
 
38
- # When LSP is enabled, it is considered as editing source code,
39
- # and autocorrection with `AutoCorrect: contextual` will not be performed.
38
+ # When LSP is enabled or the `--editor-mode` option is on, it is considered as editing
39
+ # source code, and autocorrection with `AutoCorrect: contextual` will not be performed.
40
40
  return false if contextual_autocorrect? && LSP.enabled?
41
41
 
42
42
  # :safe_autocorrect is a derived option based on several command-line
@@ -94,7 +94,7 @@ module RuboCop
94
94
  end
95
95
 
96
96
  def surrounding_heredoc?(node)
97
- node.type?(:str, :dstr, :xstr) && node.heredoc?
97
+ node.any_str_type? && node.heredoc?
98
98
  end
99
99
 
100
100
  def heredoc_range(node)
@@ -106,7 +106,7 @@ module RuboCop
106
106
  end
107
107
 
108
108
  def string_continuation?(node)
109
- node.type?(:str, :dstr, :xstr) && node.source.match?(/\\\s*$/)
109
+ node.any_str_type? && node.source.match?(/\\\s*$/)
110
110
  end
111
111
 
112
112
  def multiline_string?(node)
@@ -45,7 +45,7 @@ module RuboCop
45
45
 
46
46
  gem_declarations(processed_source.ast)
47
47
  .each_cons(2) do |previous, current|
48
- next unless consecutive_lines(previous, current)
48
+ next unless consecutive_lines?(previous, current)
49
49
  next unless case_insensitive_out_of_order?(gem_name(current), gem_name(previous))
50
50
 
51
51
  register_offense(previous, current)
@@ -29,10 +29,13 @@ module RuboCop
29
29
  def align_end(corrector, processed_source, node, align_to)
30
30
  @processed_source = processed_source
31
31
  whitespace = whitespace_range(node)
32
- return false unless whitespace.source.strip.empty?
33
-
34
32
  column = alignment_column(align_to)
35
- corrector.replace(whitespace, ' ' * column)
33
+
34
+ if whitespace.source.strip.empty?
35
+ corrector.replace(whitespace, ' ' * column)
36
+ else
37
+ corrector.insert_after(whitespace, "\n#{' ' * column}")
38
+ end
36
39
  end
37
40
 
38
41
  private
@@ -54,7 +57,7 @@ module RuboCop
54
57
  def inside_string_ranges(node)
55
58
  return [] unless node.is_a?(Parser::AST::Node)
56
59
 
57
- node.each_node(:str, :dstr, :xstr).filter_map { |n| inside_string_range(n) }
60
+ node.each_node(:any_str).filter_map { |n| inside_string_range(n) }
58
61
  end
59
62
 
60
63
  def inside_string_range(node)
@@ -6,7 +6,7 @@ module RuboCop
6
6
  class ForToEachCorrector
7
7
  extend NodePattern::Macros
8
8
 
9
- CORRECTION = '%<collection>s.each do |%<argument>s|'
9
+ CORRECTION = '%<collection>s%<dot>seach do |%<argument>s|'
10
10
 
11
11
  def initialize(for_node)
12
12
  @for_node = for_node
@@ -25,7 +25,12 @@ module RuboCop
25
25
  attr_reader :for_node, :variable_node, :collection_node
26
26
 
27
27
  def correction
28
- format(CORRECTION, collection: collection_source, argument: variable_node.source)
28
+ format(
29
+ CORRECTION,
30
+ collection: collection_source,
31
+ dot: collection_node.csend_type? ? '&.' : '.',
32
+ argument: variable_node.source
33
+ )
29
34
  end
30
35
 
31
36
  def collection_source
@@ -10,8 +10,11 @@ module RuboCop
10
10
  COMMA_REGEXP = /(?<=\))\s*,/.freeze
11
11
 
12
12
  def correct(corrector, node)
13
- corrector.remove(node.loc.begin)
14
- corrector.remove(node.loc.end)
13
+ buffer = node.source_range.source_buffer
14
+ corrector.remove(range_with_surrounding_space(range: node.loc.begin, buffer: buffer,
15
+ side: :right, whitespace: true))
16
+ corrector.remove(range_with_surrounding_space(range: node.loc.end, buffer: buffer,
17
+ side: :left))
15
18
  handle_orphaned_comma(corrector, node)
16
19
 
17
20
  return unless ternary_condition?(node) && next_char_is_question_mark?(node)
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gemspec
6
+ # Use consistent style for Gemspec attributes assignment.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # # This example uses two styles for assignment of metadata attribute.
12
+ # Gem::Specification.new do |spec|
13
+ # spec.metadata = { 'key' => 'value' }
14
+ # spec.metadata['another-key'] = 'another-value'
15
+ # end
16
+ #
17
+ # # good
18
+ # Gem::Specification.new do |spec|
19
+ # spec.metadata['key'] = 'value'
20
+ # spec.metadata['another-key'] = 'another-value'
21
+ # end
22
+ #
23
+ # # good
24
+ # Gem::Specification.new do |spec|
25
+ # spec.metadata = { 'key' => 'value', 'another-key' => 'another-value' }
26
+ # end
27
+ #
28
+ # # bad
29
+ # # This example uses two styles for assignment of authors attribute.
30
+ # Gem::Specification.new do |spec|
31
+ # spec.authors = %w[author-0 author-1]
32
+ # spec.authors[2] = 'author-2'
33
+ # end
34
+ #
35
+ # # good
36
+ # Gem::Specification.new do |spec|
37
+ # spec.authors = %w[author-0 author-1 author-2]
38
+ # end
39
+ #
40
+ # # good
41
+ # Gem::Specification.new do |spec|
42
+ # spec.authors[0] = 'author-0'
43
+ # spec.authors[1] = 'author-1'
44
+ # spec.authors[2] = 'author-2'
45
+ # end
46
+ #
47
+ # # good
48
+ # # This example uses consistent assignment per attribute,
49
+ # # even though two different styles are used overall.
50
+ # Gem::Specification.new do |spec|
51
+ # spec.metadata = { 'key' => 'value' }
52
+ # spec.authors[0] = 'author-0'
53
+ # spec.authors[1] = 'author-1'
54
+ # spec.authors[2] = 'author-2'
55
+ # end
56
+ #
57
+ class AttributeAssignment < Base
58
+ include GemspecHelp
59
+
60
+ MSG = 'Use consistent style for Gemspec attributes assignment.'
61
+
62
+ def on_new_investigation
63
+ return if processed_source.blank?
64
+
65
+ assignments = source_assignments(processed_source.ast)
66
+ indexed_assignments = source_indexed_assignments(processed_source.ast)
67
+
68
+ assignments.keys.intersection(indexed_assignments.keys).each do |attribute|
69
+ indexed_assignments[attribute].each do |node|
70
+ add_offense(node)
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def source_assignments(ast)
78
+ assignment_method_declarations(ast)
79
+ .select(&:assignment_method?)
80
+ .group_by(&:method_name)
81
+ .transform_keys { |method_name| method_name.to_s.delete_suffix('=').to_sym }
82
+ end
83
+
84
+ def source_indexed_assignments(ast)
85
+ indexed_assignment_method_declarations(ast)
86
+ .group_by { |node| node.children.first.method_name }
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -54,22 +54,6 @@ module RuboCop
54
54
  MSG = '`%<assignment>s` method calls already given on line ' \
55
55
  '%<line_of_first_occurrence>d of the gemspec.'
56
56
 
57
- # @!method assignment_method_declarations(node)
58
- def_node_search :assignment_method_declarations, <<~PATTERN
59
- (send
60
- (lvar #match_block_variable_name?) _ ...)
61
- PATTERN
62
-
63
- # @!method indexed_assignment_method_declarations(node)
64
- def_node_search :indexed_assignment_method_declarations, <<~PATTERN
65
- (send
66
- (send (lvar #match_block_variable_name?) _)
67
- :[]=
68
- literal?
69
- _
70
- )
71
- PATTERN
72
-
73
57
  def on_new_investigation
74
58
  return if processed_source.blank?
75
59
 
@@ -96,12 +80,6 @@ module RuboCop
96
80
  end
97
81
  end
98
82
 
99
- def match_block_variable_name?(receiver_name)
100
- gem_specification(processed_source.ast) do |block_variable_name|
101
- return block_variable_name == receiver_name
102
- end
103
- end
104
-
105
83
  def duplicated_assignment_method_nodes
106
84
  assignment_method_declarations(processed_source.ast)
107
85
  .select(&:assignment_method?)
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  dependency_declarations(processed_source.ast)
73
73
  .each_cons(2) do |previous, current|
74
- next unless consecutive_lines(previous, current)
74
+ next unless consecutive_lines?(previous, current)
75
75
  next unless case_insensitive_out_of_order?(gem_name(current), gem_name(previous))
76
76
  next unless get_dependency_name(previous) == get_dependency_name(current)
77
77
 
@@ -74,6 +74,14 @@ module RuboCop
74
74
  }
75
75
  PATTERN
76
76
 
77
+ # @!method metadata_assignment(node)
78
+ def_node_search :metadata_assignment, <<~PATTERN
79
+ `{
80
+ (send _ :metadata= _)
81
+ (send (send _ :metadata) :[]= (str _) _)
82
+ }
83
+ PATTERN
84
+
77
85
  # @!method rubygems_mfa_required(node)
78
86
  def_node_search :rubygems_mfa_required, <<~PATTERN
79
87
  (pair (str "rubygems_mfa_required") $_)
@@ -131,9 +139,15 @@ module RuboCop
131
139
  end
132
140
 
133
141
  def insert_mfa_required(corrector, node, block_var)
134
- corrector.insert_before(node.loc.end, <<~RUBY)
142
+ require_mfa_directive = <<~RUBY.strip
135
143
  #{block_var}.metadata['rubygems_mfa_required'] = 'true'
136
144
  RUBY
145
+
146
+ if (last_assignment = metadata_assignment(processed_source.ast).to_a.last)
147
+ corrector.insert_after(last_assignment, "\n#{require_mfa_directive}")
148
+ else
149
+ corrector.insert_before(node.loc.end, "#{require_mfa_directive}\n")
150
+ end
137
151
  end
138
152
 
139
153
  def change_value(corrector, value)
@@ -46,7 +46,7 @@ module RuboCop
46
46
  /\A(does not|doesn't) (register|find|flag|report)/ => 'registers',
47
47
  /\A(does not|doesn't) add (a|an|any )?offense/ => 'registers an offense',
48
48
  /\Aregisters no offense/ => 'registers an offense',
49
- /\A(accepts|register)\b/ => 'registers'
49
+ /\A(accepts|allows|register)\b/ => 'registers'
50
50
  }.freeze
51
51
 
52
52
  EXPECT_NO_CORRECTIONS_DESCRIPTION_MAPPING = {
@@ -110,8 +110,8 @@ module RuboCop
110
110
  def directive_offense_type(directive, actual_name)
111
111
  return :missing_directive unless directive
112
112
 
113
- return :wrong_scope if wrong_scope(directive, actual_name)
114
- return :no_scope if no_scope(directive, actual_name)
113
+ return :wrong_scope if wrong_scope?(directive, actual_name)
114
+ return :no_scope if no_scope?(directive, actual_name)
115
115
 
116
116
  # The method directive being prefixed by 'self.' is always an offense.
117
117
  # The matched method_name does not contain the receiver but the
@@ -121,11 +121,11 @@ module RuboCop
121
121
  end
122
122
  end
123
123
 
124
- def wrong_scope(directive, actual_name)
124
+ def wrong_scope?(directive, actual_name)
125
125
  !actual_name.start_with?('self.') && directive[:has_scope_directive]
126
126
  end
127
127
 
128
- def no_scope(directive, actual_name)
128
+ def no_scope?(directive, actual_name)
129
129
  actual_name.start_with?('self.') && !directive[:has_scope_directive]
130
130
  end
131
131
 
@@ -29,6 +29,9 @@ module RuboCop
29
29
  NODE_GROUPS = {
30
30
  any_block: %i[block numblock itblock],
31
31
  any_def: %i[def defs],
32
+ any_match_pattern: %i[match_pattern match_pattern_p],
33
+ any_str: %i[str dstr xstr],
34
+ any_sym: %i[sym dsym],
32
35
  argument: %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg forward_arg shadowarg],
33
36
  boolean: %i[true false],
34
37
  call: %i[send csend],
@@ -209,7 +212,7 @@ module RuboCop
209
212
  # A heredoc can be a `dstr` without interpolation, but if there is interpolation
210
213
  # there'll be a `begin` node, in which case, we cannot evaluate the pattern.
211
214
  def acceptable_heredoc?(node)
212
- node.type?(:str, :dstr) && node.heredoc? && node.each_child_node(:begin).none?
215
+ node.any_str_type? && node.heredoc? && node.each_child_node(:begin).none?
213
216
  end
214
217
 
215
218
  def process_pattern(pattern_node)
@@ -66,8 +66,9 @@ module RuboCop
66
66
  end
67
67
 
68
68
  def autocorrect_to_explicit_predicate(corrector, node, group_name)
69
- corrector.replace(node.selector, "#{group_name}_type?")
70
- corrector.remove(arguments_range(node))
69
+ range = node.loc.selector.begin.join(node.source_range.end)
70
+
71
+ corrector.replace(range, "#{group_name}_type?")
71
72
  end
72
73
 
73
74
  def autocorrect_keep_method(corrector, symbol_args, group_name, group_types)
@@ -76,7 +76,7 @@ module RuboCop
76
76
  end
77
77
 
78
78
  def on_send(node) # rubocop:disable InternalAffairs/OnSendWithoutOnCSend
79
- new_identifier = node.first_argument
79
+ return unless (new_identifier = node.first_argument)
80
80
  return unless new_identifier.basic_literal?
81
81
 
82
82
  new_identifier = new_identifier.value
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module InternalAffairs
6
- # Check for useless `RESTRICT_ON_SEND`.
6
+ # Checks for useless `RESTRICT_ON_SEND`.
7
7
  #
8
8
  # @example
9
9
  # # bad
@@ -357,7 +357,7 @@ module RuboCop
357
357
  end
358
358
 
359
359
  def find_heredoc(node)
360
- node.each_node(:str, :dstr, :xstr).find(&:heredoc?)
360
+ node.each_node(:any_str).find(&:heredoc?)
361
361
  end
362
362
 
363
363
  def buffer
@@ -161,7 +161,7 @@ module RuboCop
161
161
  elements.flat_map do |e|
162
162
  e.loc.column
163
163
  end
164
- end.uniq.count == 1
164
+ end.uniq.one?
165
165
  end
166
166
 
167
167
  def first_argument_line(elements)
@@ -120,7 +120,7 @@ module RuboCop
120
120
  end
121
121
 
122
122
  def heredoc?(node)
123
- node.type?(:str, :dstr) && node.heredoc?
123
+ node.any_str_type? && node.heredoc?
124
124
  end
125
125
 
126
126
  def end_range(node)
@@ -76,28 +76,40 @@ module RuboCop
76
76
  # # good
77
77
  # class ErrorA < BaseError; end
78
78
  # class ErrorB < BaseError; end
79
- # class ErrorC < BaseError; end
80
79
  #
81
80
  # # good
82
81
  # class ErrorA < BaseError; end
83
82
  #
84
83
  # class ErrorB < BaseError; end
85
84
  #
86
- # class ErrorC < BaseError; end
85
+ # # good - DefLikeMacros: [memoize]
86
+ # memoize :attribute_a
87
+ # memoize :attribute_b
88
+ #
89
+ # # good
90
+ # memoize :attribute_a
91
+ #
92
+ # memoize :attribute_b
87
93
  #
88
94
  # @example AllowAdjacentOneLineDefs: false
89
95
  #
90
96
  # # bad
91
97
  # class ErrorA < BaseError; end
92
98
  # class ErrorB < BaseError; end
93
- # class ErrorC < BaseError; end
94
99
  #
95
100
  # # good
96
101
  # class ErrorA < BaseError; end
97
102
  #
98
103
  # class ErrorB < BaseError; end
99
104
  #
100
- # class ErrorC < BaseError; end
105
+ # # bad - DefLikeMacros: [memoize]
106
+ # memoize :attribute_a
107
+ # memoize :attribute_b
108
+ #
109
+ # # good
110
+ # memoize :attribute_a
111
+ #
112
+ # memoize :attribute_b
101
113
  #
102
114
  class EmptyLineBetweenDefs < Base
103
115
  include RangeHelp
@@ -158,6 +170,8 @@ module RuboCop
158
170
  def def_location(correction_node)
159
171
  if correction_node.any_block_type?
160
172
  correction_node.source_range.join(correction_node.children.first.source_range)
173
+ elsif correction_node.send_type?
174
+ correction_node.source_range
161
175
  else
162
176
  correction_node.loc.keyword.join(correction_node.loc.name)
163
177
  end
@@ -175,8 +189,14 @@ module RuboCop
175
189
  end
176
190
 
177
191
  def macro_candidate?(node)
178
- node.any_block_type? && node.children.first.macro? &&
179
- empty_line_between_macros.include?(node.children.first.method_name)
192
+ macro_candidate = if node.any_block_type?
193
+ node.send_node
194
+ elsif node.send_type?
195
+ node
196
+ end
197
+ return false unless macro_candidate
198
+
199
+ macro_candidate.macro? && empty_line_between_macros.include?(macro_candidate.method_name)
180
200
  end
181
201
 
182
202
  def method_candidate?(node)
@@ -240,7 +260,9 @@ module RuboCop
240
260
  end
241
261
 
242
262
  def def_start(node)
243
- if node.any_block_type? && node.children.first.send_type?
263
+ node = node.send_node if node.any_block_type?
264
+
265
+ if node.send_type?
244
266
  node.source_range.line
245
267
  else
246
268
  node.loc.keyword.line
@@ -252,11 +274,7 @@ module RuboCop
252
274
  end
253
275
 
254
276
  def end_loc(node)
255
- if node.any_def_type? && node.endless?
256
- node.source_range.end
257
- else
258
- node.loc.end
259
- end
277
+ node.source_range.end
260
278
  end
261
279
 
262
280
  def autocorrect_remove_lines(corrector, newline_pos, count)
@@ -0,0 +1,101 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Layout
6
+ # Checks for an empty line after a module inclusion method (`extend`,
7
+ # `include` and `prepend`), or a group of them.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # class Foo
12
+ # include Bar
13
+ # attr_reader :baz
14
+ # end
15
+ #
16
+ # # good
17
+ # class Foo
18
+ # include Bar
19
+ #
20
+ # attr_reader :baz
21
+ # end
22
+ #
23
+ # # also good - multiple module inclusions grouped together
24
+ # class Foo
25
+ # extend Bar
26
+ # include Baz
27
+ # prepend Qux
28
+ # end
29
+ #
30
+ class EmptyLinesAfterModuleInclusion < Base
31
+ include RangeHelp
32
+ extend AutoCorrector
33
+
34
+ MSG = 'Add an empty line after module inclusion.'
35
+
36
+ MODULE_INCLUSION_METHODS = %i[include extend prepend].freeze
37
+
38
+ RESTRICT_ON_SEND = MODULE_INCLUSION_METHODS
39
+
40
+ def on_send(node)
41
+ return if node.receiver || node.arguments.empty?
42
+ return if node.parent&.type?(:send, :any_block)
43
+
44
+ return if next_line_empty_or_enable_directive_comment?(node.last_line)
45
+
46
+ next_line_node = next_line_node(node)
47
+ return unless require_empty_line?(next_line_node)
48
+
49
+ add_offense(node) { |corrector| autocorrect(corrector, node) }
50
+ end
51
+
52
+ private
53
+
54
+ def autocorrect(corrector, node)
55
+ node_range = range_by_whole_lines(node.source_range)
56
+
57
+ next_line = node_range.last_line + 1
58
+ if enable_directive_comment?(next_line)
59
+ node_range = processed_source.comment_at_line(next_line)
60
+ end
61
+
62
+ corrector.insert_after(node_range, "\n")
63
+ end
64
+
65
+ def next_line_empty_or_enable_directive_comment?(line)
66
+ line_empty?(line) || (enable_directive_comment?(line + 1) && line_empty?(line + 1))
67
+ end
68
+
69
+ def enable_directive_comment?(line)
70
+ return false unless (comment = processed_source.comment_at_line(line))
71
+
72
+ DirectiveComment.new(comment).enabled?
73
+ end
74
+
75
+ def line_empty?(line)
76
+ processed_source[line].nil? || processed_source[line].blank?
77
+ end
78
+
79
+ def require_empty_line?(node)
80
+ return false unless node
81
+
82
+ !allowed_method?(node)
83
+ end
84
+
85
+ def allowed_method?(node)
86
+ node = node.body if node.respond_to?(:modifier_form?) && node.modifier_form?
87
+
88
+ return false unless node.send_type?
89
+
90
+ MODULE_INCLUSION_METHODS.include?(node.method_name)
91
+ end
92
+
93
+ def next_line_node(node)
94
+ return if node.parent.if_type?
95
+
96
+ node.right_sibling
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end