rubocop 0.67.2 → 0.68.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|