rubocop 0.93.1 → 1.6.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 (199) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +36 -16
  3. data/config/default.yml +276 -82
  4. data/config/obsoletion.yml +196 -0
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop.rb +31 -2
  7. data/lib/rubocop/cli.rb +5 -1
  8. data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
  9. data/lib/rubocop/cli/command/execute_runner.rb +26 -11
  10. data/lib/rubocop/cli/command/suggest_extensions.rb +80 -0
  11. data/lib/rubocop/cli/command/version.rb +1 -1
  12. data/lib/rubocop/comment_config.rb +1 -1
  13. data/lib/rubocop/config.rb +4 -0
  14. data/lib/rubocop/config_loader.rb +34 -8
  15. data/lib/rubocop/config_loader_resolver.rb +12 -6
  16. data/lib/rubocop/config_obsoletion.rb +65 -247
  17. data/lib/rubocop/config_obsoletion/changed_enforced_styles.rb +33 -0
  18. data/lib/rubocop/config_obsoletion/changed_parameter.rb +21 -0
  19. data/lib/rubocop/config_obsoletion/cop_rule.rb +34 -0
  20. data/lib/rubocop/config_obsoletion/extracted_cop.rb +44 -0
  21. data/lib/rubocop/config_obsoletion/parameter_rule.rb +44 -0
  22. data/lib/rubocop/config_obsoletion/removed_cop.rb +41 -0
  23. data/lib/rubocop/config_obsoletion/renamed_cop.rb +34 -0
  24. data/lib/rubocop/config_obsoletion/rule.rb +41 -0
  25. data/lib/rubocop/config_obsoletion/split_cop.rb +27 -0
  26. data/lib/rubocop/config_regeneration.rb +1 -1
  27. data/lib/rubocop/config_validator.rb +25 -10
  28. data/lib/rubocop/cop/autocorrect_logic.rb +21 -6
  29. data/lib/rubocop/cop/badge.rb +9 -24
  30. data/lib/rubocop/cop/base.rb +33 -16
  31. data/lib/rubocop/cop/bundler/duplicated_gem.rb +26 -6
  32. data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
  33. data/lib/rubocop/cop/commissioner.rb +37 -23
  34. data/lib/rubocop/cop/cop.rb +2 -2
  35. data/lib/rubocop/cop/corrector.rb +3 -1
  36. data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +2 -2
  37. data/lib/rubocop/cop/correctors/string_literal_corrector.rb +6 -8
  38. data/lib/rubocop/cop/force.rb +1 -1
  39. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
  40. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
  41. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
  42. data/lib/rubocop/cop/generator.rb +3 -10
  43. data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
  44. data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
  45. data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
  46. data/lib/rubocop/cop/layout/class_structure.rb +22 -3
  47. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  48. data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
  49. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +80 -10
  50. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +1 -0
  51. data/lib/rubocop/cop/layout/empty_lines_around_arguments.rb +6 -1
  52. data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -1
  53. data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
  54. data/lib/rubocop/cop/layout/end_of_line.rb +5 -5
  55. data/lib/rubocop/cop/layout/extra_spacing.rb +1 -2
  56. data/lib/rubocop/cop/layout/first_argument_indentation.rb +7 -2
  57. data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
  58. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +12 -0
  59. data/lib/rubocop/cop/layout/line_length.rb +10 -13
  60. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +7 -3
  61. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
  62. data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
  63. data/lib/rubocop/cop/layout/space_around_operators.rb +4 -1
  64. data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
  65. data/lib/rubocop/cop/layout/trailing_whitespace.rb +37 -13
  66. data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
  67. data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
  68. data/lib/rubocop/cop/lint/debugger.rb +17 -28
  69. data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
  70. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
  71. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +77 -0
  72. data/lib/rubocop/cop/lint/else_layout.rb +29 -3
  73. data/lib/rubocop/cop/lint/empty_block.rb +82 -0
  74. data/lib/rubocop/cop/lint/empty_class.rb +93 -0
  75. data/lib/rubocop/cop/lint/flip_flop.rb +8 -2
  76. data/lib/rubocop/cop/lint/interpolation_check.rb +7 -2
  77. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +39 -7
  78. data/lib/rubocop/cop/lint/loop.rb +4 -4
  79. data/lib/rubocop/cop/lint/missing_super.rb +7 -4
  80. data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
  81. data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
  82. data/lib/rubocop/cop/lint/number_conversion.rb +46 -13
  83. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +27 -8
  84. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
  85. data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
  86. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +13 -0
  87. data/lib/rubocop/cop/lint/to_enum_arguments.rb +86 -0
  88. data/lib/rubocop/cop/lint/to_json.rb +1 -1
  89. data/lib/rubocop/cop/lint/unexpected_block_arity.rb +85 -0
  90. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +199 -0
  91. data/lib/rubocop/cop/lint/useless_access_modifier.rb +2 -2
  92. data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
  93. data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
  94. data/lib/rubocop/cop/metrics/abc_size.rb +25 -1
  95. data/lib/rubocop/cop/metrics/block_length.rb +13 -7
  96. data/lib/rubocop/cop/metrics/method_length.rb +7 -2
  97. data/lib/rubocop/cop/metrics/parameter_lists.rb +68 -2
  98. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +20 -10
  99. data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +146 -0
  100. data/lib/rubocop/cop/metrics/utils/repeated_csend_discount.rb +6 -1
  101. data/lib/rubocop/cop/migration/department_name.rb +1 -1
  102. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  103. data/lib/rubocop/cop/mixin/configurable_numbering.rb +4 -3
  104. data/lib/rubocop/cop/mixin/enforce_superclass.rb +9 -1
  105. data/lib/rubocop/cop/mixin/ignored_methods.rb +36 -3
  106. data/lib/rubocop/cop/mixin/line_length_help.rb +1 -1
  107. data/lib/rubocop/cop/mixin/method_complexity.rb +6 -0
  108. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
  109. data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
  110. data/lib/rubocop/cop/mixin/string_help.rb +4 -1
  111. data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
  112. data/lib/rubocop/cop/naming/accessor_method_name.rb +15 -1
  113. data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +12 -2
  114. data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
  115. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
  116. data/lib/rubocop/cop/naming/predicate_name.rb +2 -1
  117. data/lib/rubocop/cop/naming/variable_number.rb +100 -8
  118. data/lib/rubocop/cop/offense.rb +3 -3
  119. data/lib/rubocop/cop/security/open.rb +12 -10
  120. data/lib/rubocop/cop/style/accessor_grouping.rb +1 -1
  121. data/lib/rubocop/cop/style/and_or.rb +11 -3
  122. data/lib/rubocop/cop/style/arguments_forwarding.rb +142 -0
  123. data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
  124. data/lib/rubocop/cop/style/case_like_if.rb +0 -4
  125. data/lib/rubocop/cop/style/character_literal.rb +10 -11
  126. data/lib/rubocop/cop/style/class_and_module_children.rb +8 -3
  127. data/lib/rubocop/cop/style/collection_compact.rb +91 -0
  128. data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +169 -0
  129. data/lib/rubocop/cop/style/documentation.rb +12 -1
  130. data/lib/rubocop/cop/style/double_negation.rb +6 -1
  131. data/lib/rubocop/cop/style/float_division.rb +44 -1
  132. data/lib/rubocop/cop/style/format_string.rb +8 -3
  133. data/lib/rubocop/cop/style/format_string_token.rb +47 -2
  134. data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
  135. data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
  136. data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
  137. data/lib/rubocop/cop/style/if_unless_modifier.rb +11 -3
  138. data/lib/rubocop/cop/style/if_with_semicolon.rb +39 -4
  139. data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
  140. data/lib/rubocop/cop/style/ip_addresses.rb +1 -1
  141. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
  142. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +10 -13
  143. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +8 -13
  144. data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +7 -11
  145. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -2
  146. data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
  147. data/lib/rubocop/cop/style/multiple_comparison.rb +55 -7
  148. data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
  149. data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
  150. data/lib/rubocop/cop/style/numeric_literals.rb +14 -11
  151. data/lib/rubocop/cop/style/perl_backrefs.rb +86 -9
  152. data/lib/rubocop/cop/style/raise_args.rb +21 -6
  153. data/lib/rubocop/cop/style/redundant_argument.rb +88 -0
  154. data/lib/rubocop/cop/style/redundant_condition.rb +2 -1
  155. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  156. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +7 -1
  157. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +24 -8
  158. data/lib/rubocop/cop/style/redundant_self.rb +3 -0
  159. data/lib/rubocop/cop/style/safe_navigation.rb +16 -4
  160. data/lib/rubocop/cop/style/semicolon.rb +3 -0
  161. data/lib/rubocop/cop/style/single_line_block_params.rb +30 -7
  162. data/lib/rubocop/cop/style/sole_nested_conditional.rb +65 -3
  163. data/lib/rubocop/cop/style/special_global_vars.rb +1 -13
  164. data/lib/rubocop/cop/style/static_class.rb +97 -0
  165. data/lib/rubocop/cop/style/string_concatenation.rb +39 -2
  166. data/lib/rubocop/cop/style/string_literals.rb +14 -8
  167. data/lib/rubocop/cop/style/string_literals_in_interpolation.rb +4 -3
  168. data/lib/rubocop/cop/style/swap_values.rb +108 -0
  169. data/lib/rubocop/cop/style/symbol_proc.rb +5 -3
  170. data/lib/rubocop/cop/style/trailing_underscore_variable.rb +3 -1
  171. data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
  172. data/lib/rubocop/cop/team.rb +6 -1
  173. data/lib/rubocop/cop/util.rb +6 -2
  174. data/lib/rubocop/cop/variable_force/branch.rb +1 -1
  175. data/lib/rubocop/cop/variable_force/scope.rb +1 -1
  176. data/lib/rubocop/core_ext/hash.rb +20 -0
  177. data/lib/rubocop/ext/regexp_node.rb +36 -11
  178. data/lib/rubocop/ext/regexp_parser.rb +95 -0
  179. data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
  180. data/lib/rubocop/formatter/emacs_style_formatter.rb +2 -0
  181. data/lib/rubocop/formatter/formatter_set.rb +2 -1
  182. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
  183. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  184. data/lib/rubocop/formatter/simple_text_formatter.rb +2 -0
  185. data/lib/rubocop/formatter/tap_formatter.rb +2 -0
  186. data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -1
  187. data/lib/rubocop/lockfile.rb +40 -0
  188. data/lib/rubocop/magic_comment.rb +2 -2
  189. data/lib/rubocop/options.rb +11 -1
  190. data/lib/rubocop/rake_task.rb +2 -2
  191. data/lib/rubocop/rspec/shared_contexts.rb +4 -0
  192. data/lib/rubocop/runner.rb +1 -1
  193. data/lib/rubocop/target_finder.rb +1 -1
  194. data/lib/rubocop/target_ruby.rb +65 -1
  195. data/lib/rubocop/version.rb +56 -6
  196. metadata +50 -9
  197. data/bin/console +0 -10
  198. data/bin/rubocop-profile +0 -32
  199. data/bin/setup +0 -7
