rubocop 1.32.0 → 1.37.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +2 -2
  3. data/config/default.yml +104 -16
  4. data/config/obsoletion.yml +23 -1
  5. data/lib/rubocop/arguments_env.rb +17 -0
  6. data/lib/rubocop/arguments_file.rb +17 -0
  7. data/lib/rubocop/cache_config.rb +29 -0
  8. data/lib/rubocop/cli/command/{auto_genenerate_config.rb → auto_generate_config.rb} +2 -2
  9. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  10. data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
  11. data/lib/rubocop/cli/command/suggest_extensions.rb +53 -15
  12. data/lib/rubocop/config.rb +1 -1
  13. data/lib/rubocop/config_finder.rb +68 -0
  14. data/lib/rubocop/config_loader.rb +12 -40
  15. data/lib/rubocop/config_loader_resolver.rb +1 -5
  16. data/lib/rubocop/config_obsoletion/changed_parameter.rb +5 -0
  17. data/lib/rubocop/config_obsoletion/parameter_rule.rb +4 -0
  18. data/lib/rubocop/config_obsoletion.rb +7 -2
  19. data/lib/rubocop/cop/cop.rb +1 -1
  20. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +58 -0
  21. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  22. data/lib/rubocop/cop/generator/require_file_injector.rb +2 -2
  23. data/lib/rubocop/cop/generator.rb +1 -2
  24. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +69 -0
  25. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +62 -0
  26. data/lib/rubocop/cop/internal_affairs.rb +2 -0
  27. data/lib/rubocop/cop/layout/block_alignment.rb +16 -12
  28. data/lib/rubocop/cop/layout/block_end_newline.rb +35 -5
  29. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +5 -2
  30. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +2 -0
  31. data/lib/rubocop/cop/layout/end_of_line.rb +4 -4
  32. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -1
  33. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
  34. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -2
  35. data/lib/rubocop/cop/layout/indentation_width.rb +6 -2
  36. data/lib/rubocop/cop/layout/line_length.rb +4 -1
  37. data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -1
  38. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  39. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  40. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  41. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  42. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  43. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  44. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +25 -9
  45. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +28 -3
  46. data/lib/rubocop/cop/legacy/corrections_proxy.rb +1 -1
  47. data/lib/rubocop/cop/legacy/corrector.rb +1 -1
  48. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +21 -8
  49. data/lib/rubocop/cop/lint/debugger.rb +26 -16
  50. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  51. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  52. data/lib/rubocop/cop/lint/duplicate_methods.rb +11 -1
  53. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  54. data/lib/rubocop/cop/lint/duplicate_require.rb +1 -1
  55. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  56. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  57. data/lib/rubocop/cop/lint/empty_conditional_body.rb +107 -1
  58. data/lib/rubocop/cop/lint/erb_new_arguments.rb +9 -9
  59. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
  60. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  61. data/lib/rubocop/cop/lint/next_without_accumulator.rb +25 -6
  62. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +6 -6
  63. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +12 -0
  64. data/lib/rubocop/cop/lint/number_conversion.rb +24 -8
  65. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  66. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  67. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +12 -1
  68. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  69. data/lib/rubocop/cop/lint/redundant_require_statement.rb +29 -9
  70. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +9 -3
  71. data/lib/rubocop/cop/lint/redundant_with_index.rb +13 -10
  72. data/lib/rubocop/cop/lint/redundant_with_object.rb +12 -11
  73. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  74. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +3 -2
  75. data/lib/rubocop/cop/lint/shadowed_exception.rb +15 -10
  76. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +27 -3
  77. data/lib/rubocop/cop/lint/unreachable_loop.rb +9 -3
  78. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  79. data/lib/rubocop/cop/lint/useless_access_modifier.rb +8 -6
  80. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  81. data/lib/rubocop/cop/lint/void.rb +2 -0
  82. data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
  83. data/lib/rubocop/cop/metrics/block_length.rb +6 -7
  84. data/lib/rubocop/cop/metrics/method_length.rb +8 -8
  85. data/lib/rubocop/cop/mixin/allowed_methods.rb +20 -1
  86. data/lib/rubocop/cop/mixin/allowed_pattern.rb +17 -1
  87. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  88. data/lib/rubocop/cop/mixin/comments_help.rb +17 -1
  89. data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -1
  90. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  91. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +82 -4
  92. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -6
  93. data/lib/rubocop/cop/mixin/method_complexity.rb +8 -13
  94. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  95. data/lib/rubocop/cop/mixin/range_help.rb +4 -5
  96. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  97. data/lib/rubocop/cop/mixin/surrounding_space.rb +6 -5
  98. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  99. data/lib/rubocop/cop/naming/constant_name.rb +2 -2
  100. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  101. data/lib/rubocop/cop/naming/predicate_name.rb +24 -3
  102. data/lib/rubocop/cop/style/access_modifier_declarations.rb +97 -1
  103. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  104. data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -2
  105. data/lib/rubocop/cop/style/block_delimiters.rb +26 -7
  106. data/lib/rubocop/cop/style/case_equality.rb +40 -10
  107. data/lib/rubocop/cop/style/class_and_module_children.rb +4 -4
  108. data/lib/rubocop/cop/style/class_equality_comparison.rb +32 -7
  109. data/lib/rubocop/cop/style/class_methods_definitions.rb +2 -1
  110. data/lib/rubocop/cop/style/collection_compact.rb +6 -1
  111. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  112. data/lib/rubocop/cop/style/combinable_loops.rb +3 -1
  113. data/lib/rubocop/cop/style/double_negation.rb +2 -0
  114. data/lib/rubocop/cop/style/each_for_simple_loop.rb +41 -6
  115. data/lib/rubocop/cop/style/each_with_object.rb +39 -8
  116. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  117. data/lib/rubocop/cop/style/empty_heredoc.rb +15 -1
  118. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  119. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  120. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  121. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  122. data/lib/rubocop/cop/style/for.rb +2 -0
  123. data/lib/rubocop/cop/style/format_string_token.rb +21 -8
  124. data/lib/rubocop/cop/style/guard_clause.rb +27 -16
  125. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -1
  126. data/lib/rubocop/cop/style/hash_except.rb +0 -4
  127. data/lib/rubocop/cop/style/hash_syntax.rb +17 -0
  128. data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
  129. data/lib/rubocop/cop/style/inverse_methods.rb +8 -6
  130. data/lib/rubocop/cop/style/magic_comment_format.rb +307 -0
  131. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +15 -4
  132. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -1
  133. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -7
  134. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -6
  135. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +4 -1
  136. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -1
  137. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  138. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  139. data/lib/rubocop/cop/style/next.rb +3 -5
  140. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  141. data/lib/rubocop/cop/style/numeric_literals.rb +16 -1
  142. data/lib/rubocop/cop/style/numeric_predicate.rb +28 -8
  143. data/lib/rubocop/cop/style/object_then.rb +2 -0
  144. data/lib/rubocop/cop/style/operator_method_call.rb +39 -0
  145. data/lib/rubocop/cop/style/perl_backrefs.rb +22 -1
  146. data/lib/rubocop/cop/style/proc.rb +4 -1
  147. data/lib/rubocop/cop/style/redundant_begin.rb +3 -0
  148. data/lib/rubocop/cop/style/redundant_condition.rb +24 -6
  149. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  150. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  151. data/lib/rubocop/cop/style/redundant_parentheses.rb +19 -22
  152. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  153. data/lib/rubocop/cop/style/redundant_self.rb +2 -0
  154. data/lib/rubocop/cop/style/redundant_sort.rb +21 -6
  155. data/lib/rubocop/cop/style/redundant_sort_by.rb +24 -8
  156. data/lib/rubocop/cop/style/redundant_string_escape.rb +173 -0
  157. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  158. data/lib/rubocop/cop/style/safe_navigation.rb +4 -2
  159. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  160. data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -5
  161. data/lib/rubocop/cop/style/static_class.rb +32 -1
  162. data/lib/rubocop/cop/style/symbol_array.rb +3 -1
  163. data/lib/rubocop/cop/style/symbol_proc.rb +38 -12
  164. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -13
  165. data/lib/rubocop/cop/style/top_level_method_definition.rb +3 -1
  166. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  167. data/lib/rubocop/cop/style/word_array.rb +3 -1
  168. data/lib/rubocop/cop/util.rb +1 -1
  169. data/lib/rubocop/ext/range.rb +15 -0
  170. data/lib/rubocop/feature_loader.rb +94 -0
  171. data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
  172. data/lib/rubocop/formatter/disabled_config_formatter.rb +9 -3
  173. data/lib/rubocop/formatter/html_formatter.rb +3 -3
  174. data/lib/rubocop/formatter/markdown_formatter.rb +1 -1
  175. data/lib/rubocop/formatter/tap_formatter.rb +1 -1
  176. data/lib/rubocop/options.rb +13 -13
  177. data/lib/rubocop/result_cache.rb +22 -20
  178. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  179. data/lib/rubocop/runner.rb +4 -0
  180. data/lib/rubocop/server/cache.rb +41 -2
  181. data/lib/rubocop/server/cli.rb +26 -2
  182. data/lib/rubocop/server/client_command/exec.rb +5 -0
  183. data/lib/rubocop/server/core.rb +2 -1
  184. data/lib/rubocop/server/socket_reader.rb +5 -1
  185. data/lib/rubocop/server.rb +1 -1
  186. data/lib/rubocop/version.rb +8 -2
  187. data/lib/rubocop.rb +8 -3
  188. metadata +20 -9
  189. data/lib/rubocop/cop/mixin/ignored_methods.rb +0 -52
