rubocop 1.88.2 → 1.89.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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +67 -11
  3. data/lib/rubocop/comment_config.rb +35 -19
  4. data/lib/rubocop/config.rb +53 -0
  5. data/lib/rubocop/config_validator.rb +38 -1
  6. data/lib/rubocop/cop/badge.rb +8 -0
  7. data/lib/rubocop/cop/base.rb +6 -4
  8. data/lib/rubocop/cop/bundler.rb +19 -0
  9. data/lib/rubocop/cop/corrector.rb +1 -1
  10. data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
  11. data/lib/rubocop/cop/gemspec.rb +22 -0
  12. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  13. data/lib/rubocop/cop/generator.rb +4 -0
  14. data/lib/rubocop/cop/layout/class_structure.rb +8 -2
  15. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  16. data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
  17. data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
  18. data/lib/rubocop/cop/layout/line_length.rb +85 -12
  19. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +12 -2
  20. data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
  21. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +33 -34
  22. data/lib/rubocop/cop/layout.rb +114 -0
  23. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  24. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
  25. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
  26. data/lib/rubocop/cop/lint/constant_reassignment.rb +1 -10
  27. data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
  28. data/lib/rubocop/cop/lint/deprecated_reference.rb +185 -0
  29. data/lib/rubocop/cop/lint/duplicate_methods.rb +202 -22
  30. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  31. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
  32. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  33. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  34. data/lib/rubocop/cop/lint/name_typo.rb +223 -0
  35. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +0 -4
  36. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +7 -0
  37. data/lib/rubocop/cop/lint/to_enum_arguments.rb +37 -1
  38. data/lib/rubocop/cop/lint/unused_private_method.rb +157 -0
  39. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
  40. data/lib/rubocop/cop/lint/void.rb +17 -6
  41. data/lib/rubocop/cop/lint.rb +176 -0
  42. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  43. data/lib/rubocop/cop/metrics.rb +31 -0
  44. data/lib/rubocop/cop/migration/department_name.rb +0 -4
  45. data/lib/rubocop/cop/migration.rb +13 -0
  46. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
  47. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  48. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
  49. data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
  50. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
  51. data/lib/rubocop/cop/mixin/statement_modifier.rb +42 -6
  52. data/lib/rubocop/cop/mixin.rb +78 -77
  53. data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
  54. data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
  55. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  56. data/lib/rubocop/cop/naming.rb +31 -0
  57. data/lib/rubocop/cop/offense.rb +10 -6
  58. data/lib/rubocop/cop/registry.rb +153 -43
  59. data/lib/rubocop/cop/security.rb +19 -0
  60. data/lib/rubocop/cop/style/array_intersect.rb +24 -5
  61. data/lib/rubocop/cop/style/class_and_module_children.rb +78 -18
  62. data/lib/rubocop/cop/style/conditional_assignment.rb +22 -0
  63. data/lib/rubocop/cop/style/copyright.rb +6 -1
  64. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +41 -3
  65. data/lib/rubocop/cop/style/documentation.rb +47 -0
  66. data/lib/rubocop/cop/style/endless_method.rb +0 -10
  67. data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -48
  68. data/lib/rubocop/cop/style/lambda.rb +4 -0
  69. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -3
  70. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +30 -0
  71. data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
  72. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  73. data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
  74. data/lib/rubocop/cop/style/negative_array_index.rb +3 -1
  75. data/lib/rubocop/cop/style/redundant_assignment.rb +11 -0
  76. data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
  77. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  78. data/lib/rubocop/cop/style/redundant_format.rb +12 -3
  79. data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
  80. data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -63
  81. data/lib/rubocop/cop/style/safe_navigation.rb +10 -0
  82. data/lib/rubocop/cop/style/sole_nested_conditional.rb +20 -3
  83. data/lib/rubocop/cop/style/static_class.rb +21 -0
  84. data/lib/rubocop/cop/style.rb +310 -0
  85. data/lib/rubocop/cop/team.rb +48 -3
  86. data/lib/rubocop/cop/variable_force/variable.rb +18 -6
  87. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  88. data/lib/rubocop/directive_comment.rb +2 -0
  89. data/lib/rubocop/feature_loader.rb +0 -6
  90. data/lib/rubocop/lsp/routes.rb +41 -3
  91. data/lib/rubocop/lsp/runtime.rb +4 -0
  92. data/lib/rubocop/lsp/server.rb +4 -0
  93. data/lib/rubocop/lsp/stdin_runner.rb +20 -0
  94. data/lib/rubocop/pending_cops_reporter.rb +1 -6
  95. data/lib/rubocop/project_index_loader.rb +16 -0
  96. data/lib/rubocop/result_cache.rb +6 -4
  97. data/lib/rubocop/rspec/shared_contexts.rb +14 -2
  98. data/lib/rubocop/runner.rb +45 -20
  99. data/lib/rubocop/server/cache.rb +10 -5
  100. data/lib/rubocop/target_ruby.rb +1 -1
  101. data/lib/rubocop/version.rb +1 -1
  102. data/lib/rubocop.rb +17 -626
  103. metadata +18 -3
