rubocop 0.31.0 → 0.35.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/CHANGELOG.md +315 -0
- data/README.md +199 -38
- data/config/default.yml +91 -12
- data/config/disabled.yml +45 -4
- data/config/enabled.yml +107 -9
- data/lib/rubocop/ast_node.rb +48 -0
- data/lib/rubocop/cli.rb +11 -1
- data/lib/rubocop/comment_config.rb +4 -1
- data/lib/rubocop/config.rb +26 -17
- data/lib/rubocop/config_loader.rb +61 -14
- data/lib/rubocop/cop/commissioner.rb +7 -12
- data/lib/rubocop/cop/cop.rb +43 -20
- data/lib/rubocop/cop/lint/block_alignment.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +69 -0
- data/lib/rubocop/cop/lint/debugger.rb +9 -48
- data/lib/rubocop/cop/lint/def_end_alignment.rb +8 -4
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +42 -23
- data/lib/rubocop/cop/lint/duplicate_methods.rb +2 -2
- data/lib/rubocop/cop/lint/duplicated_key.rb +37 -0
- data/lib/rubocop/cop/lint/end_alignment.rb +33 -13
- data/lib/rubocop/cop/lint/eval.rb +6 -2
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +175 -0
- data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -5
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +10 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +31 -0
- data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +19 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +1 -1
- data/lib/rubocop/cop/lint/space_before_first_arg.rb +1 -1
- data/lib/rubocop/cop/lint/unneeded_disable.rb +72 -0
- data/lib/rubocop/cop/lint/unused_block_argument.rb +6 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +8 -0
- data/lib/rubocop/cop/metrics/abc_size.rb +17 -6
- data/lib/rubocop/cop/metrics/class_length.rb +1 -1
- data/lib/rubocop/cop/metrics/method_length.rb +1 -3
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/parameter_lists.rb +1 -1
- data/lib/rubocop/cop/mixin/access_modifier_node.rb +1 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -2
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +28 -4
- data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +26 -3
- data/lib/rubocop/cop/mixin/check_assignment.rb +2 -3
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +59 -12
- data/lib/rubocop/cop/mixin/configurable_max.rb +1 -1
- data/lib/rubocop/cop/mixin/configurable_naming.rb +14 -3
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -3
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +10 -1
- data/lib/rubocop/cop/mixin/first_element_line_break.rb +41 -0
- data/lib/rubocop/cop/mixin/if_node.rb +10 -0
- data/lib/rubocop/cop/mixin/method_preference.rb +28 -0
- data/lib/rubocop/cop/mixin/negative_conditional.rb +1 -1
- data/lib/rubocop/cop/mixin/on_method_def.rb +4 -5
- data/lib/rubocop/cop/mixin/safe_assignment.rb +3 -14
- data/lib/rubocop/cop/mixin/space_after_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/space_before_punctuation.rb +8 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +4 -7
- data/lib/rubocop/cop/mixin/string_help.rb +1 -1
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
- data/lib/rubocop/cop/mixin/surrounding_space.rb +5 -4
- data/lib/rubocop/cop/offense.rb +16 -3
- data/lib/rubocop/cop/performance/case_when_splat.rb +160 -0
- data/lib/rubocop/cop/performance/count.rb +35 -30
- data/lib/rubocop/cop/performance/detect.rb +16 -3
- data/lib/rubocop/cop/performance/fixed_size.rb +50 -0
- data/lib/rubocop/cop/performance/flat_map.rb +3 -3
- data/lib/rubocop/cop/performance/sample.rb +103 -59
- data/lib/rubocop/cop/performance/size.rb +2 -1
- data/lib/rubocop/cop/performance/string_replacement.rb +187 -0
- data/lib/rubocop/cop/rails/action_filter.rb +31 -5
- data/lib/rubocop/cop/rails/date.rb +15 -14
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +97 -0
- data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
- data/lib/rubocop/cop/rails/time_zone.rb +46 -18
- data/lib/rubocop/cop/style/alias.rb +1 -0
- data/lib/rubocop/cop/style/align_hash.rb +8 -15
- data/lib/rubocop/cop/style/align_parameters.rb +19 -7
- data/lib/rubocop/cop/style/and_or.rb +42 -13
- data/lib/rubocop/cop/style/auto_resource_cleanup.rb +2 -1
- data/lib/rubocop/cop/style/block_comments.rb +4 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +69 -24
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +40 -12
- data/lib/rubocop/cop/style/case_indentation.rb +18 -4
- data/lib/rubocop/cop/style/collection_methods.rb +2 -20
- data/lib/rubocop/cop/style/command_literal.rb +2 -10
- data/lib/rubocop/cop/style/comment_annotation.rb +29 -8
- data/lib/rubocop/cop/style/copyright.rb +5 -3
- data/lib/rubocop/cop/style/documentation.rb +21 -12
- data/lib/rubocop/cop/style/dot_position.rb +6 -0
- data/lib/rubocop/cop/style/double_negation.rb +4 -15
- data/lib/rubocop/cop/style/each_with_object.rb +17 -4
- data/lib/rubocop/cop/style/else_alignment.rb +2 -1
- data/lib/rubocop/cop/style/empty_else.rb +25 -0
- data/lib/rubocop/cop/style/empty_line_between_defs.rb +39 -14
- data/lib/rubocop/cop/style/encoding.rb +10 -4
- data/lib/rubocop/cop/style/extra_spacing.rb +126 -5
- data/lib/rubocop/cop/style/first_array_element_line_break.rb +41 -0
- data/lib/rubocop/cop/style/first_hash_element_line_break.rb +35 -0
- data/lib/rubocop/cop/style/first_method_argument_line_break.rb +37 -0
- data/lib/rubocop/cop/style/first_method_parameter_line_break.rb +42 -0
- data/lib/rubocop/cop/style/first_parameter_indentation.rb +5 -3
- data/lib/rubocop/cop/style/for.rb +2 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +5 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -5
- data/lib/rubocop/cop/style/indent_hash.rb +67 -37
- data/lib/rubocop/cop/style/indentation_width.rb +36 -10
- data/lib/rubocop/cop/style/initial_indentation.rb +37 -0
- data/lib/rubocop/cop/style/leading_comment_space.rb +3 -2
- data/lib/rubocop/cop/style/method_call_parentheses.rb +28 -1
- data/lib/rubocop/cop/style/method_def_parentheses.rb +10 -7
- data/lib/rubocop/cop/style/multiline_operation_indentation.rb +21 -24
- data/lib/rubocop/cop/style/mutable_constant.rb +35 -0
- data/lib/rubocop/cop/style/nested_modifier.rb +97 -0
- data/lib/rubocop/cop/style/next.rb +50 -15
- data/lib/rubocop/cop/style/non_nil_check.rb +12 -8
- data/lib/rubocop/cop/style/one_line_conditional.rb +8 -4
- data/lib/rubocop/cop/style/option_hash.rb +64 -0
- data/lib/rubocop/cop/style/optional_arguments.rb +49 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +218 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +3 -66
- data/lib/rubocop/cop/style/predicate_name.rb +7 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +2 -13
- data/lib/rubocop/cop/style/redundant_freeze.rb +37 -0
- data/lib/rubocop/cop/style/redundant_return.rb +32 -3
- data/lib/rubocop/cop/style/regexp_literal.rb +2 -10
- data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +81 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +30 -22
- data/lib/rubocop/cop/style/send.rb +18 -0
- data/lib/rubocop/cop/style/signal_exception.rb +24 -11
- data/lib/rubocop/cop/style/single_line_methods.rb +8 -9
- data/lib/rubocop/cop/style/single_space_before_first_arg.rb +1 -1
- data/lib/rubocop/cop/style/space_around_operators.rb +2 -0
- data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +61 -0
- data/lib/rubocop/cop/style/special_global_vars.rb +4 -2
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +108 -0
- data/lib/rubocop/cop/style/string_methods.rb +32 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +11 -10
- data/lib/rubocop/cop/style/symbol_literal.rb +1 -1
- data/lib/rubocop/cop/style/symbol_proc.rb +62 -13
- data/lib/rubocop/cop/style/trailing_blank_lines.rb +9 -1
- data/lib/rubocop/cop/style/trailing_comma.rb +17 -7
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -2
- data/lib/rubocop/cop/style/trivial_accessors.rb +10 -1
- data/lib/rubocop/cop/style/unneeded_percent_q.rb +31 -20
- data/lib/rubocop/cop/style/variable_name.rb +5 -0
- data/lib/rubocop/cop/style/while_until_do.rb +1 -1
- data/lib/rubocop/cop/style/word_array.rb +15 -2
- data/lib/rubocop/cop/team.rb +25 -5
- data/lib/rubocop/cop/util.rb +7 -2
- data/lib/rubocop/cop/variable_force/locatable.rb +6 -6
- data/lib/rubocop/cop/variable_force.rb +10 -10
- data/lib/rubocop/formatter/base_formatter.rb +1 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +70 -8
- data/lib/rubocop/formatter/formatter_set.rb +27 -1
- data/lib/rubocop/formatter/progress_formatter.rb +10 -2
- data/lib/rubocop/formatter/simple_text_formatter.rb +1 -1
- data/lib/rubocop/node_pattern.rb +390 -0
- data/lib/rubocop/options.rb +148 -81
- data/lib/rubocop/processed_source.rb +7 -2
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/remote_config.rb +60 -0
- data/lib/rubocop/result_cache.rb +123 -0
- data/lib/rubocop/runner.rb +85 -22
- data/lib/rubocop/target_finder.rb +4 -4
- data/lib/rubocop/token.rb +2 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop/warning.rb +11 -0
- data/lib/rubocop.rb +32 -3
- data/relnotes/v0.32.0.md +139 -0
- data/relnotes/v0.32.1.md +122 -0
- data/relnotes/v0.33.0.md +157 -0
- data/relnotes/v0.34.0.md +182 -0
- data/relnotes/v0.34.1.md +129 -0
- data/relnotes/v0.34.2.md +139 -0
- data/relnotes/v0.35.0.md +210 -0
- data/rubocop.gemspec +4 -4
- metadata +50 -12
- data/lib/rubocop/cop/performance/parallel_assignment.rb +0 -79
|
@@ -25,32 +25,55 @@ module RuboCop
|
|
|
25
25
|
def on_block(node)
|
|
26
26
|
return if ignored_node?(node)
|
|
27
27
|
|
|
28
|
-
if proper_block_style?(node)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
add_offense(node, :begin) { opposite_style_detected }
|
|
32
|
-
end
|
|
28
|
+
return if proper_block_style?(node)
|
|
29
|
+
|
|
30
|
+
add_offense(node, :begin)
|
|
33
31
|
end
|
|
34
32
|
|
|
35
33
|
private
|
|
36
34
|
|
|
37
|
-
def
|
|
35
|
+
def line_count_based_message(node)
|
|
36
|
+
block_length = Util.block_length(node)
|
|
37
|
+
|
|
38
|
+
if block_length > 0
|
|
39
|
+
'Avoid using `{...}` for multi-line blocks.'
|
|
40
|
+
else
|
|
41
|
+
'Prefer `{...}` over `do...end` for single-line blocks.'
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def semantic_message(node)
|
|
38
46
|
block_begin = node.loc.begin.source
|
|
47
|
+
|
|
48
|
+
if block_begin == '{'
|
|
49
|
+
'Prefer `do...end` over `{...}` for procedural blocks.'
|
|
50
|
+
else
|
|
51
|
+
'Prefer `{...}` over `do...end` for functional blocks.'
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def braces_for_chaining_message(node)
|
|
39
56
|
block_length = Util.block_length(node)
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
'Avoid using `{...}` for multi-line blocks.'
|
|
58
|
+
if block_length > 0
|
|
59
|
+
if return_value_chaining?(node)
|
|
60
|
+
'Prefer `{...}` over `do...end` for multi-line chained blocks.'
|
|
45
61
|
else
|
|
46
|
-
'Prefer `
|
|
62
|
+
'Prefer `do...end` for multi-line blocks without chaining.'
|
|
47
63
|
end
|
|
64
|
+
else
|
|
65
|
+
'Prefer `{...}` over `do...end` for single-line blocks.'
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def message(node)
|
|
70
|
+
case style
|
|
71
|
+
when :line_count_based
|
|
72
|
+
line_count_based_message(node)
|
|
48
73
|
when :semantic
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
'Prefer `{...}` over `do...end` for functional blocks.'
|
|
53
|
-
end
|
|
74
|
+
semantic_message(node)
|
|
75
|
+
when :braces_for_chaining
|
|
76
|
+
braces_for_chaining_message(node)
|
|
54
77
|
end
|
|
55
78
|
end
|
|
56
79
|
|
|
@@ -59,11 +82,8 @@ module RuboCop
|
|
|
59
82
|
b = node.loc.begin
|
|
60
83
|
e = node.loc.end
|
|
61
84
|
if b.is?('{')
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
if b.source_buffer.source[b.begin_pos - 1, 1] =~ /\S/
|
|
65
|
-
corrector.insert_before(b, ' ')
|
|
66
|
-
end
|
|
85
|
+
corrector.insert_before(b, ' ') unless whitespace_before?(b)
|
|
86
|
+
corrector.insert_before(e, ' ') unless whitespace_before?(e)
|
|
67
87
|
corrector.replace(b, 'do')
|
|
68
88
|
corrector.replace(e, 'end')
|
|
69
89
|
else
|
|
@@ -73,6 +93,10 @@ module RuboCop
|
|
|
73
93
|
end
|
|
74
94
|
end
|
|
75
95
|
|
|
96
|
+
def whitespace_before?(node)
|
|
97
|
+
node.source_buffer.source[node.begin_pos - 1, 1] =~ /\s/
|
|
98
|
+
end
|
|
99
|
+
|
|
76
100
|
def get_block(node)
|
|
77
101
|
case node.type
|
|
78
102
|
when :block
|
|
@@ -89,6 +113,8 @@ module RuboCop
|
|
|
89
113
|
line_count_based_block_style?(node)
|
|
90
114
|
when :semantic
|
|
91
115
|
semantic_block_style?(node)
|
|
116
|
+
when :braces_for_chaining
|
|
117
|
+
braces_for_chaining_style?(node)
|
|
92
118
|
end
|
|
93
119
|
end
|
|
94
120
|
|
|
@@ -116,6 +142,21 @@ module RuboCop
|
|
|
116
142
|
end
|
|
117
143
|
end
|
|
118
144
|
|
|
145
|
+
def braces_for_chaining_style?(node)
|
|
146
|
+
block_length = Util.block_length(node)
|
|
147
|
+
block_begin = node.loc.begin.source
|
|
148
|
+
|
|
149
|
+
if block_length > 0
|
|
150
|
+
block_begin == (return_value_chaining?(node) ? '{' : 'do')
|
|
151
|
+
else
|
|
152
|
+
block_begin == '{'
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def return_value_chaining?(node)
|
|
157
|
+
node.parent && node.parent.send_type? && node.parent.loc.dot
|
|
158
|
+
end
|
|
159
|
+
|
|
119
160
|
def extract_method_name_from_block(block)
|
|
120
161
|
node, _args, _body = *block
|
|
121
162
|
_receiver, method_name, *_args = *node
|
|
@@ -147,15 +188,15 @@ module RuboCop
|
|
|
147
188
|
if node.parent.begin_type?
|
|
148
189
|
return_value_used?(node.parent)
|
|
149
190
|
else
|
|
150
|
-
|
|
151
|
-
node.parent.send_type?
|
|
191
|
+
node.parent.assignment? || node.parent.send_type?
|
|
152
192
|
end
|
|
153
193
|
end
|
|
154
194
|
|
|
155
195
|
def return_value_of_scope?(node)
|
|
156
196
|
return unless node.parent
|
|
157
197
|
|
|
158
|
-
conditional?(node.parent) || node.parent
|
|
198
|
+
conditional?(node.parent) || array_or_range?(node.parent) ||
|
|
199
|
+
node.parent.children.last == node
|
|
159
200
|
end
|
|
160
201
|
|
|
161
202
|
def procedural_methods
|
|
@@ -173,6 +214,10 @@ module RuboCop
|
|
|
173
214
|
def conditional?(node)
|
|
174
215
|
node.if_type? || node.or_type? || node.and_type?
|
|
175
216
|
end
|
|
217
|
+
|
|
218
|
+
def array_or_range?(node)
|
|
219
|
+
node.array_type? || node.irange_type? || node.erange_type?
|
|
220
|
+
end
|
|
176
221
|
end
|
|
177
222
|
end
|
|
178
223
|
end
|
|
@@ -7,6 +7,7 @@ module RuboCop
|
|
|
7
7
|
# if the last parameter is a hash.
|
|
8
8
|
class BracesAroundHashParameters < Cop
|
|
9
9
|
include ConfigurableEnforcedStyle
|
|
10
|
+
include AutocorrectUnlessChangingAST
|
|
10
11
|
|
|
11
12
|
MSG = '%s curly braces around a hash parameter.'
|
|
12
13
|
|
|
@@ -14,7 +15,7 @@ module RuboCop
|
|
|
14
15
|
_receiver, method_name, *args = *node
|
|
15
16
|
|
|
16
17
|
# Discard attr writer methods.
|
|
17
|
-
return if
|
|
18
|
+
return if node.asgn_method_call?
|
|
18
19
|
# Discard operator methods.
|
|
19
20
|
return if operator?(method_name)
|
|
20
21
|
|
|
@@ -28,9 +29,10 @@ module RuboCop
|
|
|
28
29
|
|
|
29
30
|
def check(arg, args)
|
|
30
31
|
if style == :braces && !braces?(arg)
|
|
31
|
-
add_offense(arg,
|
|
32
|
+
add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
|
|
32
33
|
elsif style == :no_braces && braces?(arg)
|
|
33
|
-
add_offense(arg,
|
|
34
|
+
add_offense(arg.parent, arg.loc.expression,
|
|
35
|
+
format(MSG, 'Redundant'))
|
|
34
36
|
elsif style == :context_dependent
|
|
35
37
|
check_context_dependent(arg, args)
|
|
36
38
|
end
|
|
@@ -40,27 +42,53 @@ module RuboCop
|
|
|
40
42
|
braces_around_2nd_from_end = args.length > 1 && args[-2].type == :hash
|
|
41
43
|
if braces?(arg)
|
|
42
44
|
unless braces_around_2nd_from_end
|
|
43
|
-
add_offense(arg,
|
|
45
|
+
add_offense(arg.parent, arg.loc.expression,
|
|
46
|
+
format(MSG, 'Redundant'))
|
|
44
47
|
end
|
|
45
48
|
elsif braces_around_2nd_from_end
|
|
46
|
-
add_offense(arg,
|
|
49
|
+
add_offense(arg.parent, arg.loc.expression, format(MSG, 'Missing'))
|
|
47
50
|
end
|
|
48
51
|
end
|
|
49
52
|
|
|
50
|
-
|
|
53
|
+
# We let AutocorrectUnlessChangingAST#autocorrect work with the send
|
|
54
|
+
# node, becuase that context is needed. When parsing the code to see if
|
|
55
|
+
# the AST has changed, a braceless hash would not be parsed as a hash
|
|
56
|
+
# otherwise.
|
|
57
|
+
def correction(send_node)
|
|
58
|
+
_receiver, _method_name, *args = *send_node
|
|
59
|
+
node = args.last
|
|
51
60
|
lambda do |corrector|
|
|
52
61
|
if braces?(node)
|
|
53
|
-
|
|
54
|
-
corrector.remove(right_range)
|
|
55
|
-
left_range = range_with_surrounding_space(node.loc.end, :left)
|
|
56
|
-
corrector.remove(left_range)
|
|
62
|
+
remove_braces(corrector, node)
|
|
57
63
|
else
|
|
58
|
-
corrector
|
|
59
|
-
corrector.insert_after(node.loc.expression, '}')
|
|
64
|
+
add_braces(corrector, node)
|
|
60
65
|
end
|
|
61
66
|
end
|
|
62
67
|
end
|
|
63
68
|
|
|
69
|
+
def remove_braces(corrector, node)
|
|
70
|
+
comments = processed_source.comments
|
|
71
|
+
right_brace_and_space = range_with_surrounding_space(node.loc.end,
|
|
72
|
+
:left)
|
|
73
|
+
if comments.any? { |c| c.loc.line == right_brace_and_space.line }
|
|
74
|
+
# Removing a line break between a comment and the closing
|
|
75
|
+
# parenthesis would cause a syntax error, so we only remove the
|
|
76
|
+
# braces in that case.
|
|
77
|
+
corrector.remove(node.loc.begin)
|
|
78
|
+
corrector.remove(node.loc.end)
|
|
79
|
+
else
|
|
80
|
+
left_brace_and_space = range_with_surrounding_space(node.loc.begin,
|
|
81
|
+
:right)
|
|
82
|
+
corrector.remove(left_brace_and_space)
|
|
83
|
+
corrector.remove(right_brace_and_space)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def add_braces(corrector, node)
|
|
88
|
+
corrector.insert_before(node.loc.expression, '{')
|
|
89
|
+
corrector.insert_after(node.loc.expression, '}')
|
|
90
|
+
end
|
|
91
|
+
|
|
64
92
|
def non_empty_hash?(arg)
|
|
65
93
|
arg && arg.type == :hash && arg.children.any?
|
|
66
94
|
end
|
|
@@ -8,6 +8,7 @@ module RuboCop
|
|
|
8
8
|
#
|
|
9
9
|
# It will register a separate offense for each misaligned *when*.
|
|
10
10
|
class CaseIndentation < Cop
|
|
11
|
+
include AutocorrectAlignment
|
|
11
12
|
include ConfigurableEnforcedStyle
|
|
12
13
|
|
|
13
14
|
def on_case(case_node)
|
|
@@ -50,10 +51,6 @@ module RuboCop
|
|
|
50
51
|
end
|
|
51
52
|
end
|
|
52
53
|
|
|
53
|
-
def configured_indentation_width
|
|
54
|
-
config.for_cop('IndentationWidth')['Width']
|
|
55
|
-
end
|
|
56
|
-
|
|
57
54
|
def parameter_name
|
|
58
55
|
'IndentWhenRelativeTo'
|
|
59
56
|
end
|
|
@@ -64,6 +61,23 @@ module RuboCop
|
|
|
64
61
|
when :end then case_node.location.end.column
|
|
65
62
|
end
|
|
66
63
|
end
|
|
64
|
+
|
|
65
|
+
def autocorrect(node)
|
|
66
|
+
when_column = node.location.keyword.column
|
|
67
|
+
source_buffer = node.loc.expression.source_buffer
|
|
68
|
+
begin_pos = node.loc.keyword.begin_pos
|
|
69
|
+
whitespace = Parser::Source::Range.new(source_buffer,
|
|
70
|
+
begin_pos - when_column,
|
|
71
|
+
begin_pos)
|
|
72
|
+
return false unless whitespace.source.strip.empty?
|
|
73
|
+
|
|
74
|
+
case_node = node.each_ancestor(:case).first
|
|
75
|
+
base_type = cop_config[parameter_name] == 'end' ? :end : :case
|
|
76
|
+
column = base_column(case_node, base_type)
|
|
77
|
+
column += configured_indentation_width if cop_config['IndentOneStep']
|
|
78
|
+
|
|
79
|
+
->(corrector) { corrector.replace(whitespace, ' ' * column) }
|
|
80
|
+
end
|
|
67
81
|
end
|
|
68
82
|
end
|
|
69
83
|
end
|
|
@@ -10,6 +10,8 @@ module RuboCop
|
|
|
10
10
|
# Enumerable or not (static analysis limitation), so this cop
|
|
11
11
|
# can yield some false positives.
|
|
12
12
|
class CollectionMethods < Cop
|
|
13
|
+
include MethodPreference
|
|
14
|
+
|
|
13
15
|
MSG = 'Prefer `%s` over `%s`.'
|
|
14
16
|
|
|
15
17
|
def on_block(node)
|
|
@@ -44,26 +46,6 @@ module RuboCop
|
|
|
44
46
|
method_name)
|
|
45
47
|
)
|
|
46
48
|
end
|
|
47
|
-
|
|
48
|
-
def preferred_method(method)
|
|
49
|
-
preferred_methods[method.to_sym]
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def preferred_methods
|
|
53
|
-
@preferred_methods ||=
|
|
54
|
-
begin
|
|
55
|
-
# Make sure default configuration 'foo' => 'bar' is removed from
|
|
56
|
-
# the total configuration if there is a 'bar' => 'foo' override.
|
|
57
|
-
default = default_cop_config['PreferredMethods']
|
|
58
|
-
merged = cop_config['PreferredMethods']
|
|
59
|
-
overrides = merged.values - default.values
|
|
60
|
-
merged.reject { |key, _| overrides.include?(key) }.symbolize_keys
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
def default_cop_config
|
|
65
|
-
ConfigLoader.default_configuration[cop_name]
|
|
66
|
-
end
|
|
67
49
|
end
|
|
68
50
|
end
|
|
69
51
|
end
|
|
@@ -47,7 +47,7 @@ module RuboCop
|
|
|
47
47
|
def check_backtick_literal(node)
|
|
48
48
|
return if style == :backticks && !contains_disallowed_backtick?(node)
|
|
49
49
|
return if style == :mixed &&
|
|
50
|
-
single_line?
|
|
50
|
+
node.single_line? &&
|
|
51
51
|
!contains_disallowed_backtick?(node)
|
|
52
52
|
|
|
53
53
|
add_offense(node, :expression, MSG_USE_PERCENT_X)
|
|
@@ -56,7 +56,7 @@ module RuboCop
|
|
|
56
56
|
def check_percent_x_literal(node)
|
|
57
57
|
return if style == :backticks && contains_disallowed_backtick?(node)
|
|
58
58
|
return if style == :percent_x
|
|
59
|
-
return if style == :mixed &&
|
|
59
|
+
return if style == :mixed && node.multiline?
|
|
60
60
|
return if style == :mixed && contains_disallowed_backtick?(node)
|
|
61
61
|
|
|
62
62
|
add_offense(node, :expression, MSG_USE_BACKTICKS)
|
|
@@ -87,14 +87,6 @@ module RuboCop
|
|
|
87
87
|
node.loc.begin.source == '`'
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
def single_line?(node)
|
|
91
|
-
!multi_line?(node)
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
def multi_line?(node)
|
|
95
|
-
block_length(node) > 1
|
|
96
|
-
end
|
|
97
|
-
|
|
98
90
|
def preferred_delimiters
|
|
99
91
|
config.for_cop('Style/PercentLiteralDelimiters') \
|
|
100
92
|
['PreferredDelimiters']['%x'].split(//)
|
|
@@ -8,30 +8,51 @@ module RuboCop
|
|
|
8
8
|
class CommentAnnotation < Cop
|
|
9
9
|
include AnnotationComment
|
|
10
10
|
|
|
11
|
-
MSG = 'Annotation keywords should be all upper case,
|
|
12
|
-
'
|
|
11
|
+
MSG = 'Annotation keywords like `%s` should be all upper case, ' \
|
|
12
|
+
'followed by a colon, and a space, ' \
|
|
13
|
+
'then a note describing the problem.'
|
|
14
|
+
MISSING_NOTE = 'Annotation comment, with keyword `%s`, ' \
|
|
15
|
+
'is missing a note.'
|
|
13
16
|
|
|
14
17
|
def investigate(processed_source)
|
|
15
|
-
processed_source.comments.
|
|
18
|
+
processed_source.comments.each_with_index do |comment, ix|
|
|
19
|
+
next unless first_comment_line?(processed_source.comments, ix)
|
|
20
|
+
|
|
16
21
|
margin, first_word, colon, space, note = split_comment(comment)
|
|
17
22
|
next unless annotation?(comment) &&
|
|
18
23
|
!correct_annotation?(first_word, colon, space, note)
|
|
19
24
|
|
|
20
25
|
start = comment.loc.expression.begin_pos + margin.length
|
|
21
26
|
length = first_word.length + colon.to_s.length + space.to_s.length
|
|
22
|
-
|
|
27
|
+
source_buffer = comment.loc.expression.source_buffer
|
|
28
|
+
range = Parser::Source::Range.new(source_buffer,
|
|
23
29
|
start,
|
|
24
30
|
start + length)
|
|
25
|
-
|
|
31
|
+
if note
|
|
32
|
+
add_offense(comment, range, format(MSG, first_word))
|
|
33
|
+
else
|
|
34
|
+
add_offense(comment, range, format(MISSING_NOTE, first_word))
|
|
35
|
+
end
|
|
26
36
|
end
|
|
27
37
|
end
|
|
28
38
|
|
|
29
39
|
private
|
|
30
40
|
|
|
31
|
-
def
|
|
41
|
+
def first_comment_line?(comments, ix)
|
|
42
|
+
ix == 0 || comments[ix - 1].loc.line < comments[ix].loc.line - 1
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def autocorrect(comment)
|
|
46
|
+
margin, first_word, colon, space, note = split_comment(comment)
|
|
47
|
+
start = comment.loc.expression.begin_pos + margin.length
|
|
48
|
+
return if note.nil?
|
|
49
|
+
|
|
32
50
|
lambda do |corrector|
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
length = first_word.length + colon.to_s.length + space.to_s.length
|
|
52
|
+
range = Parser::Source::Range.new(comment.loc.expression.source,
|
|
53
|
+
start,
|
|
54
|
+
start + length)
|
|
55
|
+
corrector.replace(range, "#{first_word.upcase}: ")
|
|
35
56
|
end
|
|
36
57
|
end
|
|
37
58
|
|
|
@@ -68,10 +68,12 @@ module RuboCop
|
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def autocorrect(token)
|
|
71
|
-
fail 'An AutocorrectNotice must be defined in ' \
|
|
71
|
+
fail Warning, 'An AutocorrectNotice must be defined in ' \
|
|
72
72
|
'your RuboCop config' if autocorrect_notice.empty?
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
regex = Regexp.new(notice)
|
|
74
|
+
fail Warning, "AutocorrectNotice '#{autocorrect_notice}' must " \
|
|
75
|
+
"match Notice /#{notice}/" unless autocorrect_notice =~ regex
|
|
76
|
+
|
|
75
77
|
lambda do |corrector|
|
|
76
78
|
if token.nil?
|
|
77
79
|
range = Parser::Source::Range.new('', 0, 0)
|
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
|
6
6
|
# This cop checks for missing top-level documentation of
|
|
7
7
|
# classes and modules. Classes with no body are exempt from the
|
|
8
8
|
# check and so are namespace modules - modules that have nothing in
|
|
9
|
-
# their bodies except classes or other
|
|
9
|
+
# their bodies except classes or other modules.
|
|
10
10
|
#
|
|
11
11
|
# The documentation requirement is annulled if the class or module has
|
|
12
12
|
# a "#:nodoc:" comment next to it. Likewise, "#:nodoc: all" does the
|
|
@@ -47,34 +47,43 @@ module RuboCop
|
|
|
47
47
|
end
|
|
48
48
|
end
|
|
49
49
|
|
|
50
|
+
# 'Const = Class.new' or 'Const = Module.new' are module definitions
|
|
51
|
+
mod_new = '(send (const nil {:Class :Module}) :new ...)'
|
|
52
|
+
def_node_matcher :module_definition?,
|
|
53
|
+
"{class
|
|
54
|
+
module
|
|
55
|
+
(casgn _ _ {#{mod_new} (block #{mod_new} ...)})}"
|
|
56
|
+
|
|
50
57
|
def namespace?(body_node)
|
|
51
58
|
return false unless body_node
|
|
52
59
|
|
|
53
60
|
case body_node.type
|
|
54
61
|
when :begin
|
|
55
|
-
body_node.children.all?
|
|
56
|
-
[:class, :module].include?(node.type)
|
|
57
|
-
end
|
|
58
|
-
when :class, :module
|
|
59
|
-
true
|
|
62
|
+
body_node.children.all? { |node| module_definition?(node) }
|
|
60
63
|
else
|
|
61
|
-
|
|
64
|
+
module_definition?(body_node)
|
|
62
65
|
end
|
|
63
66
|
end
|
|
64
67
|
|
|
65
68
|
# Returns true if the node has a comment on the line above it that
|
|
66
69
|
# isn't an annotation.
|
|
67
70
|
def associated_comment?(node, ast_with_comments)
|
|
68
|
-
|
|
71
|
+
preceding_comments = preceding_comments(node, ast_with_comments)
|
|
72
|
+
return false if preceding_comments.empty?
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
distance = node.loc.keyword.line - preceding_comment.loc.line
|
|
74
|
+
distance = node.loc.keyword.line - preceding_comments.last.loc.line
|
|
72
75
|
return false if distance > 1
|
|
73
|
-
return false unless comment_line_only?(
|
|
76
|
+
return false unless comment_line_only?(preceding_comments.last)
|
|
74
77
|
|
|
75
78
|
# As long as there's at least one comment line that isn't an
|
|
76
79
|
# annotation, it's OK.
|
|
77
|
-
|
|
80
|
+
preceding_comments.any? { |comment| !annotation?(comment) }
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def preceding_comments(node, ast_with_comments)
|
|
84
|
+
return [] unless node && ast_with_comments
|
|
85
|
+
|
|
86
|
+
ast_with_comments[node].select { |c| c.loc.line < node.loc.line }
|
|
78
87
|
end
|
|
79
88
|
|
|
80
89
|
def comment_line_only?(comment)
|
|
@@ -46,6 +46,12 @@ module RuboCop
|
|
|
46
46
|
|
|
47
47
|
dot_line = node.loc.dot.line
|
|
48
48
|
|
|
49
|
+
# don't register an offense if there is a line comment between
|
|
50
|
+
# the dot and the selector
|
|
51
|
+
# otherwise, we might break the code while "correcting" it
|
|
52
|
+
# (even if there is just an extra blank line, treat it the same)
|
|
53
|
+
return true if (selector_line - dot_line) > 1
|
|
54
|
+
|
|
49
55
|
case style
|
|
50
56
|
when :leading then dot_line == selector_line
|
|
51
57
|
when :trailing then dot_line != selector_line
|
|
@@ -22,22 +22,11 @@ module RuboCop
|
|
|
22
22
|
class DoubleNegation < Cop
|
|
23
23
|
MSG = 'Avoid the use of double negation (`!!`).'
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
return unless not_node?(node)
|
|
27
|
-
|
|
28
|
-
receiver, _method_name, *_args = *node
|
|
29
|
-
|
|
30
|
-
add_offense(node, :selector) if not_node?(receiver)
|
|
31
|
-
end
|
|
25
|
+
def_node_matcher :double_negative?, '(send (send _ :!) :!)'
|
|
32
26
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
_receiver, method_name, *args = *node
|
|
37
|
-
|
|
38
|
-
# ! does not take any arguments
|
|
39
|
-
args.empty? && method_name == :! &&
|
|
40
|
-
node.loc.selector.is?('!')
|
|
27
|
+
def on_send(node)
|
|
28
|
+
return unless double_negative?(node) && node.loc.selector.is?('!')
|
|
29
|
+
add_offense(node, :selector)
|
|
41
30
|
end
|
|
42
31
|
end
|
|
43
32
|
end
|
|
@@ -7,6 +7,9 @@ module RuboCop
|
|
|
7
7
|
# returned at the end and so could be replace by each_with_object without
|
|
8
8
|
# the need to return the object at the end.
|
|
9
9
|
#
|
|
10
|
+
# However, we can't replace with each_with_object if the accumulator
|
|
11
|
+
# parameter is assigned to within the block.
|
|
12
|
+
#
|
|
10
13
|
# @example
|
|
11
14
|
# # bad
|
|
12
15
|
# [1, 2].inject({}) { |a, e| a[e] = e; a }
|
|
@@ -23,16 +26,26 @@ module RuboCop
|
|
|
23
26
|
# filter out super and zsuper nodes
|
|
24
27
|
return unless method.type == :send
|
|
25
28
|
|
|
26
|
-
_, method_name,
|
|
29
|
+
_, method_name, method_arg = *method
|
|
27
30
|
|
|
28
31
|
return unless METHODS.include? method_name
|
|
29
|
-
return if
|
|
32
|
+
return if method_arg && BASIC_LITERALS.include?(method_arg.type)
|
|
30
33
|
|
|
31
34
|
return_value = return_value(body)
|
|
32
35
|
return unless return_value
|
|
33
36
|
|
|
34
37
|
return unless first_argument_returned?(args, return_value)
|
|
35
38
|
|
|
39
|
+
# if the accumulator parameter is assigned to in the block,
|
|
40
|
+
# then we can't convert to each_with_object
|
|
41
|
+
first_arg, = *args
|
|
42
|
+
accumulator_var, = *first_arg
|
|
43
|
+
return if body.each_descendant.any? do |n|
|
|
44
|
+
next unless n.assignment?
|
|
45
|
+
lhs, _rhs = *n
|
|
46
|
+
lhs.equal?(accumulator_var)
|
|
47
|
+
end
|
|
48
|
+
|
|
36
49
|
add_offense(method, :selector, format(MSG, method_name))
|
|
37
50
|
end
|
|
38
51
|
|
|
@@ -47,8 +60,8 @@ module RuboCop
|
|
|
47
60
|
|
|
48
61
|
def first_argument_returned?(args, return_value)
|
|
49
62
|
first_arg, = *args
|
|
50
|
-
accumulator_var = *first_arg
|
|
51
|
-
return_var = *return_value
|
|
63
|
+
accumulator_var, = *first_arg
|
|
64
|
+
return_var, = *return_value
|
|
52
65
|
|
|
53
66
|
accumulator_var == return_var
|
|
54
67
|
end
|
|
@@ -8,6 +8,7 @@ module RuboCop
|
|
|
8
8
|
# are special cases when they should follow the same rules as the
|
|
9
9
|
# alignment of end.
|
|
10
10
|
class ElseAlignment < Cop
|
|
11
|
+
include EndKeywordAlignment
|
|
11
12
|
include AutocorrectAlignment
|
|
12
13
|
include CheckAssignment
|
|
13
14
|
|
|
@@ -86,7 +87,7 @@ module RuboCop
|
|
|
86
87
|
|
|
87
88
|
end_config = config.for_cop('Lint/EndAlignment')
|
|
88
89
|
style = end_config['Enabled'] ? end_config['AlignWith'] : 'keyword'
|
|
89
|
-
base =
|
|
90
|
+
base = variable_alignment?(node.loc, rhs, style.to_sym) ? node : rhs
|
|
90
91
|
|
|
91
92
|
return if rhs.type != :if
|
|
92
93
|
|
|
@@ -110,6 +110,31 @@ module RuboCop
|
|
|
110
110
|
add_offense(node, :else, MSG)
|
|
111
111
|
end
|
|
112
112
|
end
|
|
113
|
+
|
|
114
|
+
def autocorrect(node)
|
|
115
|
+
return false if autocorrect_forbidden?(node.type.to_s)
|
|
116
|
+
|
|
117
|
+
lambda do |corrector|
|
|
118
|
+
end_pos = if node.loc.end
|
|
119
|
+
node.loc.end.begin_pos
|
|
120
|
+
else
|
|
121
|
+
node.loc.expression.end_pos + 1
|
|
122
|
+
end
|
|
123
|
+
range = Parser::Source::Range.new(node.loc.expression.source_buffer,
|
|
124
|
+
node.loc.else.begin_pos,
|
|
125
|
+
end_pos)
|
|
126
|
+
corrector.remove(range)
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def autocorrect_forbidden?(type)
|
|
131
|
+
[type, 'both'].include? missing_else_style
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def missing_else_style
|
|
135
|
+
missing_config = config.for_cop('Style/MissingElse')
|
|
136
|
+
missing_config['Enabled'] ? missing_config['EnforcedStyle'] : nil
|
|
137
|
+
end
|
|
113
138
|
end
|
|
114
139
|
end
|
|
115
140
|
end
|