@@ -3,6 +3,7 @@
3
3
  require 'erb'
4
4
  require 'yaml'
5
5
  require 'pathname'
6
+ require_relative 'config_finder'
6
7
 
7
8
  module RuboCop
8
9
  # Raised when a RuboCop configuration file is not found.
@@ -15,8 +16,7 @@ module RuboCop
15
16
  # during a run of the rubocop program, if files in several
16
17
  # directories are inspected.
17
18
  class ConfigLoader
18
- DOTFILE = '.rubocop.yml'
19
- XDG_CONFIG = 'config.yml'
19
+ DOTFILE = ConfigFinder::DOTFILE
20
20
  RUBOCOP_HOME = File.realpath(File.join(File.dirname(__FILE__), '..', '..'))
21
21
  DEFAULT_FILE = File.join(RUBOCOP_HOME, 'config', 'default.yml')
22
22
 
@@ -25,7 +25,7 @@ module RuboCop
25
25
 
26
26
  attr_accessor :debug, :ignore_parent_exclusion, :disable_pending_cops, :enable_pending_cops,
27
27
  :ignore_unrecognized_cops
28
- attr_writer :default_configuration, :project_root
28
+ attr_writer :default_configuration
29
29
  attr_reader :loaded_features
30
30
 
31
31
  alias debug? debug
@@ -95,8 +95,7 @@ module RuboCop
95
95
  # user's home directory is checked. If there's no .rubocop.yml
96
96
  # there either, the path to the default file is returned.
97
97
  def configuration_file_for(target_dir)
98
- find_project_dotfile(target_dir) || find_user_dotfile ||
99
- find_user_xdg_config || DEFAULT_FILE
98
+ ConfigFinder.find_config_path(target_dir)
100
99
  end
101
100
 
102
101
  def configuration_from_file(config_file, check: true)
@@ -122,7 +121,7 @@ module RuboCop
122
121
  end
123
122
 
124
123
  def add_excludes_from_files(config, config_file)
125
- exclusion_file = find_last_file_upwards(DOTFILE, config_file, project_root)
124
+ exclusion_file = find_last_file_upwards(DOTFILE, config_file, ConfigFinder.project_root)
126
125
 
127
126
  return unless exclusion_file
128
127
  return if PathUtil.relative_path(exclusion_file) == PathUtil.relative_path(config_file)
@@ -140,8 +139,14 @@ module RuboCop
140
139
 
141
140
  # Returns the path RuboCop inferred as the root of the project. No file
142
141
  # searches will go past this directory.
142
+ # @deprecated Use `RuboCop::ConfigFinder.project_root` instead.
143
143
  def project_root
144
- @project_root ||= find_project_root
144
+ warn Rainbow(<<~WARNING).yellow
145
+ `RuboCop::ConfigLoader.project_root` is deprecated and will be removed in RuboCop 2.0. \
146
+ Use `RuboCop::ConfigFinder.project_root` instead.
147
+ WARNING
148
+
149
+ ConfigFinder.project_root
145
150
  end
146
151
 
147
152
  PENDING_BANNER = <<~BANNER
@@ -187,39 +192,6 @@ module RuboCop
187
192
  File.absolute_path(file.is_a?(RemoteConfig) ? file.file : file)
188
193
  end
189
194
 
190
- def find_project_dotfile(target_dir)
191
- find_file_upwards(DOTFILE, target_dir, project_root)
192
- end
193
-
194
- def find_project_root
195
- pwd = Dir.pwd
196
- gems_file = find_last_file_upwards('Gemfile', pwd) || find_last_file_upwards('gems.rb', pwd)
197
- return unless gems_file
198
-
199
- File.dirname(gems_file)
200
- end
201
-
202
- def find_user_dotfile
203
- return unless ENV.key?('HOME')
204
-
205
- file = File.join(Dir.home, DOTFILE)
206
- return file if File.exist?(file)
207
- end
208
-
209
- def find_user_xdg_config
210
- xdg_config_home = expand_path(ENV.fetch('XDG_CONFIG_HOME', '~/.config'))
211
- xdg_config = File.join(xdg_config_home, 'rubocop', XDG_CONFIG)
212
- return xdg_config if File.exist?(xdg_config)
213
- end
214
-
215
- def expand_path(path)
216
- File.expand_path(path)
217
- rescue ArgumentError
218
- # Could happen because HOME or ID could not be determined. Fall back on
219
- # using the path literally in that case.
220
- path
221
- end
222
-
223
195
  def resolver
224
196
  @resolver ||= ConfigLoaderResolver.new
225
197
  end
@@ -11,11 +11,7 @@ module RuboCop
11
11
  config_dir = File.dirname(path)
12
12
  hash.delete('require').tap do |loaded_features|
13
13
  Array(loaded_features).each do |feature|
14
- if feature.start_with?('.')
15
- require(File.join(config_dir, feature))
16
- else
17
- require(feature)
18
- end
14
+ FeatureLoader.load(config_directory_path: config_dir, feature: feature)
19
15
  end
20
16
  end
21
17
  end
@@ -12,6 +12,11 @@ module RuboCop
12
12
 
13
13
  if alternative
14
14
  "#{base}\n`#{parameter}` has been renamed to `#{alternative.chomp}`."
15
+ elsif alternatives
16
+ "#{base}\n`#{parameter}` has been renamed to #{to_sentence(alternatives.map do |item|
17
+ "`#{item}`"
18
+ end,
19
+ connector: 'and/or')}."
15
20
  else
16
21
  "#{base}\n#{reason.chomp}"
17
22
  end
@@ -32,6 +32,10 @@ module RuboCop
32
32
  metadata['alternative']
33
33
  end
34
34
 
35
+ def alternatives
36
+ metadata['alternatives']
37
+ end
38
+
35
39
  def reason
36
40
  metadata['reason']
37
41
  end
@@ -47,10 +47,15 @@ module RuboCop
47
47
 
48
48
  # Default rules for obsoletions are in config/obsoletion.yml
49
49
  # Additional rules files can be added with `RuboCop::ConfigObsoletion.files << filename`
50
- def load_rules
50
+ def load_rules # rubocop:disable Metrics/AbcSize
51
51
  rules = self.class.files.each_with_object({}) do |filename, hash|
52
52
  hash.merge!(YAML.safe_load(File.read(filename))) do |_key, first, second|
53
- first.merge(second)
53
+ case first
54
+ when Hash
55
+ first.merge(second)
56
+ when Array
57
+ first.concat(second)
58
+ end
54
59
  end
55
60
  end
56
61
 
@@ -7,7 +7,7 @@ module RuboCop
7
7
  module Cop
8
8
  # @deprecated Use Cop::Base instead
9
9
  # Legacy scaffold for Cops.
10
- # See https://docs.rubocop.org/rubocop/cop_api_v1_changelog.html
10
+ # See https://docs.rubocop.org/rubocop/v1_upgrade_notes.html
11
11
  class Cop < Base
12
12
  attr_reader :offenses
13
13
 
@@ -5,9 +5,15 @@ module RuboCop
5
5
  # This autocorrects parentheses
6
6
  class ParenthesesCorrector
7
7
  class << self
8
+ include RangeHelp
9
+
10
+ COMMA_REGEXP = /(?<=\))\s*,/.freeze
11
+
8
12
  def correct(corrector, node)
9
13
  corrector.remove(node.loc.begin)
10
14
  corrector.remove(node.loc.end)
15
+ handle_orphaned_comma(corrector, node)
16
+
11
17
  return unless ternary_condition?(node) && next_char_is_question_mark?(node)
12
18
 
13
19
  corrector.insert_after(node.loc.end, ' ')
@@ -22,6 +28,58 @@ module RuboCop
22
28
  def next_char_is_question_mark?(node)
23
29
  node.loc.last_column == node.parent.loc.question.column
24
30
  end
31
+
32
+ def only_closing_paren_before_comma?(node)
33
+ source_buffer = node.source_range.source_buffer
34
+ line_range = source_buffer.line_range(node.loc.end.line)
35
+
36
+ line_range.source.start_with?(/\s*\)\s*,/)
37
+ end
38
+
39
+ # If removing parentheses leaves a comma on its own line, remove all the whitespace
40
+ # preceding it to prevent a syntax error.
41
+ def handle_orphaned_comma(corrector, node)
42
+ return unless only_closing_paren_before_comma?(node)
43
+
44
+ range = extend_range_for_heredoc(node, parens_range(node))
45
+ corrector.remove(range)
46
+
47
+ add_heredoc_comma(corrector, node)
48
+ end
49
+
50
+ # Get a range for the closing parenthesis and all whitespace to the left of it
51
+ def parens_range(node)
52
+ range_with_surrounding_space(
53
+ range: node.loc.end,
54
+ buffer: node.source_range.source_buffer,
55
+ side: :left,
56
+ newlines: true,
57
+ whitespace: true,
58
+ continuations: true
59
+ )
60
+ end
61
+
62
+ # If the node contains a heredoc, remove the comma too
63
+ # It'll be added back in the right place later
64
+ def extend_range_for_heredoc(node, range)
65
+ return range unless heredoc?(node)
66
+
67
+ comma_line = range_by_whole_lines(node.loc.end, buffer: node.source_range.source_buffer)
68
+ offset = comma_line.source.match(COMMA_REGEXP)[0]&.size || 0
69
+
70
+ range.adjust(end_pos: offset)
71
+ end
72
+
73
+ # Add a comma back after the heredoc identifier
74
+ def add_heredoc_comma(corrector, node)
75
+ return unless heredoc?(node)
76
+
77
+ corrector.insert_after(node.child_nodes.last.loc.expression, ',')
78
+ end
79
+
80
+ def heredoc?(node)
81
+ node.child_nodes.last.loc.is_a?(Parser::Source::Map::Heredoc)
82
+ end
25
83
  end
26
84
  end
27
85
  end
@@ -84,7 +84,7 @@ module RuboCop
84
84
  (str "true")
85
85
  PATTERN
86
86
 
87
- def on_block(node) # rubocop:disable Metrics/MethodLength
87
+ def on_block(node) # rubocop:disable Metrics/MethodLength, InternalAffairs/NumblockHandler
88
88
  gem_specification(node) do |block_var|
89
89
  metadata_value = metadata(node)
90
90
  mfa_value = mfa_value(metadata_value)
@@ -55,8 +55,8 @@ module RuboCop
55
55
  end
56
56
  end
57
57
 
58
- def require_path_fragments(require_directove)
59
- path = require_directove.match(REQUIRE_PATH)
58
+ def require_path_fragments(require_directive)
59
+ path = require_directive.match(REQUIRE_PATH)
60
60
 
61
61
  path ? path.captures.first.split('/') : []
62
62
  end
@@ -206,9 +206,8 @@ module RuboCop
206
206
  end
207
207
 
208
208
  def snake_case(camel_case_string)
209
- return 'rspec' if camel_case_string == 'RSpec'
210
-
211
209
  camel_case_string
210
+ .gsub('RSpec', 'Rspec')
212
211
  .gsub(%r{([^A-Z/])([A-Z]+)}, '\1_\2')
213
212
  .gsub(%r{([A-Z])([A-Z][^A-Z\d/]+)}, '\1_\2')
