rubocop 1.6.0 → 1.9.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 (109) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +3 -2
  4. data/config/default.yml +142 -19
  5. data/lib/rubocop.rb +15 -1
  6. data/lib/rubocop/cli/command/auto_genenerate_config.rb +5 -4
  7. data/lib/rubocop/comment_config.rb +6 -6
  8. data/lib/rubocop/config.rb +10 -7
  9. data/lib/rubocop/config_loader.rb +11 -14
  10. data/lib/rubocop/config_loader_resolver.rb +21 -4
  11. data/lib/rubocop/config_obsoletion.rb +5 -3
  12. data/lib/rubocop/config_obsoletion/extracted_cop.rb +6 -6
  13. data/lib/rubocop/config_store.rb +12 -1
  14. data/lib/rubocop/cop/base.rb +1 -1
  15. data/lib/rubocop/cop/gemspec/required_ruby_version.rb +3 -2
  16. data/lib/rubocop/cop/generator.rb +1 -3
  17. data/lib/rubocop/cop/internal_affairs.rb +6 -1
  18. data/lib/rubocop/cop/internal_affairs/empty_line_between_expect_offense_and_correction.rb +68 -0
  19. data/lib/rubocop/cop/internal_affairs/example_description.rb +89 -0
  20. data/lib/rubocop/cop/internal_affairs/redundant_described_class_as_subject.rb +61 -0
  21. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +64 -0
  22. data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +145 -0
  23. data/lib/rubocop/cop/layout/class_structure.rb +7 -2
  24. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +19 -3
  25. data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +14 -0
  26. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +2 -2
  27. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +3 -10
  28. data/lib/rubocop/cop/layout/space_around_equals_in_parameter_default.rb +1 -0
  29. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  30. data/lib/rubocop/cop/layout/space_before_brackets.rb +62 -0
  31. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +13 -10
  32. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +2 -2
  33. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +59 -0
  34. data/lib/rubocop/cop/lint/binary_operator_with_identical_operands.rb +7 -2
  35. data/lib/rubocop/cop/lint/deprecated_constants.rb +75 -0
  36. data/lib/rubocop/cop/lint/duplicate_branch.rb +64 -2
  37. data/lib/rubocop/cop/lint/lambda_without_literal_block.rb +44 -0
  38. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +10 -6
  39. data/lib/rubocop/cop/lint/number_conversion.rb +41 -6
  40. data/lib/rubocop/cop/lint/numbered_parameter_assignment.rb +47 -0
  41. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +39 -0
  42. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +2 -1
  43. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +50 -0
  44. data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +50 -17
  45. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -11
  46. data/lib/rubocop/cop/lint/symbol_conversion.rb +102 -0
  47. data/lib/rubocop/cop/lint/triple_quotes.rb +71 -0
  48. data/lib/rubocop/cop/lint/unreachable_loop.rb +17 -0
  49. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  50. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +18 -0
  51. data/lib/rubocop/cop/mixin/check_line_breakable.rb +5 -0
  52. data/lib/rubocop/cop/mixin/comments_help.rb +1 -11
  53. data/lib/rubocop/cop/mixin/first_element_line_break.rb +1 -1
  54. data/lib/rubocop/cop/mixin/uncommunicative_name.rb +5 -1
  55. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +59 -5
  56. data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +38 -5
  57. data/lib/rubocop/cop/naming/variable_name.rb +2 -0
  58. data/lib/rubocop/cop/naming/variable_number.rb +2 -9
  59. data/lib/rubocop/cop/registry.rb +10 -0
  60. data/lib/rubocop/cop/severity.rb +3 -3
  61. data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -1
  62. data/lib/rubocop/cop/style/ascii_comments.rb +1 -1
  63. data/lib/rubocop/cop/style/collection_methods.rb +14 -1
  64. data/lib/rubocop/cop/style/commented_keyword.rb +22 -5
  65. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +49 -9
  66. data/lib/rubocop/cop/style/empty_literal.rb +6 -2
  67. data/lib/rubocop/cop/style/endless_method.rb +102 -0
  68. data/lib/rubocop/cop/style/eval_with_location.rb +63 -34
  69. data/lib/rubocop/cop/style/explicit_block_argument.rb +10 -0
  70. data/lib/rubocop/cop/style/float_division.rb +3 -0
  71. data/lib/rubocop/cop/style/for.rb +2 -0
  72. data/lib/rubocop/cop/style/format_string_token.rb +18 -2
  73. data/lib/rubocop/cop/style/hash_except.rb +95 -0
  74. data/lib/rubocop/cop/style/hash_like_case.rb +2 -1
  75. data/lib/rubocop/cop/style/if_inside_else.rb +22 -10
  76. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +96 -0
  77. data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -2
  78. data/lib/rubocop/cop/style/lambda_call.rb +2 -1
  79. data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -0
  80. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +16 -6
  81. data/lib/rubocop/cop/style/method_def_parentheses.rb +7 -0
  82. data/lib/rubocop/cop/style/multiline_method_signature.rb +26 -1
  83. data/lib/rubocop/cop/style/multiline_when_then.rb +3 -1
  84. data/lib/rubocop/cop/style/mutable_constant.rb +13 -3
  85. data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +4 -0
  86. data/lib/rubocop/cop/style/nil_comparison.rb +1 -0
  87. data/lib/rubocop/cop/style/non_nil_check.rb +23 -13
  88. data/lib/rubocop/cop/style/raise_args.rb +5 -2
  89. data/lib/rubocop/cop/style/redundant_argument.rb +7 -1
  90. data/lib/rubocop/cop/style/redundant_freeze.rb +8 -4
  91. data/lib/rubocop/cop/style/redundant_return.rb +1 -1
  92. data/lib/rubocop/cop/style/single_line_methods.rb +34 -2
  93. data/lib/rubocop/cop/style/sole_nested_conditional.rb +29 -5
  94. data/lib/rubocop/cop/style/string_concatenation.rb +1 -1
  95. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  96. data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
  97. data/lib/rubocop/cop/style/while_until_modifier.rb +2 -4
  98. data/lib/rubocop/cop/util.rb +3 -1
  99. data/lib/rubocop/formatter/git_hub_actions_formatter.rb +1 -0
  100. data/lib/rubocop/magic_comment.rb +30 -1
  101. data/lib/rubocop/options.rb +10 -10
  102. data/lib/rubocop/rspec/cop_helper.rb +0 -4
  103. data/lib/rubocop/rspec/expect_offense.rb +37 -22
  104. data/lib/rubocop/runner.rb +17 -1
  105. data/lib/rubocop/target_finder.rb +4 -2
  106. data/lib/rubocop/target_ruby.rb +47 -11
  107. data/lib/rubocop/util.rb +16 -0
  108. data/lib/rubocop/version.rb +8 -2
  109. metadata +26 -7
@@ -22,11 +22,12 @@ module RuboCop
22
22
  SUPER_TYPES = %i[super zsuper].freeze
23
23
 
24
24
  def_node_matcher :proc_node?, '(send (const {nil? cbase} :Proc) :new)'
25
+ def_node_matcher :symbol_proc_receiver?, '{(send ...) (super ...) zsuper}'
25
26
  def_node_matcher :symbol_proc?, <<~PATTERN
26
- ({block numblock}
27
- ${(send ...) (super ...) zsuper}
28
- ${(args (arg _)) %Integer}
29
- (send (lvar _var) $_))
27
+ {
28
+ (block $#symbol_proc_receiver? $(args (arg _var)) (send (lvar _var) $_))
29
+ (numblock $#symbol_proc_receiver? $1 (send (lvar :_1) $_))
30
+ }
30
31
  PATTERN
31
32
 
32
33
  def self.autocorrect_incompatible_with
@@ -120,7 +120,7 @@ module RuboCop
120
120
  if condition.begin_type?
121
121
  condition.to_a.any? { |x| complex_condition?(x) }
122
122
  else
123
- non_complex_expression?(condition) ? false : true
123
+ !non_complex_expression?(condition)
124
124
  end
125
125
  end
126
126
 
@@ -41,12 +41,10 @@ module RuboCop
41
41
  'having a single-line body.'
42
42
 
43
43
  def on_while(node)
44
- return unless node.multiline? && single_line_as_modifier?(node)
44
+ return unless single_line_as_modifier?(node)
45
45
 
46
46
  add_offense(node.loc.keyword, message: format(MSG, keyword: node.keyword)) do |corrector|
47
- oneline = "#{node.body.source} #{node.keyword} #{node.condition.source}"
48
-
49
- corrector.replace(node, oneline)
47
+ corrector.replace(node, to_modifier_form(node))
50
48
  end
51
49
  end
52
50
  alias on_until on_while
@@ -33,7 +33,9 @@ module RuboCop
33
33
  end
34
34
 
35
35
  def add_parentheses(node, corrector)
36
- if node.arguments.empty?
36
+ if !node.respond_to?(:arguments)
37
+ corrector.wrap(node, '(', ')')
38
+ elsif node.arguments.empty?
37
39
  corrector.insert_after(node, '()')
38
40
  else
39
41
  corrector.replace(args_begin(node), '(')
@@ -23,6 +23,7 @@ module RuboCop
23
23
 
24
24
  def minimum_severity_to_fail
25
25
  @minimum_severity_to_fail ||= begin
26
+ # Unless given explicitly as `fail_level`, `:info` severity offenses do not fail
26
27
  name = options.fetch(:fail_level, :refactor)
27
28
  RuboCop::Cop::Severity.new(name)
28
29
  end
@@ -27,7 +27,7 @@ module RuboCop
27
27
  end
28
28
 
29
29
  def any?
30
- frozen_string_literal_specified? || encoding_specified?
30
+ frozen_string_literal_specified? || encoding_specified? || shareable_constant_value_specified?
31
31
  end
32
32
 
33
33
  # Does the magic comment enable the frozen string literal feature.
@@ -46,6 +46,10 @@ module RuboCop
46
46
  [true, false].include?(frozen_string_literal)
47
47
  end
48
48
 
49
+ def valid_shareable_constant_value?
50
+ %w[none literal experimental_everything experimental_copy].include?(shareable_constant_values)
51
+ end
52
+
49
53
  # Was a magic comment for the frozen string literal found?
50
54
  #
51
55
  # @return [Boolean]
@@ -53,6 +57,13 @@ module RuboCop
53
57
  specified?(frozen_string_literal)
54
58
  end
55
59
 
60
+ # Was a shareable_constant_value specified?
61
+ #
62
+ # @return [Boolean]
63
+ def shareable_constant_value_specified?
64
+ specified?(shareable_constant_value)
65
+ end
66
+
56
67
  # Expose the `frozen_string_literal` value coerced to a boolean if possible.
57
68
  #
58
69
  # @return [Boolean] if value is `true` or `false`
@@ -69,6 +80,13 @@ module RuboCop
69
80
  end
70
81
  end
71
82
 
83
+ # Expose the `shareable_constant_value` value coerced to a boolean if possible.
84
+ #
85
+ # @return [String] for shareable_constant_value config
86
+ def shareable_constant_value
87
+ extract_shareable_constant_value
88
+ end
89
+
72
90
  def encoding_specified?
73
91
  specified?(encoding)
74
92
  end
@@ -146,6 +164,10 @@ module RuboCop
146
164
  def extract_frozen_string_literal
147
165
  match('frozen[_-]string[_-]literal')
148
166
  end
167
+
168
+ def extract_shareable_constant_value
169
+ match('shareable[_-]constant[_-]values')
170
+ end
149
171
  end
150
172
 
151
173
  # Wrapper for Vim style magic comments.
@@ -176,6 +198,9 @@ module RuboCop
176
198
 
177
199
  # Vim comments cannot specify frozen string literal behavior.
178
200
  def frozen_string_literal; end
201
+
202
+ # Vim comments cannot specify shareable constant values behavior.
203
+ def shareable_constant_value; end
179
204
  end
180
205
 
181
206
  # Wrapper for regular magic comments not bound to an editor.
@@ -209,6 +234,10 @@ module RuboCop
209
234
  def extract_frozen_string_literal
210
235
  extract(/\A\s*#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/io)
211
236
  end
237
+
238
+ def extract_shareable_constant_value
239
+ extract(/\A\s*#\s*shareable[_-]constant[_-]value:\s*(#{TOKEN})\s*\z/io)
240
+ end
212
241
  end
213
242
  end
214
243
  end
@@ -66,7 +66,7 @@ module RuboCop
66
66
  add_configuration_options(opts)
67
67
  add_formatting_options(opts)
68
68
 
69
- option(opts, '-r', '--require FILE') { |f| require f }
69
+ option(opts, '-r', '--require FILE') { |f| require_feature(f) }
70
70
 
71
71
  add_severity_option(opts)
72
72
  add_flags_with_optional_args(opts)
@@ -92,14 +92,7 @@ module RuboCop
92
92
  raise OptionArgumentError, message
93
93
  end
94
94
 
95
- @options[:"#{option}"] =
96
- if list.empty?
97
- ['']
98
- else
99
- list.split(',').map do |c|
100
- Cop::Registry.qualified_cop_name(c, "--#{option} option")
101
- end
102
- end
95
+ @options[:"#{option}"] = list.empty? ? [''] : list.split(',')
103
96
  end
104
97
  end
105
98
 
@@ -239,6 +232,13 @@ module RuboCop
239
232
  long_opt[2..-1].sub('[no-]', '').sub(/ .*/, '')
240
233
  .tr('-', '_').gsub(/[\[\]]/, '').to_sym
241
234
  end
235
+
236
+ def require_feature(file)
237
+ # If any features were added on the CLI from `--require`,
238
+ # add them to the config.
239
+ ConfigLoader.add_loaded_features(file)
240
+ require file
241
+ end
242
242
  end
243
243
 
244
244
  # Validates option arguments and the options' compatibility with each other.
@@ -470,7 +470,7 @@ module RuboCop
470
470
  'This option applies to the previously',
471
471
  'specified --format, or the default format',
472
472
  'if no format is specified.'],
473
- fail_level: ['Minimum severity (A/R/C/W/E/F) for exit',
473
+ fail_level: ['Minimum severity (A/I/R/C/W/E/F) for exit',
474
474
  'with error code.'],
475
475
  display_time: 'Display elapsed time in seconds.',
476
476
  display_only_failed: ['Only output offense messages. Omit passing',
@@ -9,10 +9,6 @@ module CopHelper
9
9
  let(:ruby_version) { 2.4 }
10
10
  let(:rails_version) { false }
11
11
 
12
- def inspect_source_file(source)
13
- Tempfile.open('tmp') { |f| inspect_source(source, f) }
14
- end
15
-
16
12
  def inspect_source(source, file = nil)
17
13
  RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {}
18
14
  RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
@@ -111,28 +111,12 @@ module RuboCop
111
111
  source
112
112
  end
113
113
 
114
- # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
115
- def expect_offense(source, file = nil, severity: nil, **replacements)
116
- source = format_offense(source, **replacements)
117
- RuboCop::Formatter::DisabledConfigFormatter
118
- .config_to_allow_offenses = {}
119
- RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
120
- cop.instance_variable_get(:@options)[:auto_correct] = true
121
-
122
- expected_annotations = AnnotatedSource.parse(source)
123
-
124
- if expected_annotations.plain_source == source
125
- raise 'Use `expect_no_offenses` to assert that no offenses are found'
126
- end
127
-
128
- @processed_source = parse_source(expected_annotations.plain_source,
129
- file)
130
-
131
- unless @processed_source.valid_syntax?
132
- raise 'Error parsing example code: ' \
133
- "#{@processed_source.diagnostics.map(&:render).join("\n")}"
134
- end
114
+ def expect_offense(source, file = nil, severity: nil, chomp: false, **replacements)
115
+ expected_annotations = parse_annotations(source, **replacements)
116
+ source = expected_annotations.plain_source
117
+ source = source.chomp if chomp
135
118
 
119
+ @processed_source = parse_processed_source(source, file)
136
120
  @offenses = _investigate(cop, @processed_source)
137
121
  actual_annotations =
138
122
  expected_annotations.with_offense_annotations(@offenses)
@@ -143,7 +127,14 @@ module RuboCop
143
127
  @offenses
144
128
  end
145
129
 
146
- def expect_correction(correction, loop: true)
130
+ # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
131
+ def expect_correction(correction, loop: true, source: nil)
132
+ if source
133
+ expected_annotations = parse_annotations(source, raise_error: false)
134
+ @processed_source = parse_processed_source(expected_annotations.plain_source)
135
+ _investigate(cop, @processed_source)
136
+ end
137
+
147
138
  raise '`expect_correction` must follow `expect_offense`' unless @processed_source
148
139
 
149
140
  iteration = 0
@@ -192,6 +183,30 @@ module RuboCop
192
183
  expect(actual_annotations.to_s).to eq(source)
193
184
  end
194
185
 
186
+ def parse_annotations(source, raise_error: true, **replacements)
187
+ set_formatter_options
188
+
189
+ source = format_offense(source, **replacements)
190
+ annotations = AnnotatedSource.parse(source)
191
+ return annotations unless raise_error && annotations.plain_source == source
192
+
193
+ raise 'Use `expect_no_offenses` to assert that no offenses are found'
194
+ end
195
+
196
+ def parse_processed_source(source, file = nil)
197
+ processed_source = parse_source(source, file)
198
+ return processed_source if processed_source.valid_syntax?
199
+
200
+ raise 'Error parsing example code: ' \
201
+ "#{processed_source.diagnostics.map(&:render).join("\n")}"
202
+ end
203
+
204
+ def set_formatter_options
205
+ RuboCop::Formatter::DisabledConfigFormatter.config_to_allow_offenses = {}
206
+ RuboCop::Formatter::DisabledConfigFormatter.detected_styles = {}
207
+ cop.instance_variable_get(:@options)[:auto_correct] = true
208
+ end
209
+
195
210
  # Parsed representation of code annotated with the `^^^ Message` style
196
211
  class AnnotatedSource
197
212
  ANNOTATION_PATTERN = /\A\s*(\^+|\^{}) /.freeze
@@ -323,11 +323,16 @@ module RuboCop
323
323
  Cop::Team.mobilize(mobilized_cop_classes(config), config, @options)
324
324
  end
325
325
 
326
- def mobilized_cop_classes(config)
326
+ def mobilized_cop_classes(config) # rubocop:disable Metrics/AbcSize
327
327
  @mobilized_cop_classes ||= {}.compare_by_identity
328
328
  @mobilized_cop_classes[config] ||= begin
329
329
  cop_classes = Cop::Registry.all
330
330
 
331
+ # `@options[:only]` and `@options[:except]` are not qualified until
332
+ # needed so that the Registry can be fully loaded, including any
333
+ # cops added by `require`s.
334
+ qualify_option_cop_names
335
+
331
336
  OptionsValidator.new(@options).validate_cop_options
332
337
 
333
338
  if @options[:only]
@@ -342,6 +347,16 @@ module RuboCop
342
347
  end
343
348
  end
344
349
 
350
+ def qualify_option_cop_names
351
+ %i[only except].each do |option|
352
+ next unless @options[option]
353
+
354
+ @options[option].map! do |cop_name|
355
+ Cop::Registry.qualified_cop_name(cop_name, "--#{option} option")
356
+ end
357
+ end
358
+ end
359
+
345
360
  def filter_cop_classes(cop_classes, config)
346
361
  # use only cops that link to a style guide if requested
347
362
  return unless style_guide_cops_only?(config)
@@ -375,6 +390,7 @@ module RuboCop
375
390
 
376
391
  def minimum_severity_to_fail
377
392
  @minimum_severity_to_fail ||= begin
393
+ # Unless given explicitly as `fail_level`, `:info` severity offenses do not fail
378
394
  name = @options[:fail_level] || :refactor
379
395
  RuboCop::Cop::Severity.new(name)
380
396
  end
@@ -58,7 +58,7 @@ module RuboCop
58
58
  base_dir = base_dir.gsub(File::ALT_SEPARATOR, File::SEPARATOR) if File::ALT_SEPARATOR
59
59
  all_files = find_files(base_dir, File::FNM_DOTMATCH)
60
60
  # use file.include? for performance optimization
61
- hidden_files = all_files.select { |file| file.include?(HIDDEN_PATH_SUBSTRING) }
61
+ hidden_files = all_files.select { |file| file.include?(HIDDEN_PATH_SUBSTRING) }.sort
62
62
  base_dir_config = @config_store.for(base_dir)
63
63
 
64
64
  target_files = all_files.select do |file|
@@ -70,7 +70,9 @@ module RuboCop
70
70
 
71
71
  def to_inspect?(file, hidden_files, base_dir_config)
72
72
  return false if base_dir_config.file_to_exclude?(file)
73
- return true if !hidden_files.include?(file) && ruby_file?(file)
73
+ return true if !hidden_files.bsearch do |hidden_file|
74
+ file <=> hidden_file
75
+ end && ruby_file?(file)
74
76
 
75
77
  base_dir_config.file_to_include?(file)
76
78
  end
@@ -44,33 +44,61 @@ module RuboCop
44
44
  # The target ruby version may be found in a .ruby-version file.
45
45
  # @api private
46
46
  class RubyVersionFile < Source
47
- FILENAME = '.ruby-version'
47
+ RUBY_VERSION_FILENAME = '.ruby-version'
48
+ RUBY_VERSION_PATTERN = /\A(?:ruby-)?(?<version>\d+\.\d+)/.freeze
48
49
 
49
50
  def name
50
- "`#{FILENAME}`"
51
+ "`#{RUBY_VERSION_FILENAME}`"
51
52
  end
52
53
 
53
54
  private
54
55
 
56
+ def filename
57
+ RUBY_VERSION_FILENAME
58
+ end
59
+
60
+ def pattern
61
+ RUBY_VERSION_PATTERN
62
+ end
63
+
55
64
  def find_version
56
- file = ruby_version_file
65
+ file = version_file
57
66
  return unless file && File.file?(file)
58
67
 
59
- # rubocop:disable Lint/MixedRegexpCaptureTypes
60
- # `(ruby-)` is not a capture type.
61
- File.read(file).match(/\A(ruby-)?(?<version>\d+\.\d+)/) do |md|
62
- # rubocop:enable Lint/MixedRegexpCaptureTypes
68
+ File.read(file).match(pattern) do |md|
63
69
  md[:version].to_f
64
70
  end
65
71
  end
66
72
 
67
- def ruby_version_file
68
- @ruby_version_file ||=
69
- @config.find_file_upwards(FILENAME,
73
+ def version_file
74
+ @version_file ||=
75
+ @config.find_file_upwards(filename,
70
76
  @config.base_dir_for_path_parameters)
71
77
  end
72
78
  end
73
79
 
80
+ # The target ruby version may be found in a .tool-versions file, in a line
81
+ # starting with `ruby`.
82
+ # @api private
83
+ class ToolVersionsFile < RubyVersionFile
84
+ TOOL_VERSIONS_FILENAME = '.tool-versions'
85
+ TOOL_VERSIONS_PATTERN = /\Aruby (?:ruby-)?(?<version>\d+\.\d+)/.freeze
86
+
87
+ def name
88
+ "`#{TOOL_VERSIONS_FILENAME}`"
89
+ end
90
+
91
+ private
92
+
93
+ def filename
94
+ TOOL_VERSIONS_FILENAME
95
+ end
96
+
97
+ def pattern
98
+ TOOL_VERSIONS_PATTERN
99
+ end
100
+ end
101
+
74
102
  # The lock file of Bundler may identify the target ruby version.
75
103
  # @api private
76
104
  class BundlerLockFile < Source
@@ -194,7 +222,15 @@ module RuboCop
194
222
  KNOWN_RUBIES
195
223
  end
196
224
 
197
- SOURCES = [RuboCopConfig, RubyVersionFile, BundlerLockFile, GemspecFile, Default].freeze
225
+ SOURCES = [
226
+ RuboCopConfig,
227
+ RubyVersionFile,
228
+ ToolVersionsFile,
229
+ BundlerLockFile,
230
+ GemspecFile,
231
+ Default
232
+ ].freeze
233
+
198
234
  private_constant :SOURCES
199
235
 
200
236
  def initialize(config)
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # This module contains a collection of useful utility methods.
5
+ module Util
6
+ def self.silence_warnings
7
+ # Replaces Kernel::silence_warnings since it hides any warnings,
8
+ # including the RuboCop ones
9
+ old_verbose = $VERBOSE
10
+ $VERBOSE = nil
11
+ yield
12
+ ensure
13
+ $VERBOSE = old_verbose
14
+ end
15
+ end
16
+ end