@@ -5,6 +5,8 @@ module RuboCop
5
5
  # This formatter displays a YAML configuration file where all cops that
6
6
  # detected any offenses are configured to not detect the offense.
7
7
  class DisabledConfigFormatter < BaseFormatter
8
+ include PathUtil
9
+
8
10
  HEADING = <<~COMMENTS
9
11
  # This configuration was generated by
10
12
  # `%<command>s`
@@ -195,15 +197,28 @@ module RuboCop
195
197
  end
196
198
 
197
199
  def excludes(offending_files, cop_name, parent)
198
- # Exclude properties in .rubocop_todo.yml override default ones, as well
199
- # as any custom excludes in .rubocop.yml, so in order to retain those
200
- # excludes we must copy them.
201
- # There can be multiple .rubocop.yml files in subdirectories, but we
202
- # just look at the current working directory
200
+ # Exclude properties in .rubocop_todo.yml override default ones, as well as any custom
201
+ # excludes in .rubocop.yml, so in order to retain those excludes we must copy them.
202
+ # There can be multiple .rubocop.yml files in subdirectories, but we just look at the
203
+ # current working directory.
203
204
  config = ConfigStore.new.for(parent)
204
205
  cfg = config[cop_name] || {}
205
206
 
206
- ((cfg['Exclude'] || []) + offending_files).uniq
207
+ if merge_mode_for_exclude?(config) || merge_mode_for_exclude?(cfg)
208
+ offending_files
209
+ else
210
+ cop_exclude = cfg['Exclude']
211
+ if cop_exclude && cop_exclude != default_config(cop_name)['Exclude']
212
+ warn "`#{cop_name}: Exclude` in `#{smart_path(config.loaded_path)}` overrides a " \
213
+ 'generated `Exclude` in `.rubocop_todo.yml`. Either remove ' \
214
+ "`#{cop_name}: Exclude` or add `inherit_mode: merge: - Exclude`."
215
+ end
216
+ ((cop_exclude || []) + offending_files).uniq
217
+ end
218
+ end
219
+
220
+ def merge_mode_for_exclude?(cfg)
221
+ Array(cfg.to_h.dig('inherit_mode', 'merge')).include?('Exclude')
207
222
  end