214
213
  .downcase
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Checks for missing `numblock handlers. The blocks with numbered
7
+ # arguments introduced in Ruby 2.7 are parsed with a node type of
8
+ # `numblock` instead of block. Cops that define `block` handlers
9
+ # need to define `numblock` handlers or disable this cope for them.
10
+ #
11
+ # @example
12
+ #
13
+ # # bad
14
+ # class BlockRelatedCop < Base
15
+ # def on_block(node)
16
+ # end
17
+ # end
18
+ #
19
+ # # good
20
+ # class BlockRelatedCop < Base
21
+ # def on_block(node)
22
+ # end
23
+ #
24
+ # alias on_numblock on_block
25
+ # end
26
+ #
27
+ # class BlockRelatedCop < Base
28
+ # def on_block(node)
29
+ # end
30
+ #
31
+ # alias_method :on_numblock, :on_block
32
+ # end
33
+ #
34
+ # class BlockRelatedCop < Base
35
+ # def on_block(node)
36
+ # end
37
+ #
38
+ # def on_numblock(node)
39
+ # end
40
+ # end
41
+ class NumblockHandler < Base
42
+ MSG = 'Define on_numblock to handle blocks with numbered arguments.'
43
+
44
+ def on_def(node)
45
+ return unless block_handler?(node)
46
+ return unless node.parent
47
+
48
+ add_offense(node) unless numblock_handler?(node.parent)
49
+ end
50
+
51
+ private
52
+
53
+ # @!method block_handler?(node)
54
+ def_node_matcher :block_handler?, <<~PATTERN
55
+ (def :on_block (args (arg :node)) ...)
56
+ PATTERN
57
+
58
+ # @!method numblock_handler?(node)
59
+ def_node_matcher :numblock_handler?, <<~PATTERN
60
+ {
61
+ `(def :on_numblock (args (arg :node)) ...)
62
+ `(alias (sym :on_numblock) (sym :on_block))
63
+ `(send nil? :alias_method (sym :on_numblock) (sym :on_block))
64
+ }
65
+ PATTERN
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,62 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Enforces the use of `node.single_line?` instead of
7
+ # comparing `first_line` and `last_line` for equality.
8
+ #
9
+ # @example
10
+ # # bad
11
+ # node.loc.first_line == node.loc.last_line
12
+ #
13
+ # # bad
14
+ # node.loc.last_line == node.loc.first_line
15
+ #
16
+ # # bad
17
+ # node.loc.line == node.loc.last_line
18
+ #
19
+ # # bad
20
+ # node.loc.last_line == node.loc.line
21
+ #
22
+ # # bad
23
+ # node.first_line == node.last_line
24
+ #
25
+ # # good
26
+ # node.single_line?
27
+ #
28
+ class SingleLineComparison < Base
29
+ extend AutoCorrector
30
+
31
+ MSG = 'Use `%<preferred>s`.'
32
+ RESTRICT_ON_SEND = %i[== !=].freeze
33
+
34
+ # @!method single_line_comparison(node)
35
+ def_node_matcher :single_line_comparison, <<~PATTERN
36
+ {
37
+ (send (send $_receiver {:line :first_line}) {:== :!=} (send _receiver :last_line))
38
+ (send (send $_receiver :last_line) {:== :!=} (send _receiver {:line :first_line}))
39
+ }
40
+ PATTERN
41
+
42
+ def on_send(node)
43
+ return unless (receiver = single_line_comparison(node))
44
+
45
+ bang = node.method?(:!=) ? '!' : ''
46
+ preferred = "#{bang}#{extract_receiver(receiver)}.single_line?"
47
+
48
+ add_offense(node, message: format(MSG, preferred: preferred)) do |corrector|
49
+ corrector.replace(node, preferred)
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def extract_receiver(node)
56
+ node = node.receiver if node.send_type? && %i[loc source_range].include?(node.method_name)
57
+ node.source
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -10,6 +10,7 @@ require_relative 'internal_affairs/method_name_equal'
10
10
  require_relative 'internal_affairs/node_destructuring'
11
11
  require_relative 'internal_affairs/node_matcher_directive'
12
12
  require_relative 'internal_affairs/node_type_predicate'
13
+ require_relative 'internal_affairs/numblock_handler'
13
14
  require_relative 'internal_affairs/offense_location_keyword'
14
15
  require_relative 'internal_affairs/redundant_context_config_parameter'
15
16
  require_relative 'internal_affairs/redundant_described_class_as_subject'
@@ -17,6 +18,7 @@ require_relative 'internal_affairs/redundant_let_rubocop_config_new'
17
18
  require_relative 'internal_affairs/redundant_location_argument'
18
19
  require_relative 'internal_affairs/redundant_message_argument'
19
20
  require_relative 'internal_affairs/redundant_method_dispatch_node'
21
+ require_relative 'internal_affairs/single_line_comparison'
20
22
  require_relative 'internal_affairs/style_detected_api_use'
21
23
  require_relative 'internal_affairs/undefined_config'
22
24
  require_relative 'internal_affairs/useless_message_assertion'
@@ -22,23 +22,24 @@ module RuboCop
22
22
  # # bad