@@ -62,5 +62,21 @@ module RuboCop
62
62
  rescue StandardError => e
63
63
  warn Rainbow("rubydex index build failed: #{e.message}. Continuing without it.").yellow
64
64
  end
65
+
66
+ # The Ruby source files of every gem in the project's bundle, for
67
+ # `AllCops/ProjectIndexIncludesGems`. Returns an empty array when RuboCop
68
+ # does not run inside Bundler, so the option silently degrades to
69
+ # project-only indexing.
70
+ def bundled_gem_source_files
71
+ return [] unless defined?(Bundler) && Bundler::SharedHelpers.in_bundle?
72
+
73
+ Bundler.load.specs.flat_map do |spec|
74
+ Dir.glob(File.join(spec.full_gem_path, 'lib', '**', '*.rb'))
75
+ end
76
+ rescue StandardError => e
77
+ message = "rubydex bundle indexing failed: #{e.message}. Indexing the project only."
78
+ warn Rainbow(message).yellow
79
+ []
80
+ end
65
81
  end
66
82
  end
@@ -237,10 +237,12 @@ module RuboCop
237
237
  # when traversing the relative paths with symlinks.
238
238
  exe_root = File.absolute_path(exe_root)
239
239
 
240
- # These are all the files we have `require`d plus everything in the
241
- # exe directory. A change to any of them could affect the cop output
242
- # so we include them in the cache hash.
243
- source_files = $LOADED_FEATURES + Find.find(exe_root).to_a
240
+ # These are all the files we have `require`d, all of RuboCop's own files whether loaded
241
+ # or not (cops are loaded lazily, so which of them are in `$LOADED_FEATURES` varies from
242
+ # run to run), plus everything in the exe directory. A change to any of them could affect
243
+ # the cop output so we include them in the cache hash.
244
+ rubocop_lib_files = Dir[File.join(File.absolute_path(lib_root), 'rubocop', '**', '*.rb')]
245
+ source_files = $LOADED_FEATURES | rubocop_lib_files | Find.find(exe_root).to_a
244
246
  source_files -= ResultCache.rubocop_required_features # Rely on gem versions
245
247
 
246
248
  source_files
@@ -6,11 +6,14 @@ require 'tmpdir'
6
6
  # or stubbing Dir.pwd get a fresh value.
7
7
  RSpec.configure { |c| c.before { RuboCop::PathUtil.reset_pwd } }
8
8
 
9
- RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLength
10
- around do |example| # rubocop:disable Metrics/BlockLength
9
+ # rubocop:disable Metrics/BlockLength
10
+ RSpec.shared_context 'isolated environment' do
11
+ around do |example|
11
12
  Dir.mktmpdir do |tmpdir|
12
13
  original_home = Dir.home
13
14
  original_xdg_config_home = ENV.fetch('XDG_CONFIG_HOME', nil)
15
+ original_xdg_cache_home = ENV.fetch('XDG_CACHE_HOME', nil)
16
+ original_rubocop_cache_root = ENV.fetch('RUBOCOP_CACHE_ROOT', nil)
14
17
 
