rubocop 0.67.2 → 0.68.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 +1 -1
- data/config/default.yml +86 -233
- data/exe/rubocop +0 -12
- data/lib/rubocop.rb +13 -30
- data/lib/rubocop/ast/builder.rb +4 -0
- data/lib/rubocop/ast/node/alias_node.rb +24 -0
- data/lib/rubocop/ast/node/class_node.rb +31 -0
- data/lib/rubocop/ast/node/module_node.rb +24 -0
- data/lib/rubocop/ast/node/range_node.rb +7 -0
- data/lib/rubocop/ast/node/resbody_node.rb +12 -0
- data/lib/rubocop/ast/node/self_class_node.rb +24 -0
- data/lib/rubocop/cli.rb +40 -4
- data/lib/rubocop/config.rb +9 -7
- data/lib/rubocop/config_loader.rb +48 -7
- data/lib/rubocop/config_loader_resolver.rb +5 -4
- data/lib/rubocop/cop/commissioner.rb +24 -0
- data/lib/rubocop/cop/correctors/unused_arg_corrector.rb +18 -6
- data/lib/rubocop/cop/internal_affairs/node_destructuring.rb +12 -14
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +9 -20
- data/lib/rubocop/cop/layout/align_arguments.rb +93 -0
- data/lib/rubocop/cop/layout/align_parameters.rb +57 -33
- data/lib/rubocop/cop/layout/class_structure.rb +5 -5
- data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +6 -8
- data/lib/rubocop/cop/layout/empty_lines_around_class_body.rb +3 -6
- data/lib/rubocop/cop/layout/empty_lines_around_module_body.rb +1 -2
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +1 -0
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +292 -0
- data/lib/rubocop/cop/layout/{first_parameter_indentation.rb → indent_first_argument.rb} +11 -12
- data/lib/rubocop/cop/layout/{indent_array.rb → indent_first_array_element.rb} +2 -2
- data/lib/rubocop/cop/layout/{indent_hash.rb → indent_first_hash_element.rb} +2 -2
- data/lib/rubocop/cop/layout/indent_first_parameter.rb +96 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -16
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +2 -4
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +1 -16
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +1 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +6 -8
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +4 -8
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +157 -0
- data/lib/rubocop/cop/lint/inherit_exception.rb +3 -4
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +18 -1
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +3 -5
- data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +25 -5
- data/lib/rubocop/cop/lint/useless_assignment.rb +2 -6
- data/lib/rubocop/cop/lint/useless_setter_call.rb +1 -2
- data/lib/rubocop/cop/message_annotator.rb +1 -0
- data/lib/rubocop/cop/metrics/line_length.rb +139 -28
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +3 -4
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +190 -0
- data/lib/rubocop/cop/mixin/{array_hash_indentation.rb → multiline_element_indentation.rb} +3 -2
- data/lib/rubocop/cop/mixin/too_many_lines.rb +3 -7
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +33 -4
- data/lib/rubocop/cop/rails/active_record_override.rb +23 -8
- data/lib/rubocop/cop/rails/delegate.rb +5 -8
- data/lib/rubocop/cop/rails/environment_comparison.rb +5 -3
- data/lib/rubocop/cop/rails/find_each.rb +1 -1
- data/lib/rubocop/cop/rails/redundant_allow_nil.rb +3 -3
- data/lib/rubocop/cop/rails/reflection_class_name.rb +1 -1
- data/lib/rubocop/cop/rails/skips_model_validations.rb +6 -7
- data/lib/rubocop/cop/rails/time_zone.rb +3 -10
- data/lib/rubocop/cop/rails/validation.rb +3 -0
- data/lib/rubocop/cop/registry.rb +3 -3
- data/lib/rubocop/cop/style/alias.rb +13 -7
- data/lib/rubocop/cop/style/block_delimiters.rb +20 -0
- data/lib/rubocop/cop/style/class_and_module_children.rb +19 -21
- data/lib/rubocop/cop/style/class_methods.rb +16 -24
- data/lib/rubocop/cop/style/conditional_assignment.rb +20 -49
- data/lib/rubocop/cop/style/documentation.rb +3 -7
- data/lib/rubocop/cop/style/format_string.rb +18 -21
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
- data/lib/rubocop/cop/style/inverse_methods.rb +4 -0
- data/lib/rubocop/cop/style/lambda.rb +12 -8
- data/lib/rubocop/cop/style/mixin_grouping.rb +8 -10
- data/lib/rubocop/cop/style/module_function.rb +2 -3
- data/lib/rubocop/cop/style/next.rb +10 -14
- data/lib/rubocop/cop/style/one_line_conditional.rb +5 -3
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -4
- data/lib/rubocop/cop/style/random_with_offset.rb +44 -47
- data/lib/rubocop/cop/style/redundant_return.rb +6 -14
- data/lib/rubocop/cop/style/redundant_sort_by.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +3 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +20 -40
- data/lib/rubocop/cop/style/unless_else.rb +1 -2
- data/lib/rubocop/cop/style/yoda_condition.rb +8 -7
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/file_finder.rb +5 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +5 -0
- data/lib/rubocop/node_pattern.rb +304 -170
- data/lib/rubocop/options.rb +4 -1
- data/lib/rubocop/rspec/shared_contexts.rb +3 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/yaml_duplication_checker.rb +1 -1
- metadata +26 -50
- data/lib/rubocop/cop/performance/caller.rb +0 -69
- data/lib/rubocop/cop/performance/case_when_splat.rb +0 -177
- data/lib/rubocop/cop/performance/casecmp.rb +0 -108
- data/lib/rubocop/cop/performance/chain_array_allocation.rb +0 -78
- data/lib/rubocop/cop/performance/compare_with_block.rb +0 -122
- data/lib/rubocop/cop/performance/count.rb +0 -102
- data/lib/rubocop/cop/performance/detect.rb +0 -110
- data/lib/rubocop/cop/performance/double_start_end_with.rb +0 -94
- data/lib/rubocop/cop/performance/end_with.rb +0 -56
- data/lib/rubocop/cop/performance/fixed_size.rb +0 -97
- data/lib/rubocop/cop/performance/flat_map.rb +0 -78
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +0 -99
- data/lib/rubocop/cop/performance/open_struct.rb +0 -46
- data/lib/rubocop/cop/performance/range_include.rb +0 -50
- data/lib/rubocop/cop/performance/redundant_block_call.rb +0 -93
- data/lib/rubocop/cop/performance/redundant_match.rb +0 -56
- data/lib/rubocop/cop/performance/redundant_merge.rb +0 -183
- data/lib/rubocop/cop/performance/regexp_match.rb +0 -265
- data/lib/rubocop/cop/performance/reverse_each.rb +0 -42
- data/lib/rubocop/cop/performance/size.rb +0 -77
- data/lib/rubocop/cop/performance/start_with.rb +0 -59
- data/lib/rubocop/cop/performance/string_replacement.rb +0 -173
- data/lib/rubocop/cop/performance/times_map.rb +0 -71
- data/lib/rubocop/cop/performance/unfreeze_string.rb +0 -50
- data/lib/rubocop/cop/performance/uri_default_parser.rb +0 -47
@@ -49,16 +49,14 @@ module RuboCop
|
|
49
49
|
return_node.each_ancestor(:block, :def, :defs) do |node|
|
50
50
|
break if scoped_node?(node)
|
51
51
|
|
52
|
-
send_node, args_node, _body_node = *node
|
53
|
-
|
54
52
|
# if a proc is passed to `Module#define_method` or
|
55
53
|
# `Object#define_singleton_method`, `return` will not cause a
|
56
54
|
# non-local exit error
|
57
|
-
break if define_method?(send_node)
|
55
|
+
break if define_method?(node.send_node)
|
58
56
|
|
59
|
-
next
|
57
|
+
next unless node.arguments?
|
60
58
|
|
61
|
-
if chained_send?(send_node)
|
59
|
+
if chained_send?(node.send_node)
|
62
60
|
add_offense(return_node, location: :keyword)
|
63
61
|
break
|
64
62
|
end
|
@@ -6,7 +6,11 @@ module RuboCop
|
|
6
6
|
# This cop checks for underscore-prefixed variables that are actually
|
7
7
|
# used.
|
8
8
|
#
|
9
|
-
#
|
9
|
+
# Since block keyword arguments cannot be arbitrarily named at call
|
10
|
+
# sites, the `AllowKeywordBlockArguments` will allow use of underscore-
|
11
|
+
# prefixed block keyword arguments.
|
12
|
+
#
|
13
|
+
# @example AllowKeywordBlockArguments: false (default)
|
10
14
|
#
|
11
15
|
# # bad
|
12
16
|
#
|
@@ -14,7 +18,9 @@ module RuboCop
|
|
14
18
|
# do_something(_num)
|
15
19
|
# end
|
16
20
|
#
|
17
|
-
#
|
21
|
+
# query(:sales) do |_id:, revenue:, cost:|
|
22
|
+
# {_id: _id, profit: revenue - cost}
|
23
|
+
# end
|
18
24
|
#
|
19
25
|
# # good
|
20
26
|
#
|
@@ -22,13 +28,18 @@ module RuboCop
|
|
22
28
|
# do_something(num)
|
23
29
|
# end
|
24
30
|
#
|
25
|
-
#
|
31
|
+
# [1, 2, 3].each do |_num|
|
32
|
+
# do_something # not using `_num`
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# @example AllowKeywordBlockArguments: true
|
26
36
|
#
|
27
37
|
# # good
|
28
38
|
#
|
29
|
-
#
|
30
|
-
#
|
39
|
+
# query(:sales) do |_id:, revenue:, cost:|
|
40
|
+
# {_id: _id, profit: revenue - cost}
|
31
41
|
# end
|
42
|
+
#
|
32
43
|
class UnderscorePrefixedVariableName < Cop
|
33
44
|
MSG = 'Do not use prefix `_` for a variable that is used.'.freeze
|
34
45
|
|
@@ -45,6 +56,7 @@ module RuboCop
|
|
45
56
|
def check_variable(variable)
|
46
57
|
return unless variable.should_be_unused?
|
47
58
|
return if variable.references.none?(&:explicit?)
|
59
|
+
return if allowed_keyword_block_argument?(variable)
|
48
60
|
|
49
61
|
node = variable.declaration_node
|
50
62
|
|
@@ -56,6 +68,14 @@ module RuboCop
|
|
56
68
|
|
57
69
|
add_offense(nil, location: location)
|
58
70
|
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def allowed_keyword_block_argument?(variable)
|
75
|
+
variable.block_argument? &&
|
76
|
+
variable.keyword_argument? &&
|
77
|
+
cop_config['AllowKeywordBlockArguments']
|
78
|
+
end
|
59
79
|
end
|
60
80
|
end
|
61
81
|
end
|
@@ -111,10 +111,7 @@ module RuboCop
|
|
111
111
|
|
112
112
|
def collect_variable_like_names(scope)
|
113
113
|
names = scope.each_node.with_object(Set.new) do |node, set|
|
114
|
-
if variable_like_method_invocation?(node)
|
115
|
-
_receiver, method_name, = *node
|
116
|
-
set << method_name
|
117
|
-
end
|
114
|
+
set << node.method_name if variable_like_method_invocation?(node)
|
118
115
|
end
|
119
116
|
|
120
117
|
variable_names = scope.variables.each_value.map(&:name)
|
@@ -124,8 +121,7 @@ module RuboCop
|
|
124
121
|
def variable_like_method_invocation?(node)
|
125
122
|
return false unless node.send_type?
|
126
123
|
|
127
|
-
receiver
|
128
|
-
receiver.nil? && args.empty?
|
124
|
+
node.receiver.nil? && !node.arguments?
|
129
125
|
end
|
130
126
|
end
|
131
127
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'uri'
|
4
4
|
|
5
|
+
# rubocop:disable Metrics/ClassLength
|
5
6
|
module RuboCop
|
6
7
|
module Cop
|
7
8
|
module Metrics
|
@@ -9,22 +10,95 @@ module RuboCop
|
|
9
10
|
# The maximum length is configurable.
|
10
11
|
# The tab size is configured in the `IndentationWidth`
|
11
12
|
# of the `Layout/Tab` cop.
|
13
|
+
#
|
14
|
+
# This cop has some autocorrection capabilities.
|
15
|
+
# It can programmatically shorten certain long lines by
|
16
|
+
# inserting line breaks into expressions that can be safely
|
17
|
+
# split across lines. These include arrays, hashes, and
|
18
|
+
# method calls with argument lists.
|
19
|
+
#
|
20
|
+
# If autocorrection is enabled, the following Layout cops
|
21
|
+
# are recommended to further format the broken lines.
|
22
|
+
#
|
23
|
+
# - AlignArray
|
24
|
+
# - AlignHash
|
25
|
+
# - AlignParameters
|
26
|
+
# - ClosingParenthesisIndentation
|
27
|
+
# - IndentFirstArgument
|
28
|
+
# - IndentFirstArrayElement
|
29
|
+
# - IndentFirstHashElement
|
30
|
+
# - IndentFirstParameter
|
31
|
+
# - MultilineArrayLineBreaks
|
32
|
+
# - MultilineHashBraceLayout
|
33
|
+
# - MultilineHashKeyLineBreaks
|
34
|
+
# - MultilineMethodArgumentLineBreaks
|
35
|
+
#
|
36
|
+
# Together, these cops will pretty print hashes, arrays,
|
37
|
+
# method calls, etc. For example, let's say the max columns
|
38
|
+
# is 25:
|
39
|
+
#
|
40
|
+
# @example
|
41
|
+
#
|
42
|
+
# # bad
|
43
|
+
# {foo: "0000000000", bar: "0000000000", baz: "0000000000"}
|
44
|
+
#
|
45
|
+
# # good
|
46
|
+
# {foo: "0000000000",
|
47
|
+
# bar: "0000000000", baz: "0000000000"}
|
48
|
+
#
|
49
|
+
# # good (with recommended cops enabled)
|
50
|
+
# {
|
51
|
+
# foo: "0000000000",
|
52
|
+
# bar: "0000000000",
|
53
|
+
# baz: "0000000000",
|
54
|
+
# }
|
12
55
|
class LineLength < Cop
|
56
|
+
include CheckLineBreakable
|
13
57
|
include ConfigurableMax
|
14
58
|
include IgnoredPattern
|
15
59
|
include RangeHelp
|
16
60
|
|
17
61
|
MSG = 'Line is too long. [%<length>d/%<max>d]'.freeze
|
18
62
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
63
|
+
def on_potential_breakable_node(node)
|
64
|
+
check_for_breakable_node(node)
|
65
|
+
end
|
66
|
+
alias on_array on_potential_breakable_node
|
67
|
+
alias on_hash on_potential_breakable_node
|
68
|
+
alias on_send on_potential_breakable_node
|
69
|
+
|
70
|
+
def investigate_post_walk(processed_source)
|
71
|
+
processed_source.lines.each_with_index do |line, line_index|
|
72
|
+
check_line(line, line_index)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def autocorrect(range)
|
77
|
+
return if range.nil?
|
78
|
+
|
79
|
+
lambda do |corrector|
|
80
|
+
corrector.insert_before(range, "\n")
|
23
81
|
end
|
24
82
|
end
|
25
83
|
|
26
84
|
private
|
27
85
|
|
86
|
+
def check_for_breakable_node(node)
|
87
|
+
breakable_node = extract_breakable_node(node, max)
|
88
|
+
return if breakable_node.nil?
|
89
|
+
|
90
|
+
line_index = breakable_node.first_line - 1
|
91
|
+
breakable_nodes_by_line_index[line_index] = breakable_node
|
92
|
+
end
|
93
|
+
|
94
|
+
def breakable_nodes_by_line_index
|
95
|
+
@breakable_nodes_by_line_index ||= {}
|
96
|
+
end
|
97
|
+
|
98
|
+
def heredocs
|
99
|
+
@heredocs ||= extract_heredocs(processed_source.ast)
|
100
|
+
end
|
101
|
+
|
28
102
|
def tab_indentation_width
|
29
103
|
config.for_cop('Layout/Tab')['IndentationWidth']
|
30
104
|
end
|
@@ -39,49 +113,72 @@ module RuboCop
|
|
39
113
|
line.length + indentation_difference(line)
|
40
114
|
end
|
41
115
|
|
42
|
-
def
|
116
|
+
def highlight_start(line)
|
43
117
|
max - indentation_difference(line)
|
44
118
|
end
|
45
119
|
|
46
|
-
def check_line(line,
|
120
|
+
def check_line(line, line_index)
|
47
121
|
return if line_length(line) <= max
|
48
|
-
return if ignored_line?(line,
|
122
|
+
return if ignored_line?(line, line_index)
|
49
123
|
|
50
|
-
if ignore_cop_directives? && directive_on_source_line?(
|
51
|
-
return check_directive_line(line,
|
124
|
+
if ignore_cop_directives? && directive_on_source_line?(line_index)
|
125
|
+
return check_directive_line(line, line_index)
|
52
126
|
end
|
53
|
-
return check_uri_line(line,
|
127
|
+
return check_uri_line(line, line_index) if allow_uri?
|
54
128
|
|
55
129
|
register_offense(
|
56
130
|
source_range(
|
57
|
-
processed_source.buffer,
|
58
|
-
|
131
|
+
processed_source.buffer, line_index,
|
132
|
+
highlight_start(line)...line_length(line)
|
59
133
|
),
|
60
|
-
line
|
134
|
+
line,
|
135
|
+
line_index
|
61
136
|
)
|
62
137
|
end
|
63
138
|
|
64
|
-
def ignored_line?(line,
|
139
|
+
def ignored_line?(line, line_index)
|
65
140
|
matches_ignored_pattern?(line) ||
|
66
|
-
heredocs && line_in_permitted_heredoc?(
|
141
|
+
heredocs && line_in_permitted_heredoc?(line_index.succ)
|
67
142
|
end
|
68
143
|
|
69
|
-
def register_offense(loc, line)
|
144
|
+
def register_offense(loc, line, line_index)
|
70
145
|
message = format(MSG, length: line_length(line), max: max)
|
71
146
|
|
72
|
-
|
147
|
+
breakable_range = breakable_range(line, line_index)
|
148
|
+
add_offense(breakable_range, location: loc, message: message) do
|
73
149
|
self.max = line_length(line)
|
74
150
|
end
|
75
151
|
end
|
76
152
|
|
77
|
-
def
|
153
|
+
def breakable_range(line, line_index)
|
154
|
+
return if line_in_heredoc?(line_index + 1)
|
155
|
+
|
156
|
+
semicolon_range = breakable_semicolon_range(line, line_index)
|
157
|
+
return semicolon_range if semicolon_range
|
158
|
+
|
159
|
+
breakable_node = breakable_nodes_by_line_index[line_index]
|
160
|
+
return breakable_node.source_range if breakable_node
|
161
|
+
end
|
162
|
+
|
163
|
+
def breakable_semicolon_range(line, line_index)
|
164
|
+
semicolon_separated_parts = line.split(';')
|
165
|
+
return if semicolon_separated_parts.length <= 1
|
166
|
+
|
167
|
+
column = semicolon_separated_parts.first.length + 1
|
168
|
+
range = source_range(processed_source.buffer, line_index, column, 1)
|
169
|
+
return if processed_source.commented?(range)
|
170
|
+
|
171
|
+
range
|
172
|
+
end
|
173
|
+
|
174
|
+
def excess_range(uri_range, line, line_index)
|
78
175
|
excessive_position = if uri_range && uri_range.begin < max
|
79
176
|
uri_range.end
|
80
177
|
else
|
81
|
-
|
178
|
+
highlight_start(line)
|
82
179
|
end
|
83
180
|
|
84
|
-
source_range(processed_source.buffer,
|
181
|
+
source_range(processed_source.buffer, line_index + 1,
|
85
182
|
excessive_position...(line_length(line)))
|
86
183
|
end
|
87
184
|
|
@@ -107,13 +204,21 @@ module RuboCop
|
|
107
204
|
end
|
108
205
|
end
|
109
206
|
|
110
|
-
def line_in_permitted_heredoc?(
|
207
|
+
def line_in_permitted_heredoc?(line_number)
|
208
|
+
return false unless allowed_heredoc
|
209
|
+
|
111
210
|
heredocs.any? do |range, delimiter|
|
112
211
|
range.cover?(line_number) &&
|
113
212
|
(allowed_heredoc == true || allowed_heredoc.include?(delimiter))
|
114
213
|
end
|
115
214
|
end
|
116
215
|
|
216
|
+
def line_in_heredoc?(line_number)
|
217
|
+
heredocs.any? do |range, _delimiter|
|
218
|
+
range.cover?(line_number)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
117
222
|
def allow_uri?
|
118
223
|
cop_config['AllowURI']
|
119
224
|
end
|
@@ -161,22 +266,23 @@ module RuboCop
|
|
161
266
|
URI::DEFAULT_PARSER.make_regexp(cop_config['URISchemes'])
|
162
267
|
end
|
163
268
|
|
164
|
-
def check_directive_line(line,
|
269
|
+
def check_directive_line(line, line_index)
|
165
270
|
return if line_length_without_directive(line) <= max
|
166
271
|
|
167
272
|
range = max..(line_length_without_directive(line) - 1)
|
168
273
|
register_offense(
|
169
274
|
source_range(
|
170
275
|
processed_source.buffer,
|
171
|
-
|
276
|
+
line_index + 1,
|
172
277
|
range
|
173
278
|
),
|
174
|
-
line
|
279
|
+
line,
|
280
|
+
line_index
|
175
281
|
)
|
176
282
|
end
|
177
283
|
|
178
|
-
def directive_on_source_line?(
|
179
|
-
source_line_number =
|
284
|
+
def directive_on_source_line?(line_index)
|
285
|
+
source_line_number = line_index + processed_source.buffer.first_line
|
180
286
|
comment =
|
181
287
|
processed_source
|
182
288
|
.comments
|
@@ -192,13 +298,18 @@ module RuboCop
|
|
192
298
|
before_comment.rstrip.length
|
193
299
|
end
|
194
300
|
|
195
|
-
def check_uri_line(line,
|
301
|
+
def check_uri_line(line, line_index)
|
196
302
|
uri_range = find_excessive_uri_range(line)
|
197
303
|
return if uri_range && allowed_uri_position?(line, uri_range)
|
198
304
|
|
199
|
-
register_offense(
|
305
|
+
register_offense(
|
306
|
+
excess_range(uri_range, line, line_index),
|
307
|
+
line,
|
308
|
+
line_index
|
309
|
+
)
|
200
310
|
end
|
201
311
|
end
|
202
312
|
end
|
203
313
|
end
|
204
314
|
end
|
315
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -39,16 +39,15 @@ module RuboCop
|
|
39
39
|
def complexity_score_for(node)
|
40
40
|
case node.type
|
41
41
|
when :case
|
42
|
-
expression, *whens, _else = *node
|
43
42
|
# If cond is nil, that means each when has an expression that
|
44
43
|
# evaluates to true or false. It's just an alternative to
|
45
44
|
# if/elsif/elsif... so the when nodes count.
|
46
|
-
if
|
47
|
-
|
45
|
+
if node.condition.nil?
|
46
|
+
node.when_branches.length
|
48
47
|
else
|
49
48
|
# Otherwise, the case node gets 0.8 complexity points and each
|
50
49
|
# when gets 0.2.
|
51
|
-
(0.8 + 0.2 *
|
50
|
+
(0.8 + 0.2 * node.when_branches.length).round
|
52
51
|
end
|
53
52
|
when :if
|
54
53
|
node.else? && !node.elsif? ? 2 : 1
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# This mixin detects collections that are safe to "break"
|
6
|
+
# by inserting new lines. This is useful for breaking
|
7
|
+
# up long lines.
|
8
|
+
#
|
9
|
+
# Let's look at hashes as an example:
|
10
|
+
#
|
11
|
+
# We know hash keys are safe to break across lines. We can add
|
12
|
+
# linebreaks into hashes on lines longer than the specified maximum.
|
13
|
+
# Then in further passes cops can clean up the multi-line hash.
|
14
|
+
# For example, say the maximum line length is as indicated below:
|
15
|
+
#
|
16
|
+
# |
|
17
|
+
# v
|
18
|
+
# {foo: "0000000000", bar: "0000000000", baz: "0000000000"}
|
19
|
+
#
|
20
|
+
# In a LineLength autocorrection pass, a line is added before
|
21
|
+
# the first key that exceeds the column limit:
|
22
|
+
#
|
23
|
+
# {foo: "0000000000", bar: "0000000000",
|
24
|
+
# baz: "0000000000"}
|
25
|
+
#
|
26
|
+
# In a MultilineHashKeyLineBreaks pass, lines are inserted
|
27
|
+
# before all keys:
|
28
|
+
#
|
29
|
+
# {foo: "0000000000",
|
30
|
+
# bar: "0000000000",
|
31
|
+
# baz: "0000000000"}
|
32
|
+
#
|
33
|
+
# Then in future passes FirstHashElementLineBreak,
|
34
|
+
# MultilineHashBraceLayout, and TrailingCommaInHashLiteral will
|
35
|
+
# manipulate as well until we get:
|
36
|
+
#
|
37
|
+
# {
|
38
|
+
# foo: "0000000000",
|
39
|
+
# bar: "0000000000",
|
40
|
+
# baz: "0000000000",
|
41
|
+
# }
|
42
|
+
#
|
43
|
+
# (Note: Passes may not happen exactly in this sequence.)
|
44
|
+
module CheckLineBreakable
|
45
|
+
def extract_breakable_node(node, max)
|
46
|
+
if node.send_type?
|
47
|
+
args = process_args(node.arguments)
|
48
|
+
return extract_breakable_node_from_elements(node, args, max)
|
49
|
+
elsif node.array_type? || node.hash_type?
|
50
|
+
return extract_breakable_node_from_elements(node, node.children, max)
|
51
|
+
end
|
52
|
+
nil
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def extract_breakable_node_from_elements(node, elements, max)
|
58
|
+
return unless breakable_collection?(node, elements)
|
59
|
+
return if safe_to_ignore?(node)
|
60
|
+
|
61
|
+
line = processed_source.lines[node.first_line - 1]
|
62
|
+
return if processed_source.commented?(node.loc.begin)
|
63
|
+
return if line.length <= max
|
64
|
+
|
65
|
+
extract_first_element_over_column_limit(node, elements, max)
|
66
|
+
end
|
67
|
+
|
68
|
+
def extract_first_element_over_column_limit(node, elements, max)
|
69
|
+
line = node.first_line
|
70
|
+
i = 0
|
71
|
+
i += 1 while within_column_limit?(elements[i], max, line)
|
72
|
+
return elements.first if i.zero?
|
73
|
+
|
74
|
+
elements[i - 1]
|
75
|
+
end
|
76
|
+
|
77
|
+
def within_column_limit?(element, max, line)
|
78
|
+
element && element.loc.column < max && element.loc.line == line
|
79
|
+
end
|
80
|
+
|
81
|
+
def safe_to_ignore?(node)
|
82
|
+
return true unless max
|
83
|
+
return true if already_on_multiple_lines?(node)
|
84
|
+
|
85
|
+
# If there's a containing breakable collection on the same
|
86
|
+
# line, we let that one get broken first. In a separate pass,
|
87
|
+
# this one might get broken as well, but to avoid conflicting
|
88
|
+
# or redundant edits, we only mark one offense at a time.
|
89
|
+
return true if contained_by_breakable_collection_on_same_line?(node)
|
90
|
+
|
91
|
+
if contained_by_multiline_collection_that_could_be_broken_up?(node)
|
92
|
+
return true
|
93
|
+
end
|
94
|
+
|
95
|
+
false
|
96
|
+
end
|
97
|
+
|
98
|
+
def breakable_collection?(node, elements)
|
99
|
+
# For simplicity we only want to insert breaks in normal
|
100
|
+
# hashes wrapped in a set of curly braces like {foo: 1}.
|
101
|
+
# That is, not a kwargs hash. For method calls, this ensures
|
102
|
+
# the method call is made with parens.
|
103
|
+
starts_with_bracket = node.loc.begin
|
104
|
+
|
105
|
+
# If the call has a second argument, we can insert a line
|
106
|
+
# break before the second argument and the rest of the
|
107
|
+
# argument will get auto-formatted onto separate lines
|
108
|
+
# by other cops.
|
109
|
+
has_second_element = elements.length >= 2
|
110
|
+
|
111
|
+
starts_with_bracket && has_second_element
|
112
|
+
end
|
113
|
+
|
114
|
+
def contained_by_breakable_collection_on_same_line?(node)
|
115
|
+
node.each_ancestor.find do |ancestor|
|
116
|
+
# Ignore ancestors on different lines.
|
117
|
+
break if ancestor.first_line != node.first_line
|
118
|
+
|
119
|
+
if ancestor.hash_type? || ancestor.array_type?
|
120
|
+
elements = ancestor.children
|
121
|
+
elsif ancestor.send_type?
|
122
|
+
elements = process_args(ancestor.arguments)
|
123
|
+
else
|
124
|
+
next
|
125
|
+
end
|
126
|
+
|
127
|
+
return true if breakable_collection?(ancestor, elements)
|
128
|
+
end
|
129
|
+
|
130
|
+
false
|
131
|
+
end
|
132
|
+
|
133
|
+
def contained_by_multiline_collection_that_could_be_broken_up?(node)
|
134
|
+
node.each_ancestor.find do |ancestor|
|
135
|
+
if ancestor.hash_type? || ancestor.array_type?
|
136
|
+
if breakable_collection?(ancestor, ancestor.children)
|
137
|
+
return children_could_be_broken_up?(ancestor.children)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
next unless ancestor.send_type?
|
142
|
+
|
143
|
+
args = process_args(ancestor.arguments)
|
144
|
+
if breakable_collection?(ancestor, args)
|
145
|
+
return children_could_be_broken_up?(args)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
false
|
150
|
+
end
|
151
|
+
|
152
|
+
def children_could_be_broken_up?(children)
|
153
|
+
return false if all_on_same_line?(children)
|
154
|
+
|
155
|
+
last_seen_line = -1
|
156
|
+
children.each do |child|
|
157
|
+
return true if last_seen_line >= child.first_line
|
158
|
+
|
159
|
+
last_seen_line = child.last_line
|
160
|
+
end
|
161
|
+
false
|
162
|
+
end
|
163
|
+
|
164
|
+
def all_on_same_line?(nodes)
|
165
|
+
return true if nodes.empty?
|
166
|
+
|
167
|
+
nodes.first.first_line == nodes.last.last_line
|
168
|
+
end
|
169
|
+
|
170
|
+
def process_args(args)
|
171
|
+
# If there is a trailing hash arg without explicit braces, like this:
|
172
|
+
#
|
173
|
+
# method(1, 'key1' => value1, 'key2' => value2)
|
174
|
+
#
|
175
|
+
# ...then each key/value pair is treated as a method 'argument'
|
176
|
+
# when determining where line breaks should appear.
|
177
|
+
if (last_arg = args.last)
|
178
|
+
if last_arg.hash_type? && !last_arg.braces?
|
179
|
+
args = args.concat(args.pop.children)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
args
|
183
|
+
end
|
184
|
+
|
185
|
+
def already_on_multiple_lines?(node)
|
186
|
+
node.first_line != node.last_line
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|