23
23
  #
24
24
  # foo.bar
25
- # .each do
26
- # baz
27
- # end
25
+ # .each do
26
+ # baz
27
+ # end
28
28
  #
29
29
  # # good
30
30
  #
31
- # variable = lambda do |i|
32
- # i
31
+ # foo.bar
32
+ # .each do
33
+ # baz
33
34
  # end
34
35
  #
35
36
  # @example EnforcedStyleAlignWith: start_of_block
36
37
  # # bad
37
38
  #
38
39
  # foo.bar
39
- # .each do
40
- # baz
41
- # end
40
+ # .each do
41
+ # baz
42
+ # end
42
43
  #
43
44
  # # good
44
45
  #
@@ -51,16 +52,17 @@ module RuboCop
51
52
  # # bad
52
53
  #
53
54
  # foo.bar
54
- # .each do
55
- # baz
56
- # end
55
+ # .each do
56
+ # baz
57
+ # end
57
58
  #
58
59
  # # good
59
60
  #
60
61
  # foo.bar
61
62
  # .each do
62
- # baz
63
+ # baz
63
64
  # end
65
+ #
64
66
  class BlockAlignment < Base
65
67
  include ConfigurableEnforcedStyle
66
68
  include RangeHelp
@@ -82,6 +84,8 @@ module RuboCop
82
84
  check_block_alignment(start_for_block_node(node), node)
83
85
  end
84
86
 
87
+ alias on_numblock on_block
88
+
85
89
  def style_parameter_name
86
90
  'EnforcedStyleAlignWith'
87
91
  end
@@ -36,24 +36,54 @@ module RuboCop
36
36
  # If the end is on its own line, there is no offense
37
37
  return if begins_its_line?(node.loc.end)
38
38
 
39
- add_offense(node.loc.end, message: message(node)) do |corrector|
40
- corrector.replace(delimiter_range(node), "\n#{node.loc.end.source}#{offset(node)}")
41
- end
39
+ register_offense(node)
42
40
  end
43
41
 
42
+ alias on_numblock on_block
43
+
44
44
  private
45
45
 
46
+ def register_offense(node)
47
+ add_offense(node.loc.end, message: message(node)) do |corrector|
48
+ offense_range = offense_range(node)
49
+ replacement = "\n#{offense_range.source.strip}"
50
+
51
+ if (heredoc = last_heredoc_argument(node.body))
52
+ corrector.remove(offense_range)
53
+ corrector.insert_after(heredoc.loc.heredoc_end, replacement)
54
+ else
55
+ corrector.replace(offense_range, replacement)
56
+ end
57
+ end
58
+ end
59
+
46
60
  def message(node)
47
61
  format(MSG, line: node.loc.end.line, column: node.loc.end.column + 1)
48
62
  end
49
63
 
50
- def delimiter_range(node)
64
+ def last_heredoc_argument(node)
65
+ return unless node&.call_type?
66
+ return unless (arguments = node&.arguments)
67
+
68
+ heredoc = arguments.reverse.detect { |arg| arg.str_type? && arg.heredoc? }
69
+ return heredoc if heredoc
70
+
71
+ last_heredoc_argument(node.children.first)
72
+ end
73
+
74
+ def offense_range(node)
51
75
  Parser::Source::Range.new(
52
76
  node.loc.expression.source_buffer,
53
77
  node.children.compact.last.loc.expression.end_pos,
54
- node.loc.expression.end_pos
78
+ end_of_method_chain(node).loc.expression.end_pos
55
79
  )
56
80
  end
81
+
82
+ def end_of_method_chain(node)
83
+ return node unless node.parent&.call_type?
84
+
85
+ end_of_method_chain(node.parent)
86
+ end
57
87
  end
58
88
  end
59
89
  end
@@ -80,8 +80,11 @@ module RuboCop
80
80
  @block_line = node.source_range.first_line
81
81
  end
82
82
 
83
- def on_send(node)
84
- return unless node.bare_access_modifier? && !node.parent&.block_type?
83
+ alias on_numblock on_block
84
+
85
+ def on_send(node) # rubocop:disable Metrics/CyclomaticComplexity
86
+ return unless node.bare_access_modifier? &&
87
+ !(node.parent&.block_type? || node.parent&.numblock_type?)
85
88
  return if expected_empty_lines?(node)
86
89
 
87
90
  message = message(node)
@@ -32,6 +32,8 @@ module RuboCop
32
32
 
33
33
  check(node, node.body, adjusted_first_line: first_line)
34
34
  end
35
+
36
+ alias on_numblock on_block
35
37
  end
36
38
  end
37
39
  end
@@ -22,20 +22,20 @@ module RuboCop
22
22
  # # all platforms.
23
23
  #
24
24
  # # bad
25
- # puts 'Hello' # Return character is CR+LF on all platfoms.
25
+ # puts 'Hello' # Return character is CR+LF on all platforms.
26
26
  #
27
27
  # # good
28
- # puts 'Hello' # Return character is LF on all platfoms.
28
+ # puts 'Hello' # Return character is LF on all platforms.
29
29
  #
30
30
  # @example EnforcedStyle: crlf
31
31
  # # The `crlf` style means that CR+LF (Carriage Return + Line Feed) is
32
32
  # # enforced on all platforms.
33
33
  #
34
34
  # # bad
35
- # puts 'Hello' # Return character is LF on all platfoms.
35
+ # puts 'Hello' # Return character is LF on all platforms.
36
36
  #
37
37
  # # good
38
- # puts 'Hello' # Return character is CR+LF on all platfoms.
38
+ # puts 'Hello' # Return character is CR+LF on all platforms.
39
39
  #
40
40
  class EndOfLine < Base
41
41
  include ConfigurableEnforcedStyle
@@ -153,7 +153,8 @@ module RuboCop
153
153
  MSG = 'Indent the first argument one step more than %<base>s.'
154
154
 
155
155
  def on_send(node)
156
- return if style != :consistent && enforce_first_argument_with_fixed_indentation?
156
+ return if style != :consistent && enforce_first_argument_with_fixed_indentation? &&
157
+ !enable_layout_first_method_argument_line_break?
157
158
  return if !node.arguments? || bare_operator?(node) || node.setter_method?
158
159
 
159
160
  indent = base_indentation(node) + configured_indentation_width
@@ -161,6 +162,7 @@ module RuboCop
161
162
  check_alignment([node.first_argument], indent)
162
163
  end
163
164
  alias on_csend on_send
165
+ alias on_super on_send
164
166
 
165
167
  private
166
168
 
@@ -267,6 +269,10 @@ module RuboCop
267
269
  argument_alignment_config['EnforcedStyle'] == 'with_fixed_indentation'
268
270
  end
269
271
 
272
+ def enable_layout_first_method_argument_line_break?
273
+ config.for_cop('Layout/FirstMethodArgumentLineBreak')['Enabled']
274
+ end
275
+
270
276
  def argument_alignment_config
271
277
  config.for_cop('Layout/ArgumentAlignment')
272
278
  end
@@ -143,7 +143,7 @@ module RuboCop
143
143
  case indent_base_type
144
144
  when :left_brace_or_bracket
145
145
  'the position of the opening bracket'
146
- when :first_colmn_after_left_parenthesis
146
+ when :first_column_after_left_parenthesis
147
147
  'the first position after the preceding left parenthesis'
148
148
  when :parent_hash_key
149
149
  'the parent hash key'
@@ -164,7 +164,7 @@ module RuboCop
164
164
  case indent_base_type
165
165
  when :left_brace_or_bracket
166
166
  'Indent the right bracket the same as the left bracket.'
167
- when :first_colmn_after_left_parenthesis
167
+ when :first_column_after_left_parenthesis
168
168
  'Indent the right bracket the same as the first position ' \
169
169
  'after the preceding left parenthesis.'
170
170
  when :parent_hash_key
@@ -192,7 +192,7 @@ module RuboCop
192
192
  case indent_base_type
193
193
  when :left_brace_or_bracket
194
194
  'the position of the opening brace'
195
- when :first_colmn_after_left_parenthesis
195
+ when :first_column_after_left_parenthesis
196
196
  'the first position after the preceding left parenthesis'
197
197
  when :parent_hash_key
198
198
  'the parent hash key'
@@ -213,7 +213,7 @@ module RuboCop
213
213
  case indent_base_type
214
214
  when :left_brace_or_bracket
215
215
  'Indent the right brace the same as the left brace.'
216
- when :first_colmn_after_left_parenthesis
216
+ when :first_column_after_left_parenthesis
217
217
  'Indent the right brace the same as the first position ' \
218
218
  'after the preceding left parenthesis.'
219
219
  when :parent_hash_key