15
18
  # Make sure to expand all symlinks in the path first. Otherwise we may
16
19
  # get mismatched pathnames when loading config files later on.
@@ -23,6 +26,12 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
23
26
  ENV['HOME'] = virtual_home
24
27
  ENV.delete('XDG_CONFIG_HOME')
25
28
 
29
+ # `XDG_CACHE_HOME` and `RUBOCOP_CACHE_ROOT` are set on some CI runners and
30
+ # would otherwise point the result cache at a fixed directory shared across examples
31
+ # and parallel test-queue workers, letting one example's cache leak into another.
32
+ ENV.delete('XDG_CACHE_HOME')
33
+ ENV.delete('RUBOCOP_CACHE_ROOT')
34
+
26
35
  base_dir = example.metadata[:project_inside_home] ? virtual_home : tmpdir
27
36
  root = example.metadata[:root]
28
37
  working_dir = root ? File.join(base_dir, 'work', root) : File.join(base_dir, 'work')
@@ -38,6 +47,8 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
38
47
  ensure
39
48
  ENV['HOME'] = original_home
40
49
  ENV['XDG_CONFIG_HOME'] = original_xdg_config_home
50
+ ENV['XDG_CACHE_HOME'] = original_xdg_cache_home
51
+ ENV['RUBOCOP_CACHE_ROOT'] = original_rubocop_cache_root
41
52
 
42
53
  RuboCop::ResultCache.reset_config_cache
43
54
  RuboCop::ConfigLoader.clear_options # This also resets RuboCop::FileFinder.root_level
@@ -58,6 +69,7 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
58
69
  end
59
70
  end
60
71
  end
72
+ # rubocop:enable Metrics/BlockLength
61
73
 
62
74
  # Workaround for https://github.com/rubocop/rubocop/issues/12978,
63
75
  # there should already be no gemfile in the temp directory
@@ -72,7 +72,7 @@ module RuboCop
72
72
  ResultCache.source_checksum
73
73
 
74
74
  target_files = find_target_files(paths)
75
- @project_index = ProjectIndexLoader.build_index(target_files) if project_index_enabled?
75
+ build_project_index(target_files)
76
76
 
77
77
  if @options[:list_target_files]
78
78
  list_files(target_files)
@@ -104,6 +104,34 @@ module RuboCop
104
104
  target_files.each(&:freeze).freeze
105
105
  end
106
106
 
107
+ def build_project_index(target_files)
108
+ return unless project_index_enabled?
109
+
110
+ @project_index = ProjectIndexLoader.build_index(project_index_files(target_files))
111
+ end
112
+
113
+ # The index always covers the whole project, not just the files being
114
+ # inspected: cross-file offenses must not depend on which files a
115
+ # particular run happens to include, or single-file runs (editors, CI
116
+ # sharding) would see different offenses than full runs. The project is
117
+ # rooted at the directory of the configuration that enabled the index.
118
+ def project_index_files(target_files)
119
+ root = @config_store.for_pwd.base_dir_for_path_parameters
120
+ project_files = begin
121
+ find_target_files([root]) | target_files
122
+ rescue Errno::ENOENT
123
+ target_files
124
+ end
125
+
126
+ (project_files | bundled_gem_files).sort
127
+ end
128
+
129
+ def bundled_gem_files
130
+ return [] unless @config_store.for_pwd.for_all_cops['ProjectIndexIncludesGems']
131
+
132
+ ProjectIndexLoader.bundled_gem_source_files
133
+ end
134
+
107
135
  def project_index_enabled?
108
136
  return false unless @config_store.for_pwd.for_all_cops['UseProjectIndex']
109
137
 
@@ -479,27 +507,31 @@ module RuboCop
479
507
  team
480
508
  end
481
509
 
482
- def mobilized_cop_classes(config) # rubocop:disable Metrics/AbcSize
510
+ def mobilized_cop_classes(config)
483
511
  @mobilized_cop_classes ||= {}.compare_by_identity
484
512
  @mobilized_cop_classes[config] ||= begin