208
223
 
209
224
  def output_exclude_path(output_buffer, exclude_path, parent)
@@ -27,6 +27,8 @@ module RuboCop
27
27
  "[Todo] #{offense.message}"
28
28
  elsif offense.corrected?
29
29
  "[Corrected] #{offense.message}"
30
+ elsif offense.correctable?
31
+ "[Correctable] #{offense.message}"
30
32
  else
31
33
  offense.message
32
34
  end
@@ -14,6 +14,7 @@ module RuboCop
14
14
  '[e]macs' => EmacsStyleFormatter,
15
15
  '[fi]les' => FileListFormatter,
16
16
  '[fu]ubar' => FuubarStyleFormatter,
17
+ '[g]ithub' => GitHubActionsFormatter,
17
18
  '[h]tml' => HTMLFormatter,
18
19
  '[j]son' => JSONFormatter,
19
20
  '[ju]nit' => JUnitFormatter,
@@ -30,7 +31,7 @@ module RuboCop
30
31
 
31
32
  FORMATTER_APIS.each do |method_name|
32
33
  define_method(method_name) do |*args|
33
- each { |f| f.send(method_name, *args) }
34
+ each { |f| f.public_send(method_name, *args) }
34
35
  end
35
36
  end
36
37
 
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Formatter
5
+ # This formatter formats report data as GitHub Workflow commands resulting
6
+ # in GitHub check annotations when run within GitHub Actions.
7
+ class GitHubActionsFormatter < BaseFormatter
8
+ ESCAPE_MAP = {
9
+ '%' => '%25',
10
+ "\n" => '%0A',
11
+ "\r" => '%0D'
12
+ }.freeze
13
+
14
+ def file_finished(file, offenses)
15
+ offenses.each { |offense| report_offense(file, offense) }
16
+ end
17
+
18
+ private
19
+
20
+ def github_escape(string)
21
+ string.gsub(Regexp.union(ESCAPE_MAP.keys), ESCAPE_MAP)
22
+ end
23
+
24
+ def minimum_severity_to_fail
25
+ @minimum_severity_to_fail ||= begin
26
+ name = options.fetch(:fail_level, :refactor)
27
+ RuboCop::Cop::Severity.new(name)
28
+ end
29
+ end
30
+
31
+ def github_severity(offense)
32
+ offense.severity < minimum_severity_to_fail ? 'warning' : 'error'
33
+ end
34
+
35
+ def report_offense(file, offense)
36
+ output.printf(
37
+ "\n::%<severity>s file=%<file>s,line=%<line>d,col=%<column>d::%<message>s\n",
38
+ severity: github_severity(offense),
39
+ file: file,
40
+ line: offense.line,
41
+ column: offense.real_column,
42
+ message: github_escape(offense.message)
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
@@ -67,7 +67,7 @@ module RuboCop
67
67
  end
68
68
 
69
69
  def total_offense_count(offense_counts)
70
- offense_counts.values.inject(0, :+)
70
+ offense_counts.values.sum
71
71
  end
72
72
  end
73
73
  end
@@ -90,6 +90,8 @@ module RuboCop
90
90
  green('[Todo] ')
91
91
  elsif offense.corrected?
92
92
  green('[Corrected] ')
93
+ elsif offense.correctable?
94
+ yellow('[Correctable] ')
93
95
  else
94
96
  ''
95
97
  end
@@ -71,6 +71,8 @@ module RuboCop
71
71
  '[Todo] '
72
72
  elsif offense.corrected?
73
73
  '[Corrected] '
74
+ elsif offense.correctable?
75
+ '[Correctable] '
74
76
  else
75
77
  ''
76
78
  end
@@ -55,7 +55,7 @@ module RuboCop
55
55
  end
56
56
 
57
57
  def total_offense_count(offense_counts)
58
- offense_counts.values.inject(0, :+)
58
+ offense_counts.values.sum
59
59
  end
60
60
  end
61
61
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # Encapsulation of a lockfile for use when checking for gems.
5
+ # Does not actually resolve gems, just parses the lockfile.
6
+ # @api private
7
+ class Lockfile
8
+ # Gems that the bundle depends on
9
+ def dependencies
10
+ return [] unless parser
11
+
12
+ parser.dependencies.values
13
+ end
14
+
15
+ # All activated gems, including transitive dependencies
16
+ def gems
17
+ return [] unless parser
18
+
19
+ # `Bundler::LockfileParser` returns `Bundler::LazySpecification` objects
20
+ # which are not resolved, so extract the dependencies from them
21
+ parser.dependencies.values.concat(parser.specs.flat_map(&:dependencies))
22
+ end
23
+
24
+ def includes_gem?(name)
25
+ gems.any? { |gem| gem.name == name }
26
+ end
27
+
28
+ private
29
+
30
+ def parser
31
+ return unless defined?(Bundler) && Bundler.default_lockfile
32
+ return @parser if defined?(@parser)
33
+
34
+ lockfile = Bundler.read_file(Bundler.default_lockfile)
35
+ @parser = lockfile ? Bundler::LockfileParser.new(lockfile) : nil
36
+ rescue Bundler::BundlerError
37
+ nil
38
+ end
39
+ end
40
+ end
@@ -194,7 +194,7 @@ module RuboCop
194
194
  class SimpleComment < MagicComment
195
195
  # Match `encoding` or `coding`
196
196
  def encoding
197
- extract(/\A\s*\#.*\b(?:en)?coding: (#{TOKEN})/i)
197
+ extract(/\A\s*\#.*\b(?:en)?coding: (#{TOKEN})/io)
198
198
  end
199
199
 
200
200
  private
@@ -207,7 +207,7 @@ module RuboCop
207
207
  # Case-insensitive and dashes/underscores are acceptable.
208
208
  # @see https://git.io/vM7Mg
209
209
  def extract_frozen_string_literal
210
- extract(/\A\s*#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/i)
210
+ extract(/\A\s*#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/io)
211
211
  end
212
212
  end
213
213
  end
@@ -5,6 +5,7 @@ require 'shellwords'
5
5
 
6
6
  module RuboCop
7
7
  class IncorrectCopNameError < StandardError; end
8
+
8
9
  class OptionArgumentError < StandardError; end
9
10
 
10
11
  # This class handles command line options.
@@ -145,6 +146,7 @@ module RuboCop
145
146
  end
146
147
  end
147
148
 
149
+ option(opts, '--display-time')
148
150
  option(opts, '--display-only-failed')
149
151
  end
150
152
 
@@ -194,6 +196,7 @@ module RuboCop
194
196
 
195
197
  option(opts, '--safe')
196
198
 
199
+ option(opts, '--stderr')
197
200
  option(opts, '--[no-]color')
198
201
 
199
202
  option(opts, '-v', '--version')
@@ -242,6 +245,9 @@ module RuboCop
242
245
  # @api private
243
246
  class OptionsValidator
244
247
  class << self
248
+ SYNTAX_DEPARTMENTS = %w[Syntax Lint/Syntax].freeze
249
+ private_constant :SYNTAX_DEPARTMENTS
250
+
245
251
  # Cop name validation must be done later than option parsing, so it's not
246
252
  # called from within Options.
247
253
  def validate_cop_list(names)
@@ -253,7 +259,7 @@ module RuboCop
253
259
  names.each do |name|
254
260
  next if cop_names.include?(name)
255
261
  next if departments.include?(name)
256
- next if %w[Syntax Lint/Syntax].include?(name)
262
+ next if SYNTAX_DEPARTMENTS.include?(name)
257
263
 
258
264
  raise IncorrectCopNameError, format_message_from(name, cop_names)
259
265
  end
@@ -466,6 +472,7 @@ module RuboCop
466
472
  'if no format is specified.'],
467
473
  fail_level: ['Minimum severity (A/R/C/W/E/F) for exit',
468
474
  'with error code.'],
475
+ display_time: 'Display elapsed time in seconds.',
469
476
  display_only_failed: ['Only output offense messages. Omit passing',
470
477
  'cops. Only valid for --format junit.'],
471
478
  display_only_fail_level_offenses:
@@ -493,6 +500,9 @@ module RuboCop
493
500
  extra_details: 'Display extra details in offense messages.',
494
501
  lint: 'Run only lint cops.',
495
502
  safe: 'Run only safe cops.',
503
+ stderr: ['Write all output to stderr except for the',
504
+ 'autocorrected source. This is especially useful',
505
+ 'when combined with --auto-correct and --stdin.'],
496
506
  list_target_files: 'List all files RuboCop will inspect.',
497
507
  auto_correct: 'Auto-correct offenses (only when it\'s safe).',
498
508
  safe_auto_correct: '(same, deprecated)',
@@ -22,7 +22,7 @@ module RuboCop
22
22
 
23
23
  task(name, *args) do |_, task_args|
24
24
  RakeFileUtils.verbose(verbose) do
25
- yield(*[self, task_args].slice(0, task_block.arity)) if block_given?
25
+ yield(*[self, task_args].slice(0, task_block.arity)) if task_block
26
26
  run_cli(verbose, full_options)
27
27
  end
28
28
  end
@@ -66,7 +66,7 @@ module RuboCop
66
66
 
67
67
  task(:auto_correct, *args) do |_, task_args|
68
68
  RakeFileUtils.verbose(verbose) do
69
- yield(*[self, task_args].slice(0, task_block.arity)) if block_given?
69
+ yield(*[self, task_args].slice(0, task_block.arity)) if task_block
70
70
  options = full_options.unshift('--auto-correct-all')
71
71
  options.delete('--parallel')
72
72
  run_cli(verbose, options)
@@ -138,3 +138,7 @@ end
138
138
  RSpec.shared_context 'ruby 2.7', :ruby27 do
139
139
  let(:ruby_version) { 2.7 }
140
140
  end
141
+
142
+ RSpec.shared_context 'ruby 3.0', :ruby30 do
143
+ let(:ruby_version) { 3.0 }
144
+ end
@@ -64,7 +64,7 @@ module RuboCop
64
64
  # instances that each inspects its allotted group of files.
65
65
  def warm_cache(target_files)
66
66
  puts 'Running parallel inspection' if @options[:debug]
67
- Parallel.each(target_files, &method(:file_offenses))
67
+ Parallel.each(target_files) { |target_file| file_offenses(target_file) }
68
68
  end
69
69
 
70
70
  def find_target_files(paths)
@@ -94,7 +94,7 @@ module RuboCop
94
94
  end
95
95
 
96
96
  def wanted_dir_patterns(base_dir, exclude_pattern, flags)
97
- dirs = Dir.glob(File.join(base_dir, '*/'), flags)
97
+ dirs = Dir.glob(File.join(base_dir.gsub('/**/', '/\**/'), '*/'), flags)
98
98
  .reject do |dir|
99
99
  dir.end_with?('/./', '/../') || File.fnmatch?(exclude_pattern, dir, flags)
100
100
  end
@@ -112,6 +112,70 @@ module RuboCop
112
112
  end
113
113
  end
114
114
 
115
+ # The target ruby version may be found in a .gemspec file.
116
+ # @api private
117
+ class GemspecFile < Source
118
+ extend NodePattern::Macros
119
+
120
+ GEMSPEC_EXTENSION = '.gemspec'
121
+
122
+ def_node_search :required_ruby_version, <<~PATTERN
123
+ (send _ :required_ruby_version= $_)
124
+ PATTERN
125
+
126
+ def_node_matcher :gem_requirement?, <<~PATTERN
127
+ (send (const(const _ :Gem):Requirement) :new $str)
128
+ PATTERN
129
+
130
+ def name
131
+ "`required_ruby_version` parameter (in #{gemspec_filename})"
132
+ end
133
+
134
+ private
135
+
136
+ def find_version
137
+ file = gemspec_filepath
138
+ return unless file && File.file?(file)
139
+
140
+ version = version_from_gemspec_file(file)
141
+ return if version.nil?
142
+
143
+ requirement = version.children.last
144
+ return version_from_array(version) if version.array_type?
145
+ return version_from_array(requirement) if gem_requirement? version
146
+
147
+ version_from_str(version.str_content)
148
+ end
149
+
150
+ def gemspec_filename
151
+ @gemspec_filename ||= begin
152
+ basename = Pathname.new(@config.base_dir_for_path_parameters).basename.to_s
153
+ "#{basename}#{GEMSPEC_EXTENSION}"
154
+ end
155
+ end
156
+
157
+ def gemspec_filepath
158
+ @gemspec_filepath ||=
159
+ @config.find_file_upwards(gemspec_filename, @config.base_dir_for_path_parameters)
160
+ end
161
+
162
+ def version_from_gemspec_file(file)
163
+ processed_source = ProcessedSource.from_file(file, DEFAULT_VERSION)
164
+ required_ruby_version(processed_source.ast).first
165
+ end
166
+
167
+ def version_from_array(array)
168
+ versions = array.children.map { |v| version_from_str(v.is_a?(String) ? v : v.str_content) }
169
+ versions.compact.min
170
+ end
171
+
172
+ def version_from_str(str)
173
+ str.match(/^(?:>=|<=)?\s*(?<version>\d+(?:\.\d+)*)/) do |md|
174
+ md[:version].to_f
175
+ end
176
+ end
177
+ end
178
+
115
179
  # If all else fails, a default version will be picked.
116
180
  # @api private
117
181
  class Default < Source
@@ -130,7 +194,7 @@ module RuboCop
130
194
  KNOWN_RUBIES
131
195
  end
132
196
 
133
- SOURCES = [RuboCopConfig, RubyVersionFile, BundlerLockFile, Default].freeze
197
+ SOURCES = [RuboCopConfig, RubyVersionFile, BundlerLockFile, GemspecFile, Default].freeze
134
198
  private_constant :SOURCES
135
199
 
136
200
  def initialize(config)
@@ -3,24 +3,74 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '0.93.1'
6
+ STRING = '1.6.1'
7
7
 
8
8
  MSG = '%<version>s (using Parser %<parser_version>s, '\
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
10
10
  'running on %<ruby_engine>s %<ruby_version>s %<ruby_platform>s)'
11
11
 
12
+ CANONICAL_FEATURE_NAMES = { 'Rspec' => 'RSpec' }.freeze
13
+
12
14
  # @api private
13
- def self.version(debug: false)
15
+ def self.version(debug: false, env: nil)
14
16
  if debug
15
- format(MSG, version: STRING, parser_version: Parser::VERSION,
16
- rubocop_ast_version: RuboCop::AST::Version::STRING,
17
- ruby_engine: RUBY_ENGINE, ruby_version: RUBY_VERSION,
18
- ruby_platform: RUBY_PLATFORM)
17
+ verbose_version = format(MSG, version: STRING, parser_version: Parser::VERSION,
18
+ rubocop_ast_version: RuboCop::AST::Version::STRING,
19
+ ruby_engine: RUBY_ENGINE, ruby_version: RUBY_VERSION,
20
+ ruby_platform: RUBY_PLATFORM)
21
+ return verbose_version unless env
22
+
23
+ extension_versions = extension_versions(env)
24
+ return verbose_version if extension_versions.empty?
25
+
26
+ <<~VERSIONS
27
+ #{verbose_version}
28
+ #{extension_versions.join("\n")}
29
+ VERSIONS
19
30
  else
20
31
  STRING
21
32
  end
22
33
  end
23
34
 
35
+ # @api private
36
+ def self.extension_versions(env)
37
+ env.config_store.for_pwd.loaded_features.sort.map do |loaded_feature|
38
+ next unless (match = loaded_feature.match(/rubocop-(?<feature>.*)/))
39
+
40
+ feature = match[:feature]
41
+ begin
42
+ require "rubocop/#{feature}/version"
43
+ rescue LoadError
44
+ # Not worth mentioning libs that are not installed
45
+ else
46
+ next unless (feature_version = feature_version(feature))
47
+
48
+ " - #{loaded_feature} #{feature_version}"
49
+ end
50
+ end.compact
51
+ end
52
+
53
+ # Returns feature version in one of two ways:
54
+ #
55
+ # * Find by RuboCop core version style (e.g. rubocop-performance, rubocop-rspec)
56
+ # * Find by `bundle gem` version style (e.g. rubocop-rake)
57
+ #
58
+ # @api private
59
+ def self.feature_version(feature)
60
+ capitalized_feature = feature.capitalize
61
+ extension_name = CANONICAL_FEATURE_NAMES.fetch(capitalized_feature, capitalized_feature)
62
+
63
+ # Find by RuboCop core version style (e.g. rubocop-performance, rubocop-rspec)
64
+ RuboCop.const_get(extension_name)::Version::STRING
65
+ rescue NameError
66
+ begin
67
+ # Find by `bundle gem` version style (e.g. rubocop-rake, rubocop-packaging)
68
+ RuboCop.const_get(extension_name)::VERSION
69
+ rescue NameError
70
+ # noop
71
+ end
72
+ end
73
+
24
74
  # @api private
25
75
  def self.document_version
26
76
  STRING.match('\d+\.\d+').to_s