rubocop 0.70.0 → 0.72.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.
- checksums.yaml +4 -4
- data/README.md +5 -10
- data/config/default.yml +50 -491
- data/lib/rubocop.rb +5 -53
- data/lib/rubocop/ast/builder.rb +2 -0
- data/lib/rubocop/ast/node.rb +1 -1
- data/lib/rubocop/ast/node/float_node.rb +12 -0
- data/lib/rubocop/ast/node/int_node.rb +12 -0
- data/lib/rubocop/ast/node/mixin/numeric_node.rb +21 -0
- data/lib/rubocop/ast/node/resbody_node.rb +1 -6
- data/lib/rubocop/cached_data.rb +1 -1
- data/lib/rubocop/config.rb +35 -6
- data/lib/rubocop/config_loader.rb +2 -2
- data/lib/rubocop/config_loader_resolver.rb +0 -6
- data/lib/rubocop/cop/cop.rb +0 -4
- data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +55 -0
- data/lib/rubocop/cop/layout/class_structure.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +3 -1
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +4 -0
- data/lib/rubocop/cop/layout/indent_first_argument.rb +6 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +7 -3
- data/lib/rubocop/cop/layout/indent_heredoc.rb +0 -1
- data/lib/rubocop/cop/layout/indentation_consistency.rb +13 -12
- data/lib/rubocop/cop/layout/indentation_width.rb +8 -4
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +2 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_alignment.rb +4 -0
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +20 -22
- data/lib/rubocop/cop/style/commented_keyword.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +2 -1
- data/lib/rubocop/cop/style/float_division.rb +94 -0
- data/lib/rubocop/cop/style/format_string.rb +7 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +42 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +12 -2
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -0
- data/lib/rubocop/cop/style/word_array.rb +2 -2
- data/lib/rubocop/cop/style/zero_length_predicate.rb +1 -1
- data/lib/rubocop/node_pattern.rb +84 -5
- data/lib/rubocop/options.rb +0 -2
- data/lib/rubocop/processed_source.rb +5 -1
- data/lib/rubocop/rspec/cop_helper.rb +0 -1
- data/lib/rubocop/rspec/shared_contexts.rb +0 -17
- data/lib/rubocop/rspec/support.rb +0 -1
- data/lib/rubocop/runner.rb +6 -7
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +8 -2
- metadata +7 -69
- data/lib/rubocop/cop/mixin/target_rails_version.rb +0 -16
- data/lib/rubocop/cop/rails/action_filter.rb +0 -117
- data/lib/rubocop/cop/rails/active_record_aliases.rb +0 -48
- data/lib/rubocop/cop/rails/active_record_override.rb +0 -82
- data/lib/rubocop/cop/rails/active_support_aliases.rb +0 -69
- data/lib/rubocop/cop/rails/application_job.rb +0 -40
- data/lib/rubocop/cop/rails/application_record.rb +0 -40
- data/lib/rubocop/cop/rails/assert_not.rb +0 -44
- data/lib/rubocop/cop/rails/belongs_to.rb +0 -102
- data/lib/rubocop/cop/rails/blank.rb +0 -164
- data/lib/rubocop/cop/rails/bulk_change_table.rb +0 -289
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +0 -91
- data/lib/rubocop/cop/rails/date.rb +0 -161
- data/lib/rubocop/cop/rails/delegate.rb +0 -132
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +0 -37
- data/lib/rubocop/cop/rails/dynamic_find_by.rb +0 -91
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +0 -45
- data/lib/rubocop/cop/rails/environment_comparison.rb +0 -68
- data/lib/rubocop/cop/rails/exit.rb +0 -67
- data/lib/rubocop/cop/rails/file_path.rb +0 -108
- data/lib/rubocop/cop/rails/find_by.rb +0 -55
- data/lib/rubocop/cop/rails/find_each.rb +0 -51
- data/lib/rubocop/cop/rails/has_and_belongs_to_many.rb +0 -25
- data/lib/rubocop/cop/rails/has_many_or_has_one_dependent.rb +0 -106
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +0 -117
- data/lib/rubocop/cop/rails/http_status.rb +0 -179
- data/lib/rubocop/cop/rails/ignored_skip_action_filter_option.rb +0 -94
- data/lib/rubocop/cop/rails/inverse_of.rb +0 -246
- data/lib/rubocop/cop/rails/lexically_scoped_action_filter.rb +0 -175
- data/lib/rubocop/cop/rails/link_to_blank.rb +0 -98
- data/lib/rubocop/cop/rails/not_null_column.rb +0 -67
- data/lib/rubocop/cop/rails/output.rb +0 -49
- data/lib/rubocop/cop/rails/output_safety.rb +0 -99
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +0 -107
- data/lib/rubocop/cop/rails/presence.rb +0 -124
- data/lib/rubocop/cop/rails/present.rb +0 -153
- data/lib/rubocop/cop/rails/read_write_attribute.rb +0 -74
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +0 -111
- data/lib/rubocop/cop/rails/redundant_receiver_in_with_options.rb +0 -136
- data/lib/rubocop/cop/rails/reflection_class_name.rb +0 -37
- data/lib/rubocop/cop/rails/refute_methods.rb +0 -76
- data/lib/rubocop/cop/rails/relative_date_constant.rb +0 -93
- data/lib/rubocop/cop/rails/request_referer.rb +0 -56
- data/lib/rubocop/cop/rails/reversible_migration.rb +0 -286
- data/lib/rubocop/cop/rails/safe_navigation.rb +0 -87
- data/lib/rubocop/cop/rails/save_bang.rb +0 -316
- data/lib/rubocop/cop/rails/scope_args.rb +0 -29
- data/lib/rubocop/cop/rails/skips_model_validations.rb +0 -87
- data/lib/rubocop/cop/rails/time_zone.rb +0 -238
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +0 -105
- data/lib/rubocop/cop/rails/unknown_env.rb +0 -63
- data/lib/rubocop/cop/rails/validation.rb +0 -109
- data/lib/rubocop/rspec/shared_examples.rb +0 -59
@@ -83,7 +83,7 @@ module RuboCop
|
|
83
83
|
|
84
84
|
check_indentation(end_loc, node.body)
|
85
85
|
|
86
|
-
return unless
|
86
|
+
return unless indented_internal_methods_style?
|
87
87
|
|
88
88
|
check_members(end_loc, [node.body])
|
89
89
|
end
|
@@ -155,8 +155,8 @@ module RuboCop
|
|
155
155
|
|
156
156
|
return unless members.any? && members.first.begin_type?
|
157
157
|
|
158
|
-
if indentation_consistency_style == '
|
159
|
-
|
158
|
+
if indentation_consistency_style == 'indented_internal_methods'
|
159
|
+
check_members_for_indented_internal_methods_style(members)
|
160
160
|
else
|
161
161
|
members.first.children.each do |member|
|
162
162
|
next if member.send_type? && member.access_modifier?
|
@@ -176,7 +176,7 @@ module RuboCop
|
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
179
|
-
def
|
179
|
+
def check_members_for_indented_internal_methods_style(members)
|
180
180
|
each_member(members) do |member, previous_modifier|
|
181
181
|
check_indentation(previous_modifier, member,
|
182
182
|
indentation_consistency_style)
|
@@ -195,6 +195,10 @@ module RuboCop
|
|
195
195
|
end
|
196
196
|
end
|
197
197
|
|
198
|
+
def indented_internal_methods_style?
|
199
|
+
indentation_consistency_style == 'indented_internal_methods'
|
200
|
+
end
|
201
|
+
|
198
202
|
def indentation_consistency_style
|
199
203
|
config.for_cop('Layout/IndentationConsistency')['EnforcedStyle']
|
200
204
|
end
|
@@ -57,24 +57,20 @@ module RuboCop
|
|
57
57
|
MSG = 'Use `%<preferred>s` instead of `%<bad>s`.'
|
58
58
|
|
59
59
|
def on_resbody(node)
|
60
|
-
|
61
|
-
return unless
|
62
|
-
|
63
|
-
@exception_name ||= exception_type.children.first
|
64
|
-
return if @exception_name.const_type? ||
|
65
|
-
variable_name == preferred_name
|
60
|
+
name = variable_name(node)
|
61
|
+
return unless name
|
62
|
+
return if preferred_name(name).to_sym == name
|
66
63
|
|
67
64
|
add_offense(node, location: offense_range(node))
|
68
65
|
end
|
69
66
|
|
70
67
|
def autocorrect(node)
|
71
68
|
lambda do |corrector|
|
72
|
-
offending_name = node
|
69
|
+
offending_name = variable_name(node)
|
70
|
+
preferred_name = preferred_name(offending_name)
|
73
71
|
corrector.replace(offense_range(node), preferred_name)
|
74
72
|
|
75
|
-
|
76
|
-
|
77
|
-
node.body.each_descendant(:lvar) do |var|
|
73
|
+
node.body&.each_descendant(:lvar) do |var|
|
78
74
|
next unless var.children.first == offending_name
|
79
75
|
|
80
76
|
corrector.replace(var.loc.expression, preferred_name)
|
@@ -89,24 +85,26 @@ module RuboCop
|
|
89
85
|
variable.loc.expression
|
90
86
|
end
|
91
87
|
|
92
|
-
def preferred_name
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
88
|
+
def preferred_name(variable_name)
|
89
|
+
preferred_name = cop_config.fetch('PreferredName', 'e')
|
90
|
+
if variable_name.to_s.start_with?('_')
|
91
|
+
"_#{preferred_name}"
|
92
|
+
else
|
93
|
+
preferred_name
|
97
94
|
end
|
98
95
|
end
|
99
96
|
|
100
|
-
def variable_name
|
101
|
-
|
102
|
-
|
97
|
+
def variable_name(node)
|
98
|
+
asgn_node = node.exception_variable
|
99
|
+
return unless asgn_node
|
103
100
|
|
104
|
-
|
105
|
-
@exception_name.loc.expression
|
101
|
+
asgn_node.children.last
|
106
102
|
end
|
107
103
|
|
108
|
-
def message(
|
109
|
-
|
104
|
+
def message(node)
|
105
|
+
offending_name = variable_name(node)
|
106
|
+
preferred_name = preferred_name(offending_name)
|
107
|
+
format(MSG, preferred: preferred_name, bad: offending_name)
|
110
108
|
end
|
111
109
|
end
|
112
110
|
end
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
# This cop checks for comments put on the same line as some keywords.
|
7
7
|
# These keywords are: `begin`, `class`, `def`, `end`, `module`.
|
8
8
|
#
|
9
|
-
# Note that some comments (`:nodoc:`, `:yields
|
9
|
+
# Note that some comments (`:nodoc:`, `:yields:`, and `rubocop:disable`)
|
10
10
|
# are allowed.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -596,7 +596,8 @@ module RuboCop
|
|
596
596
|
|
597
597
|
remove_whitespace_in_branches(corrector, branch, condition, column)
|
598
598
|
|
599
|
-
branch_else = branch.parent.loc.else
|
599
|
+
return unless (branch_else = branch.parent.loc.else)
|
600
|
+
|
600
601
|
corrector.remove_preceding(branch_else, branch_else.column - column)
|
601
602
|
end
|
602
603
|
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop checks for division with integers coerced to floats.
|
7
|
+
# It is recommended to either always use `fdiv` or coerce one side only.
|
8
|
+
# This cop also provides other options for code consistency.
|
9
|
+
#
|
10
|
+
# @example EnforcedStyle: single_coerce (default)
|
11
|
+
# # bad
|
12
|
+
# a.to_f / b.to_f
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# a.to_f / b
|
16
|
+
# a / b.to_f
|
17
|
+
#
|
18
|
+
# @example EnforcedStyle: left_coerce
|
19
|
+
# # bad
|
20
|
+
# a / b.to_f
|
21
|
+
# a.to_f / b.to_f
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# a.to_f / b
|
25
|
+
#
|
26
|
+
# @example EnforcedStyle: right_coerce
|
27
|
+
# # bad
|
28
|
+
# a.to_f / b
|
29
|
+
# a.to_f / b.to_f
|
30
|
+
#
|
31
|
+
# # good
|
32
|
+
# a / b.to_f
|
33
|
+
#
|
34
|
+
# @example EnforcedStyle: fdiv
|
35
|
+
# # bad
|
36
|
+
# a / b.to_f
|
37
|
+
# a.to_f / b
|
38
|
+
# a.to_f / b.to_f
|
39
|
+
#
|
40
|
+
# # good
|
41
|
+
# a.fdiv(b)
|
42
|
+
class FloatDivision < Cop
|
43
|
+
include ConfigurableEnforcedStyle
|
44
|
+
|
45
|
+
def_node_matcher :right_coerce?, <<-PATTERN
|
46
|
+
(send _ :/ (send _ :to_f))
|
47
|
+
PATTERN
|
48
|
+
def_node_matcher :left_coerce?, <<-PATTERN
|
49
|
+
(send (send _ :to_f) :/ _)
|
50
|
+
PATTERN
|
51
|
+
def_node_matcher :both_coerce?, <<-PATTERN
|
52
|
+
(send (send _ :to_f) :/ (send _ :to_f))
|
53
|
+
PATTERN
|
54
|
+
def_node_matcher :any_coerce?, <<-PATTERN
|
55
|
+
{(send _ :/ (send _ :to_f)) (send (send _ :to_f) :/ _)}
|
56
|
+
PATTERN
|
57
|
+
|
58
|
+
def on_send(node)
|
59
|
+
add_offense(node) if offense_condition?(node)
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def offense_condition?(node)
|
65
|
+
case style
|
66
|
+
when :left_coerce
|
67
|
+
right_coerce?(node)
|
68
|
+
when :right_coerce
|
69
|
+
left_coerce?(node)
|
70
|
+
when :single_coerce
|
71
|
+
both_coerce?(node)
|
72
|
+
when :fdiv
|
73
|
+
any_coerce?(node)
|
74
|
+
else
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def message(_node)
|
80
|
+
case style
|
81
|
+
when :left_coerce
|
82
|
+
'Prefer using `.to_f` on the left side.'
|
83
|
+
when :right_coerce
|
84
|
+
'Prefer using `.to_f` on the right side.'
|
85
|
+
when :single_coerce
|
86
|
+
'Prefer using `.to_f` on one side only.'
|
87
|
+
when :fdiv
|
88
|
+
'Prefer using `fdiv` for float divisions.'
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -48,6 +48,10 @@ module RuboCop
|
|
48
48
|
}
|
49
49
|
PATTERN
|
50
50
|
|
51
|
+
def_node_matcher :variable_argument?, <<-PATTERN
|
52
|
+
(send {str dstr} :% {send_type? lvar_type?})
|
53
|
+
PATTERN
|
54
|
+
|
51
55
|
def on_send(node)
|
52
56
|
formatter(node) do |selector|
|
53
57
|
detected_style = selector == :% ? :percent : selector
|
@@ -70,10 +74,10 @@ module RuboCop
|
|
70
74
|
end
|
71
75
|
|
72
76
|
def autocorrect(node)
|
73
|
-
|
74
|
-
detected_method = node.method_name
|
77
|
+
return if variable_argument?(node)
|
75
78
|
|
76
|
-
|
79
|
+
lambda do |corrector|
|
80
|
+
case node.method_name
|
77
81
|
when :%
|
78
82
|
autocorrect_from_percent(corrector, node)
|
79
83
|
when :format, :sprintf
|
@@ -27,6 +27,37 @@ module RuboCop
|
|
27
27
|
# else
|
28
28
|
# action_c
|
29
29
|
# end
|
30
|
+
#
|
31
|
+
# @example AllowIfModifier: false (default)
|
32
|
+
# # bad
|
33
|
+
# if condition_a
|
34
|
+
# action_a
|
35
|
+
# else
|
36
|
+
# action_b if condition_b
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# # good
|
40
|
+
# if condition_a
|
41
|
+
# action_a
|
42
|
+
# elsif condition_b
|
43
|
+
# action_b
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
# @example AllowIfModifier: true
|
47
|
+
# # good
|
48
|
+
# if condition_a
|
49
|
+
# action_a
|
50
|
+
# else
|
51
|
+
# action_b if condition_b
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
# # good
|
55
|
+
# if condition_a
|
56
|
+
# action_a
|
57
|
+
# elsif condition_b
|
58
|
+
# action_b
|
59
|
+
# end
|
60
|
+
#
|
30
61
|
class IfInsideElse < Cop
|
31
62
|
MSG = 'Convert `if` nested inside `else` to `elsif`.'
|
32
63
|
|
@@ -36,9 +67,20 @@ module RuboCop
|
|
36
67
|
else_branch = node.else_branch
|
37
68
|
|
38
69
|
return unless else_branch&.if_type? && else_branch&.if?
|
70
|
+
return if allow_if_modifier_in_else_branch?(else_branch)
|
39
71
|
|
40
72
|
add_offense(else_branch, location: :keyword)
|
41
73
|
end
|
74
|
+
|
75
|
+
private
|
76
|
+
|
77
|
+
def allow_if_modifier_in_else_branch?(else_branch)
|
78
|
+
allow_if_modifier? && else_branch&.modifier_form?
|
79
|
+
end
|
80
|
+
|
81
|
+
def allow_if_modifier?
|
82
|
+
cop_config['AllowIfModifier']
|
83
|
+
end
|
42
84
|
end
|
43
85
|
end
|
44
86
|
end
|
@@ -348,7 +348,8 @@ module RuboCop
|
|
348
348
|
end
|
349
349
|
|
350
350
|
def ambigious_literal?(node)
|
351
|
-
splat?(node) || ternary_if?(node) || regexp_slash_literal?(node)
|
351
|
+
splat?(node) || ternary_if?(node) || regexp_slash_literal?(node) ||
|
352
|
+
unary_literal?(node)
|
352
353
|
end
|
353
354
|
|
354
355
|
def splat?(node)
|
@@ -371,6 +372,11 @@ module RuboCop
|
|
371
372
|
node.regexp_type? && node.loc.begin.source == '/'
|
372
373
|
end
|
373
374
|
|
375
|
+
def unary_literal?(node)
|
376
|
+
node.numeric_type? && node.sign? ||
|
377
|
+
node.parent&.send_type? && node.parent&.unary_operation?
|
378
|
+
end
|
379
|
+
|
374
380
|
def assigned_before?(node, target)
|
375
381
|
node.assignment? &&
|
376
382
|
node.loc.operator.begin < target.loc.begin
|
@@ -108,7 +108,7 @@ module RuboCop
|
|
108
108
|
end
|
109
109
|
|
110
110
|
def non_complex_send?(node)
|
111
|
-
return false unless node.
|
111
|
+
return false unless node.call_type?
|
112
112
|
|
113
113
|
!node.operator_method? || node.method?(:[])
|
114
114
|
end
|
@@ -149,7 +149,8 @@ module RuboCop
|
|
149
149
|
|
150
150
|
def unsafe_autocorrect?(condition)
|
151
151
|
condition.children.any? do |child|
|
152
|
-
unparenthesized_method_call?(child)
|
152
|
+
unparenthesized_method_call?(child) ||
|
153
|
+
below_ternary_precedence?(child)
|
153
154
|
end
|
154
155
|
end
|
155
156
|
|
@@ -157,6 +158,15 @@ module RuboCop
|
|
157
158
|
method_name(child) =~ /^[a-z]/i && !child.parenthesized?
|
158
159
|
end
|
159
160
|
|
161
|
+
def below_ternary_precedence?(child)
|
162
|
+
# Handle English "or", e.g. 'foo or bar ? a : b'
|
163
|
+
(child.or_type? && child.semantic_operator?) ||
|
164
|
+
# Handle English "and", e.g. 'foo and bar ? a : b'
|
165
|
+
(child.and_type? && child.semantic_operator?) ||
|
166
|
+
# Handle English "not", e.g. 'not foo ? a : b'
|
167
|
+
(child.send_type? && child.prefix_not?)
|
168
|
+
end
|
169
|
+
|
160
170
|
def_node_matcher :method_name, <<-PATTERN
|
161
171
|
{($:defined? (send nil? _) ...)
|
162
172
|
(send {_ nil?} $_ _ ...)}
|
@@ -71,8 +71,8 @@ module RuboCop
|
|
71
71
|
|
72
72
|
def complex_content?(strings)
|
73
73
|
strings.any? do |s|
|
74
|
-
string = s.str_content
|
75
|
-
!string.
|
74
|
+
string = s.str_content.dup.force_encoding(::Encoding::UTF_8)
|
75
|
+
!string.valid_encoding? ||
|
76
76
|
string !~ word_regex || string =~ / /
|
77
77
|
end
|
78
78
|
end
|
@@ -7,7 +7,7 @@ module RuboCop
|
|
7
7
|
# by a predicate method, such as receiver.length == 0,
|
8
8
|
# receiver.length > 0, receiver.length != 0,
|
9
9
|
# receiver.length < 1 and receiver.size == 0 that can be
|
10
|
-
# replaced by receiver.empty? and !receiver.empty
|
10
|
+
# replaced by receiver.empty? and !receiver.empty?.
|
11
11
|
#
|
12
12
|
# @example
|
13
13
|
# # bad
|
data/lib/rubocop/node_pattern.rb
CHANGED
@@ -47,6 +47,11 @@ module RuboCop
|
|
47
47
|
# # a `sym` node, but can have more.
|
48
48
|
# '(array <$str $_>)' # captures are in the order of the pattern,
|
49
49
|
# # irrespective of the actual order of the children
|
50
|
+
# '(array int*)' # will match an array of 0 or more integers
|
51
|
+
# '(array int ?)' # will match 0 or 1 integer.
|
52
|
+
# # Note: Space needed to distinguish from int?
|
53
|
+
# '(array int+)' # will match an array of 1 or more integers
|
54
|
+
# '(array (int $_)+)' # as above and will capture the numbers in an array
|
50
55
|
# '(send $...)' # capture all the children as an array
|
51
56
|
# '(send $... int)' # capture all children but the last as an array
|
52
57
|
# '(send _x :+ _x)' # unification is performed on named wildcards
|
@@ -106,7 +111,9 @@ module RuboCop
|
|
106
111
|
class Compiler
|
107
112
|
SYMBOL = %r{:(?:[\w+@*/?!<>=~|%^-]+|\[\]=?)}.freeze
|
108
113
|
IDENTIFIER = /[a-zA-Z_][a-zA-Z0-9_-]*/.freeze
|
109
|
-
META = Regexp.union(
|
114
|
+
META = Regexp.union(
|
115
|
+
%w"( ) { } [ ] $< < > $... $ ! ^ ... + * ?"
|
116
|
+
).freeze
|
110
117
|
NUMBER = /-?\d+(?:\.\d+)?/.freeze
|
111
118
|
STRING = /".+?"/.freeze
|
112
119
|
METHOD_NAME = /\#?#{IDENTIFIER}[\!\?]?\(?/.freeze
|
@@ -160,6 +167,21 @@ module RuboCop
|
|
160
167
|
RUBY
|
161
168
|
ANY_ORDER_TEMPLATE.location = [__FILE__, line + 1]
|
162
169
|
|
170
|
+
line = __LINE__
|
171
|
+
REPEATED_TEMPLATE = ERB.new <<~RUBY.gsub("-%>\n", '%>')
|
172
|
+
<% if captured %>(<%= accumulate %> = Array.new) && <% end %>
|
173
|
+
<%= CUR_NODE %>.children[<%= range %>].all? do |<%= child %>|
|
174
|
+
<%= with_context(expr, child, use_temp_node: false) %><% if captured %>&&
|
175
|
+
<%= accumulate %>.push(<%= captured %>)<% end %>
|
176
|
+
end <% if captured %>&&
|
177
|
+
(<%= captured %> = if <%= accumulate %>.empty?
|
178
|
+
<%= captured %>.map{[]} # Transpose hack won't work for empty case
|
179
|
+
else
|
180
|
+
<%= accumulate %>.transpose
|
181
|
+
end) <% end -%>
|
182
|
+
RUBY
|
183
|
+
REPEATED_TEMPLATE.location = [__FILE__, line + 1]
|
184
|
+
|
163
185
|
def initialize(str, node_var = 'node0')
|
164
186
|
@string = str
|
165
187
|
@root = node_var
|
@@ -238,10 +260,48 @@ module RuboCop
|
|
238
260
|
when REST then compile_ellipsis
|
239
261
|
when '$<' then compile_any_order(next_capture)
|
240
262
|
when '<' then compile_any_order
|
241
|
-
else
|
263
|
+
else compile_repeated_expr(token)
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
def compile_repeated_expr(token)
|
268
|
+
before = @captures
|
269
|
+
expr = compile_expr(token)
|
270
|
+
min, max = parse_repetition_token
|
271
|
+
return [1, expr] if min.nil?
|
272
|
+
|
273
|
+
if @captures != before
|
274
|
+
captured = "captures[#{before}...#{@captures}]"
|
275
|
+
accumulate = next_temp_variable(:accumulate)
|
276
|
+
end
|
277
|
+
arity = min..max || Float::INFINITY
|
278
|
+
|
279
|
+
[arity, repeated_generator(expr, captured, accumulate)]
|
280
|
+
end
|
281
|
+
|
282
|
+
def repeated_generator(expr, captured, accumulate)
|
283
|
+
with_temp_variables do |child|
|
284
|
+
lambda do |range|
|
285
|
+
if range.begin == SEQ_HEAD_INDEX
|
286
|
+
fail_due_to 'repeated pattern at beginning of sequence'
|
287
|
+
end
|
288
|
+
REPEATED_TEMPLATE.result(binding)
|
289
|
+
end
|
242
290
|
end
|
243
291
|
end
|
244
292
|
|
293
|
+
def parse_repetition_token
|
294
|
+
case tokens.first
|
295
|
+
when '*' then min = 0
|
296
|
+
when '+' then min = 1
|
297
|
+
when '?' then min = 0
|
298
|
+
max = 1
|
299
|
+
else return
|
300
|
+
end
|
301
|
+
tokens.shift
|
302
|
+
[min, max]
|
303
|
+
end
|
304
|
+
|
245
305
|
# @private
|
246
306
|
# Builds Ruby code for a sequence
|
247
307
|
# (head *first_terms variadic_term *last_terms)
|
@@ -276,6 +336,10 @@ module RuboCop
|
|
276
336
|
last_terms_range { |r| @arities[r].inject(0, :+) } || 0
|
277
337
|
end
|
278
338
|
|
339
|
+
def variadic_term_min_arity
|
340
|
+
@variadic_index ? @arities[@variadic_index].begin : 0
|
341
|
+
end
|
342
|
+
|
279
343
|
def first_terms_range
|
280
344
|
yield 1..(@variadic_index || @terms.size) - 1 if seq_head?
|
281
345
|
end
|
@@ -289,8 +353,19 @@ module RuboCop
|
|
289
353
|
end
|
290
354
|
|
291
355
|
def compile_child_nb_guard
|
292
|
-
|
293
|
-
|
356
|
+
fixed = first_terms_arity + last_terms_arity
|
357
|
+
min = fixed + variadic_term_min_arity
|
358
|
+
op = if @variadic_index
|
359
|
+
max_variadic = @arities[@variadic_index].end
|
360
|
+
if max_variadic != Float::INFINITY
|
361
|
+
range = min..fixed + max_variadic
|
362
|
+
return "(#{range}).cover?(#{CUR_NODE}.children.size)"
|
363
|
+
end
|
364
|
+
'>='
|
365
|
+
else
|
366
|
+
'=='
|
367
|
+
end
|
368
|
+
"#{CUR_NODE}.children.size #{op} #{min}"
|
294
369
|
end
|
295
370
|
|
296
371
|
def term(index, range)
|
@@ -555,10 +630,14 @@ module RuboCop
|
|
555
630
|
end
|
556
631
|
|
557
632
|
def with_temp_variables(&block)
|
558
|
-
names = block.parameters.map { |_, name|
|
633
|
+
names = block.parameters.map { |_, name| next_temp_variable(name) }
|
559
634
|
yield(*names)
|
560
635
|
end
|
561
636
|
|
637
|
+
def next_temp_variable(name)
|
638
|
+
"#{name}#{next_temp_value}"
|
639
|
+
end
|
640
|
+
|
562
641
|
def next_temp_value
|
563
642
|
@temps += 1
|
564
643
|
end
|