485
- cop_classes = Cop::Registry.all
486
-
487
513
  # `@options[:only]` and `@options[:except]` are not qualified until
488
- # needed so that the Registry can be fully loaded, including any
489
- # cops added by `require`s.
514
+ # needed so that the registry contains any cops added by `require`s,
515
+ # which register eagerly when their files are loaded.
490
516
  qualify_option_cop_names
491
517
 
492
518
  OptionsValidator.new(@options).validate_cop_options
493
519
 
494
- if @options[:only]
495
- cop_classes.select! { |c| c.match?(@options[:only]) }
496
- else
497
- filter_cop_classes(cop_classes, config)
498
- end
520
+ # Filtering by badge keeps lazy-loaded cops unloaded; only the cops
521
+ # that survive the filter and are enabled will be loaded.
522
+ Cop::Registry.global.filter_by_badge(@options) { |badge| mobilize_cop_badge?(badge, config) }
523
+ end
524
+ end
499
525
 
500
- cop_classes.reject! { |c| c.match?(@options[:except]) }
526
+ def mobilize_cop_badge?(badge, config)
527
+ return false if badge.department == :Test
528
+ return false if badge.match_name?(@options[:except])
501
529
 
502
- Cop::Registry.new(cop_classes, @options)
530
+ if @options[:only]
531
+ badge.match_name?(@options[:only])
532
+ else
533
+ # use only cops that link to a style guide if requested.
534
+ !style_guide_cops_only?(config) || config.for_cop(badge.to_s)['StyleGuide']
503
535
  end
504
536
  end
505
537
 
@@ -513,13 +545,6 @@ module RuboCop
513
545
  end
514
546
  end
515
547
 
516
- def filter_cop_classes(cop_classes, config)
517
- # use only cops that link to a style guide if requested
518
- return unless style_guide_cops_only?(config)
519
-
520
- cop_classes.select! { |cop| config.for_cop(cop)['StyleGuide'] }
521
- end
522
-
523
548
  def style_guide_cops_only?(config)
524
549
  @options[:only_guide_cops] || config.for_all_cops['StyleGuideCopsOnly']
525
550
  end
@@ -64,11 +64,15 @@ module RuboCop
64
64
  # rubocop:enable Metrics/AbcSize
65
65
 
66
66
  def dir
67
- Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
67
+ dir_path.tap do |d|
68
68
  d.mkpath unless d.exist?
69
69
  end
70
70
  end
71
71
 
72
+ def dir_path
73
+ Pathname.new(File.join(cache_path, project_dir_cache_key))
74
+ end
75
+
72
76
  def cache_path
73
77
  cache_root_dir = if cache_root_path
74
78
  File.join(cache_root_path, 'rubocop_cache')
@@ -87,8 +91,8 @@ module RuboCop
87
91
  dir.join('token')
88
92
  end
89
93
 
90
- def pid_path
91
- dir.join('pid')
94
+ def pid_path(create_dir: true)
95
+ (create_dir ? dir : dir_path).join('pid')
92
96
  end
93
97
 
94
98
  def lock_path
@@ -108,8 +112,9 @@ module RuboCop
108
112
  end
109
113
 
110
114
  def pid_running?
111
- Process.kill(0, pid_path.read.to_i) == 1
112
- rescue Errno::ESRCH, Errno::ENOENT, Errno::EACCES, Errno::EROFS, Errno::ENAMETOOLONG
115
+ Process.kill(0, pid_path(create_dir: false).read.to_i) == 1
116
+ rescue Errno::ESRCH, Errno::ENOENT, Errno::EACCES, Errno::EROFS, Errno::ENAMETOOLONG,
117
+ Errno::ENOTDIR
113
118
  false
114
119
  end
115
120
 
@@ -212,7 +212,7 @@ module RuboCop
212
212
  end
213
213
 
214
214
  def pattern
215
- /^ruby = "(?<version>\d+\.\d+)/.freeze
215
+ /^ruby = ["'](?<version>\d+\.\d+)/.freeze
216
216
  end
217
217
  end
218
218
 
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.88.2'
6
+ STRING = '1.89.0'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \