rubocop 1.1.0 → 1.4.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.
- checksums.yaml +4 -4
- data/README.md +29 -12
- data/config/default.yml +113 -16
- data/exe/rubocop +1 -1
- data/lib/rubocop.rb +9 -0
- data/lib/rubocop/cli/command/execute_runner.rb +26 -11
- data/lib/rubocop/config_loader.rb +14 -5
- data/lib/rubocop/config_regeneration.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +3 -3
- data/lib/rubocop/cop/bundler/gem_comment.rb +1 -1
- data/lib/rubocop/cop/commissioner.rb +1 -1
- data/lib/rubocop/cop/correctors/percent_literal_corrector.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +3 -3
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +4 -5
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +1 -1
- data/lib/rubocop/cop/generator.rb +2 -9
- data/lib/rubocop/cop/generator/configuration_injector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +1 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +3 -4
- data/lib/rubocop/cop/layout/class_structure.rb +15 -3
- data/lib/rubocop/cop/layout/else_alignment.rb +15 -2
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +77 -7
- data/lib/rubocop/cop/layout/end_alignment.rb +3 -3
- data/lib/rubocop/cop/layout/hash_alignment.rb +4 -4
- data/lib/rubocop/cop/layout/line_length.rb +8 -1
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +24 -18
- data/lib/rubocop/cop/layout/space_around_method_call_operator.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_parens.rb +35 -13
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +2 -1
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +26 -2
- data/lib/rubocop/cop/lint/debugger.rb +17 -27
- data/lib/rubocop/cop/lint/duplicate_branch.rb +93 -0
- data/lib/rubocop/cop/lint/duplicate_case_condition.rb +2 -12
- data/lib/rubocop/cop/lint/else_layout.rb +29 -3
- data/lib/rubocop/cop/lint/empty_block.rb +38 -2
- data/lib/rubocop/cop/lint/empty_class.rb +93 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +22 -4
- data/lib/rubocop/cop/lint/loop.rb +4 -4
- data/lib/rubocop/cop/lint/missing_super.rb +7 -4
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +14 -0
- data/lib/rubocop/cop/lint/no_return_in_begin_end_blocks.rb +58 -0
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +19 -16
- data/lib/rubocop/cop/lint/shadowed_exception.rb +4 -5
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +6 -15
- data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +13 -4
- data/lib/rubocop/cop/lint/useless_method_definition.rb +2 -4
- data/lib/rubocop/cop/lint/useless_setter_call.rb +6 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +3 -3
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +9 -4
- data/lib/rubocop/cop/mixin/visibility_help.rb +1 -3
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +11 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +11 -5
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +67 -18
- data/lib/rubocop/cop/naming/variable_number.rb +98 -8
- data/lib/rubocop/cop/style/and_or.rb +1 -3
- data/lib/rubocop/cop/style/bisected_attr_accessor.rb +0 -4
- data/lib/rubocop/cop/style/case_like_if.rb +0 -4
- data/lib/rubocop/cop/style/collection_compact.rb +91 -0
- data/lib/rubocop/cop/style/document_dynamic_eval_definition.rb +107 -5
- data/lib/rubocop/cop/style/documentation.rb +12 -1
- data/lib/rubocop/cop/style/double_negation.rb +6 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +3 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +7 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +37 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -3
- data/lib/rubocop/cop/style/infinite_loop.rb +4 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +12 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
- data/lib/rubocop/cop/style/mixin_grouping.rb +0 -4
- data/lib/rubocop/cop/style/multiple_comparison.rb +3 -2
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +106 -0
- data/lib/rubocop/cop/style/nil_lambda.rb +52 -0
- data/lib/rubocop/cop/style/raise_args.rb +21 -6
- data/lib/rubocop/cop/style/redundant_argument.rb +73 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +1 -1
- data/lib/rubocop/cop/style/static_class.rb +97 -0
- data/lib/rubocop/cop/style/while_until_modifier.rb +9 -0
- data/lib/rubocop/cop/util.rb +5 -1
- data/lib/rubocop/cop/variable_force/branch.rb +1 -1
- data/lib/rubocop/cop/variable_force/scope.rb +1 -1
- data/lib/rubocop/ext/regexp_node.rb +10 -5
- data/lib/rubocop/ext/regexp_parser.rb +9 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +21 -6
- data/lib/rubocop/formatter/formatter_set.rb +1 -0
- data/lib/rubocop/formatter/git_hub_actions_formatter.rb +47 -0
- data/lib/rubocop/options.rb +7 -0
- data/lib/rubocop/rake_task.rb +2 -2
- data/lib/rubocop/runner.rb +1 -1
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/target_ruby.rb +65 -1
- data/lib/rubocop/version.rb +1 -1
- metadata +14 -8
- data/bin/console +0 -10
- data/bin/rubocop-profile +0 -32
- data/bin/setup +0 -7
@@ -82,7 +82,7 @@ module RuboCop
|
|
82
82
|
|
83
83
|
def each_escape(node)
|
84
84
|
node.parsed_tree&.traverse&.reduce(0) do |char_class_depth, (event, expr)|
|
85
|
-
yield(expr.text[1], expr.
|
85
|
+
yield(expr.text[1], expr.start_index, !char_class_depth.zero?) if expr.type == :escape
|
86
86
|
|
87
87
|
if expr.type == :set
|
88
88
|
char_class_depth + (event == :enter ? 1 : -1)
|
@@ -0,0 +1,97 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for places where classes with only class methods can be
|
7
|
+
# replaced with a module. Classes should be used only when it makes sense to create
|
8
|
+
# instances out of them.
|
9
|
+
#
|
10
|
+
# This cop is marked as unsafe, because it is possible that this class is a parent
|
11
|
+
# for some other subclass, monkey-patched with instance methods or
|
12
|
+
# a dummy instance is instantiated from it somewhere.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
# # bad
|
16
|
+
# class SomeClass
|
17
|
+
# def self.some_method
|
18
|
+
# # body omitted
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# def self.some_other_method
|
22
|
+
# # body omitted
|
23
|
+
# end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# module SomeModule
|
28
|
+
# module_function
|
29
|
+
#
|
30
|
+
# def some_method
|
31
|
+
# # body omitted
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# def some_other_method
|
35
|
+
# # body omitted
|
36
|
+
# end
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# # good - has instance method
|
40
|
+
# class SomeClass
|
41
|
+
# def instance_method; end
|
42
|
+
# def self.class_method; end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
class StaticClass < Base
|
46
|
+
include VisibilityHelp
|
47
|
+
|
48
|
+
MSG = 'Prefer modules to classes with only class methods.'
|
49
|
+
|
50
|
+
def on_class(class_node)
|
51
|
+
return if class_node.parent_class
|
52
|
+
|
53
|
+
add_offense(class_node) if class_convertible_to_module?(class_node)
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def class_convertible_to_module?(class_node)
|
59
|
+
nodes = class_elements(class_node)
|
60
|
+
return false if nodes.empty?
|
61
|
+
|
62
|
+
nodes.all? do |node|
|
63
|
+
node_visibility(node) == :public &&
|
64
|
+
node.defs_type? ||
|
65
|
+
sclass_convertible_to_module?(node) ||
|
66
|
+
node.equals_asgn? ||
|
67
|
+
extend_call?(node)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def extend_call?(node)
|
72
|
+
node.send_type? && node.method?(:extend)
|
73
|
+
end
|
74
|
+
|
75
|
+
def sclass_convertible_to_module?(node)
|
76
|
+
return false unless node.sclass_type?
|
77
|
+
|
78
|
+
class_elements(node).all? do |child|
|
79
|
+
node_visibility(child) == :public && (child.def_type? || child.equals_asgn?)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def class_elements(class_node)
|
84
|
+
class_def = class_node.body
|
85
|
+
|
86
|
+
if !class_def
|
87
|
+
[]
|
88
|
+
elsif class_def.begin_type?
|
89
|
+
class_def.children
|
90
|
+
else
|
91
|
+
[class_def]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
@@ -24,6 +24,15 @@ module RuboCop
|
|
24
24
|
#
|
25
25
|
# # good
|
26
26
|
# x += 1 until x > 10
|
27
|
+
#
|
28
|
+
# @example
|
29
|
+
# # bad
|
30
|
+
# x += 100 while x < 500 # a long comment that makes code too long if it were a single line
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# while x < 500 # a long comment that makes code too long if it were a single line
|
34
|
+
# x += 100
|
35
|
+
# end
|
27
36
|
class WhileUntilModifier < Base
|
28
37
|
include StatementModifier
|
29
38
|
extend AutoCorrector
|
data/lib/rubocop/cop/util.rb
CHANGED
@@ -53,7 +53,7 @@ module RuboCop
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def on_node(syms, sexp, excludes = [], &block)
|
56
|
-
return to_enum(:on_node, syms, sexp, excludes) unless
|
56
|
+
return to_enum(:on_node, syms, sexp, excludes) unless block
|
57
57
|
|
58
58
|
yield sexp if Array(syms).include?(sexp.type)
|
59
59
|
return if Array(excludes).include?(sexp.type)
|
@@ -123,6 +123,10 @@ module RuboCop
|
|
123
123
|
node1.loc.line == node2.loc.line
|
124
124
|
end
|
125
125
|
|
126
|
+
def indent(node)
|
127
|
+
' ' * node.loc.column
|
128
|
+
end
|
129
|
+
|
126
130
|
def to_supported_styles(enforced_style)
|
127
131
|
enforced_style
|
128
132
|
.sub(/^Enforced/, 'Supported')
|
@@ -78,7 +78,7 @@ module RuboCop
|
|
78
78
|
end
|
79
79
|
|
80
80
|
def each_ancestor(include_self: false, &block)
|
81
|
-
return to_enum(__method__, include_self: include_self) unless
|
81
|
+
return to_enum(__method__, include_self: include_self) unless block
|
82
82
|
|
83
83
|
yield self if include_self
|
84
84
|
scan_ancestors(&block)
|
@@ -19,13 +19,18 @@ module RuboCop
|
|
19
19
|
super
|
20
20
|
|
21
21
|
str = with_interpolations_blanked
|
22
|
-
|
23
|
-
Regexp::Parser.parse(str, options: options)
|
22
|
+
begin
|
23
|
+
@parsed_tree = Regexp::Parser.parse(str, options: options)
|
24
24
|
rescue StandardError
|
25
|
-
nil
|
25
|
+
@parsed_tree = nil
|
26
|
+
else
|
27
|
+
origin = loc.begin.end
|
28
|
+
source = @parsed_tree.to_s
|
29
|
+
@parsed_tree.each_expression(true) do |e|
|
30
|
+
e.origin = origin
|
31
|
+
e.source = source
|
32
|
+
end
|
26
33
|
end
|
27
|
-
origin = loc.begin.end
|
28
|
-
@parsed_tree&.each_expression(true) { |e| e.origin = origin }
|
29
34
|
end
|
30
35
|
|
31
36
|
def each_capture(named: ANY)
|
@@ -20,11 +20,18 @@ module RuboCop
|
|
20
20
|
module Expression
|
21
21
|
# Add `expression` and `loc` to all `regexp_parser` nodes
|
22
22
|
module Base
|
23
|
-
attr_accessor :origin
|
23
|
+
attr_accessor :origin, :source
|
24
|
+
|
25
|
+
def start_index
|
26
|
+
# ts is a byte index; convert it to a character index
|
27
|
+
@start_index ||= source.byteslice(0, ts).length
|
28
|
+
end
|
24
29
|
|
25
30
|
# Shortcut to `loc.expression`
|
26
31
|
def expression
|
27
|
-
@expression ||=
|
32
|
+
@expression ||= begin
|
33
|
+
origin.adjust(begin_pos: start_index, end_pos: start_index + full_length)
|
34
|
+
end
|
28
35
|
end
|
29
36
|
|
30
37
|
# @returns a location map like `parser` does, with:
|
@@ -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
|
-
#
|
200
|
-
#
|
201
|
-
#
|
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
|
-
(
|
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)
|
@@ -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
|
data/lib/rubocop/options.rb
CHANGED
@@ -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')
|
@@ -469,6 +472,7 @@ module RuboCop
|
|
469
472
|
'if no format is specified.'],
|
470
473
|
fail_level: ['Minimum severity (A/R/C/W/E/F) for exit',
|
471
474
|
'with error code.'],
|
475
|
+
display_time: 'Display elapsed time in seconds.',
|
472
476
|
display_only_failed: ['Only output offense messages. Omit passing',
|
473
477
|
'cops. Only valid for --format junit.'],
|
474
478
|
display_only_fail_level_offenses:
|
@@ -496,6 +500,9 @@ module RuboCop
|
|
496
500
|
extra_details: 'Display extra details in offense messages.',
|
497
501
|
lint: 'Run only lint cops.',
|
498
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.'],
|
499
506
|
list_target_files: 'List all files RuboCop will inspect.',
|
500
507
|
auto_correct: 'Auto-correct offenses (only when it\'s safe).',
|
501
508
|
safe_auto_correct: '(same, deprecated)',
|
data/lib/rubocop/rake_task.rb
CHANGED
@@ -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
|
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
|
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)
|
data/lib/rubocop/runner.rb
CHANGED
@@ -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
|
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
|
data/lib/rubocop/target_ruby.rb
CHANGED
@@ -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)
|