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
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# This lint sees if there is a mismatch between the number of
|
|
7
|
+
# expected fields for format/sprintf/#% and what is actually
|
|
8
|
+
# passed as arguments.
|
|
9
|
+
#
|
|
10
|
+
# @example
|
|
11
|
+
#
|
|
12
|
+
# format('A value: %s and another: %i', a_value)
|
|
13
|
+
#
|
|
14
|
+
class FormatParameterMismatch < Cop
|
|
15
|
+
# http://rubular.com/r/CvpbxkcTzy
|
|
16
|
+
MSG = 'Number arguments (%i) to `%s` mismatches expected fields (%i).'
|
|
17
|
+
FIELD_REGEX = /(%(([\s#+-0\*])?(\d*)?(.\d+)?(\.)?[bBdiouxXeEfgGaAcps]|%))/.freeze # rubocop:disable Metrics/LineLength
|
|
18
|
+
NAMED_FIELD_REGEX = /%\{[_a-zA-Z][_a-zA-Z]+\}/.freeze
|
|
19
|
+
KERNEL = 'Kernel'.freeze
|
|
20
|
+
SHOVEL = '<<'.freeze
|
|
21
|
+
PERCENT = '%'.freeze
|
|
22
|
+
PERCENT_PERCENT = '%%'.freeze
|
|
23
|
+
SPLAT = '*'.freeze
|
|
24
|
+
STRING_TYPES = [:str, :dstr].freeze
|
|
25
|
+
|
|
26
|
+
def on_send(node)
|
|
27
|
+
add_offense(node, :selector) if offending_node?(node)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def offending_node?(node)
|
|
33
|
+
return false unless called_on_string?(node)
|
|
34
|
+
|
|
35
|
+
if sprintf?(node) || format?(node) || percent?(node)
|
|
36
|
+
if named_mode?(node) || node_with_splat_args?(node)
|
|
37
|
+
false
|
|
38
|
+
else
|
|
39
|
+
num_of_format_args, num_of_expected_fields = count_matches(node)
|
|
40
|
+
|
|
41
|
+
num_of_format_args != :unknown &&
|
|
42
|
+
num_of_expected_fields != :unknown &&
|
|
43
|
+
num_of_expected_fields != num_of_format_args
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
false
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def called_on_string?(node)
|
|
51
|
+
receiver_node, _method, format_string, = *node
|
|
52
|
+
if receiver_node.nil? || receiver_node.const_type?
|
|
53
|
+
format_string && format_string.str_type?
|
|
54
|
+
else
|
|
55
|
+
receiver_node.str_type?
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def named_mode?(node)
|
|
60
|
+
receiver_node, _method_name, *args = *node
|
|
61
|
+
|
|
62
|
+
relevant_node = if sprintf?(node) || format?(node)
|
|
63
|
+
args.first
|
|
64
|
+
elsif percent?(node)
|
|
65
|
+
receiver_node
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
relevant_node.loc.expression.source.scan(NAMED_FIELD_REGEX).size > 0
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def node_with_splat_args?(node)
|
|
72
|
+
return false if percent?(node)
|
|
73
|
+
|
|
74
|
+
_receiver_node, _method_name, *args = *node
|
|
75
|
+
|
|
76
|
+
args[1..-1].any? { |arg| arg.type == :splat }
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def heredoc?(node)
|
|
80
|
+
_receiver, _name, args = *node
|
|
81
|
+
|
|
82
|
+
args.loc.expression.source[0, 2] == SHOVEL
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def literal?(node)
|
|
86
|
+
node.int_type? ||
|
|
87
|
+
node.str_type? ||
|
|
88
|
+
node.sym_type? ||
|
|
89
|
+
node.float_type? ||
|
|
90
|
+
node.dstr_type? ||
|
|
91
|
+
node.dsym_type?
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def count_matches(node)
|
|
95
|
+
receiver_node, _method_name, *args = *node
|
|
96
|
+
|
|
97
|
+
if (sprintf?(node) || format?(node)) && !heredoc?(node)
|
|
98
|
+
number_of_args_for_format = (args.size - 1)
|
|
99
|
+
number_of_expected_fields = expected_fields_count(args.first)
|
|
100
|
+
elsif percent?(node) && args.first.array_type?
|
|
101
|
+
number_of_expected_fields = expected_fields_count(receiver_node)
|
|
102
|
+
|
|
103
|
+
first_child_argument = args.first
|
|
104
|
+
if first_child_argument.array_type?
|
|
105
|
+
number_of_args_for_format = args.first.child_nodes.size
|
|
106
|
+
elsif literal?(first_child_argument)
|
|
107
|
+
number_of_args_for_format = 1
|
|
108
|
+
else
|
|
109
|
+
# non-literals might evaluate to an Array, or they might not
|
|
110
|
+
# so we can't tell just how many format args there will be
|
|
111
|
+
number_of_args_for_format = :unknown
|
|
112
|
+
end
|
|
113
|
+
else
|
|
114
|
+
number_of_args_for_format = number_of_expected_fields = :unknown
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
[number_of_args_for_format, number_of_expected_fields]
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def format_method?(name, node)
|
|
121
|
+
receiver, method_name, *args = *node
|
|
122
|
+
|
|
123
|
+
if receiver && receiver.const_type?
|
|
124
|
+
return false unless receiver.loc.name.is?(KERNEL)
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
return false unless method_name == name
|
|
128
|
+
|
|
129
|
+
args.size > 1 && args.first.str_type?
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def expected_fields_count(node)
|
|
133
|
+
return :unknown unless node.str_type?
|
|
134
|
+
node
|
|
135
|
+
.loc
|
|
136
|
+
.expression
|
|
137
|
+
.source
|
|
138
|
+
.scan(FIELD_REGEX)
|
|
139
|
+
.select { |x| x.first != PERCENT_PERCENT }
|
|
140
|
+
.reduce(0) { |a, e| a + (e[2] == SPLAT ? 2 : 1) }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def format?(node)
|
|
144
|
+
format_method?(:format, node)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def sprintf?(node)
|
|
148
|
+
format_method?(:sprintf, node)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def percent?(node)
|
|
152
|
+
receiver_node, method_name, *arg_nodes = *node
|
|
153
|
+
|
|
154
|
+
percent = method_name == :% &&
|
|
155
|
+
(STRING_TYPES.include?(receiver_node.type) ||
|
|
156
|
+
arg_nodes[0].array_type?)
|
|
157
|
+
|
|
158
|
+
if percent && STRING_TYPES.include?(receiver_node.type)
|
|
159
|
+
return false if heredoc?(node)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
percent
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def message(node)
|
|
166
|
+
_receiver, method_name, *_args = *node
|
|
167
|
+
num_args_for_format, num_expected_fields = count_matches(node)
|
|
168
|
+
|
|
169
|
+
method_name = 'String#%' if PERCENT == method_name.to_s
|
|
170
|
+
format(MSG, num_args_for_format, method_name, num_expected_fields)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
end
|
|
@@ -20,11 +20,6 @@ module RuboCop
|
|
|
20
20
|
class LiteralInCondition < Cop
|
|
21
21
|
MSG = 'Literal `%s` appeared in a condition.'
|
|
22
22
|
|
|
23
|
-
LITERALS = [:str, :dstr, :int, :float, :array,
|
|
24
|
-
:hash, :regexp, :nil, :true, :false]
|
|
25
|
-
|
|
26
|
-
BASIC_LITERALS = LITERALS - [:dstr, :array, :hash]
|
|
27
|
-
|
|
28
23
|
def on_if(node)
|
|
29
24
|
check_for_literal(node)
|
|
30
25
|
end
|
|
@@ -25,6 +25,12 @@ module RuboCop
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
def autocorrect(node)
|
|
29
|
+
expr = node.parent.loc.expression
|
|
30
|
+
value = autocorrected_value(node)
|
|
31
|
+
->(corrector) { corrector.replace(expr, value) }
|
|
32
|
+
end
|
|
33
|
+
|
|
28
34
|
private
|
|
29
35
|
|
|
30
36
|
def special_keyword?(node)
|
|
@@ -32,6 +38,10 @@ module RuboCop
|
|
|
32
38
|
(node.type == :str && !node.loc.respond_to?(:begin)) ||
|
|
33
39
|
node.loc.expression.is?('__LINE__')
|
|
34
40
|
end
|
|
41
|
+
|
|
42
|
+
def autocorrected_value(node)
|
|
43
|
+
node.str_type? ? node.children.last : node.loc.expression.source
|
|
44
|
+
end
|
|
35
45
|
end
|
|
36
46
|
end
|
|
37
47
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# This cop checks for nested method definitions.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # `bar` definition actually produces methods in the same scope
|
|
10
|
+
# # as the outer `foo` method. Furthermore, the `bar` method
|
|
11
|
+
# # will be redefined every time the `foo` is invoked
|
|
12
|
+
# def foo
|
|
13
|
+
# def bar
|
|
14
|
+
# end
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
class NestedMethodDefinition < Cop
|
|
18
|
+
include OnMethodDef
|
|
19
|
+
|
|
20
|
+
MSG = 'Method definitions must not be nested. ' \
|
|
21
|
+
'Use `lambda` instead.'
|
|
22
|
+
|
|
23
|
+
def on_method_def(node, _method_name, _args, _body)
|
|
24
|
+
node.each_descendant(:def) do |nested_def_node|
|
|
25
|
+
add_offense(nested_def_node, :expression)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -5,7 +5,8 @@ module RuboCop
|
|
|
5
5
|
module Lint
|
|
6
6
|
# This cop checks for non-local exit from iterator, without return value.
|
|
7
7
|
# It warns only when satisfies all of these: `return` doesn't have return
|
|
8
|
-
# value, block
|
|
8
|
+
# value, the block is preceded by a method chain, the block has arguments,
|
|
9
|
+
# and the method which receives the block is not `define_method`.
|
|
9
10
|
#
|
|
10
11
|
# @example
|
|
11
12
|
#
|
|
@@ -39,6 +40,13 @@ module RuboCop
|
|
|
39
40
|
return if return_value?(return_node)
|
|
40
41
|
return_node.each_ancestor(:block) do |block_node|
|
|
41
42
|
send_node, args_node, _body_node = *block_node
|
|
43
|
+
|
|
44
|
+
# `return` does not exit to outside of lambda block, this is safe.
|
|
45
|
+
break if lambda?(send_node)
|
|
46
|
+
# if a proc is passed to `Module#define_method`, `return` will not
|
|
47
|
+
# cause a non-local exit error
|
|
48
|
+
break if define_method?(send_node)
|
|
49
|
+
|
|
42
50
|
next if args_node.children.empty?
|
|
43
51
|
if chained_send?(send_node)
|
|
44
52
|
add_offense(return_node, :keyword)
|
|
@@ -55,6 +63,16 @@ module RuboCop
|
|
|
55
63
|
receiver_node, _selector_node = *send_node
|
|
56
64
|
!receiver_node.nil?
|
|
57
65
|
end
|
|
66
|
+
|
|
67
|
+
def lambda?(send_node)
|
|
68
|
+
receiver_node, selector_node = *send_node
|
|
69
|
+
receiver_node.nil? && selector_node == :lambda
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def define_method?(send_node)
|
|
73
|
+
_receiver, selector = *send_node
|
|
74
|
+
selector == :define_method
|
|
75
|
+
end
|
|
58
76
|
end
|
|
59
77
|
end
|
|
60
78
|
end
|
|
@@ -14,7 +14,7 @@ module RuboCop
|
|
|
14
14
|
|
|
15
15
|
def on_send(node)
|
|
16
16
|
_receiver, method_name, args = *node
|
|
17
|
-
return if operator?(method_name) ||
|
|
17
|
+
return if operator?(method_name) || node.asgn_method_call?
|
|
18
18
|
return unless args && args.loc.expression.source.start_with?('(')
|
|
19
19
|
|
|
20
20
|
space_length = spaces_before_left_parenthesis(node)
|
|
@@ -20,7 +20,7 @@ module RuboCop
|
|
|
20
20
|
_receiver, method_name, *args = *node
|
|
21
21
|
return if args.empty?
|
|
22
22
|
return if operator?(method_name)
|
|
23
|
-
return if
|
|
23
|
+
return if node.asgn_method_call?
|
|
24
24
|
|
|
25
25
|
# Setter calls with parentheses are parsed this way. The parentheses
|
|
26
26
|
# belong to the argument, not the send node.
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# This cop detects instances of rubocop:disable comments that can be
|
|
7
|
+
# removed without causing any offenses to be reported. It's implemented
|
|
8
|
+
# as a cop in that it inherits from the Cop base class and calls
|
|
9
|
+
# add_offense. The unusual part of its implementation is that it doesn't
|
|
10
|
+
# have any on_* methods or an investigate method. This means that it
|
|
11
|
+
# doesn't take part in the investigation phase when the other cops do
|
|
12
|
+
# their work. Instead, it waits until it's called in a later stage of the
|
|
13
|
+
# execution. The reason it can't be implemented as a normal cop is that
|
|
14
|
+
# it depends on the results of all other cops to do its work.
|
|
15
|
+
class UnneededDisable < Cop
|
|
16
|
+
COP_NAME = 'Lint/UnneededDisable'
|
|
17
|
+
|
|
18
|
+
def check(offenses, cop_disabled_line_ranges, comments)
|
|
19
|
+
unneeded_cops = {}
|
|
20
|
+
disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
|
|
21
|
+
|
|
22
|
+
cop_disabled_line_ranges.each do |cop, line_ranges|
|
|
23
|
+
cop_offenses = offenses.select { |o| o.cop_name == cop }
|
|
24
|
+
line_ranges.each do |line_range|
|
|
25
|
+
comment = comments.find { |c| c.loc.line == line_range.begin }
|
|
26
|
+
unneeded_cop = find_unneeded(comment, offenses, cop, cop_offenses,
|
|
27
|
+
line_range)
|
|
28
|
+
|
|
29
|
+
unless all_disabled?(comment)
|
|
30
|
+
next if ignore_offense?(disabled_ranges, line_range)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
if unneeded_cop
|
|
34
|
+
unneeded_cops[comment.loc.expression] ||= Set.new
|
|
35
|
+
unneeded_cops[comment.loc.expression].add(unneeded_cop)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
add_offenses(unneeded_cops)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def find_unneeded(comment, offenses, cop, cop_offenses, line_range)
|
|
46
|
+
if all_disabled?(comment)
|
|
47
|
+
'all cops' if offenses.none? { |o| line_range.include?(o.line) }
|
|
48
|
+
elsif cop_offenses.none? { |o| line_range.include?(o.line) }
|
|
49
|
+
cop
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def all_disabled?(comment)
|
|
54
|
+
comment.loc.expression.source =~ /rubocop:disable\s+all\b/
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def ignore_offense?(disabled_ranges, line_range)
|
|
58
|
+
disabled_ranges.any? do |range|
|
|
59
|
+
range.include?(line_range.min) && range.include?(line_range.max)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def add_offenses(unneeded_cops)
|
|
64
|
+
unneeded_cops.each do |range, cops|
|
|
65
|
+
add_offense(range, range,
|
|
66
|
+
"Unnecessary disabling of #{cops.sort.join(', ')}.")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -15,6 +15,14 @@ module RuboCop
|
|
|
15
15
|
|
|
16
16
|
def check_argument(variable)
|
|
17
17
|
return unless variable.method_argument?
|
|
18
|
+
return if variable.keyword_argument? &&
|
|
19
|
+
cop_config['AllowUnusedKeywordArguments']
|
|
20
|
+
|
|
21
|
+
if cop_config['IgnoreEmptyMethods']
|
|
22
|
+
_name, _args, body = *variable.scope.node
|
|
23
|
+
return if body.nil?
|
|
24
|
+
end
|
|
25
|
+
|
|
18
26
|
super
|
|
19
27
|
end
|
|
20
28
|
|
|
@@ -10,16 +10,27 @@ module RuboCop
|
|
|
10
10
|
include MethodComplexity
|
|
11
11
|
|
|
12
12
|
MSG = 'Assignment Branch Condition size for %s is too high. [%.4g/%.4g]'
|
|
13
|
-
BRANCH_NODES = [:send]
|
|
14
|
-
CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES
|
|
13
|
+
BRANCH_NODES = [:send].freeze
|
|
14
|
+
CONDITION_NODES = CyclomaticComplexity::COUNTED_NODES.freeze
|
|
15
15
|
|
|
16
16
|
private
|
|
17
17
|
|
|
18
18
|
def complexity(node)
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
assignment = 0
|
|
20
|
+
branch = 0
|
|
21
|
+
condition = 0
|
|
22
|
+
|
|
23
|
+
node.each_node do |child|
|
|
24
|
+
if child.assignment?
|
|
25
|
+
assignment += 1
|
|
26
|
+
elsif BRANCH_NODES.include?(child.type)
|
|
27
|
+
branch += 1
|
|
28
|
+
elsif CONDITION_NODES.include?(child.type)
|
|
29
|
+
condition += 1
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Math.sqrt(assignment**2 + branch**2 + condition**2).round(2)
|
|
23
34
|
end
|
|
24
35
|
end
|
|
25
36
|
end
|
|
@@ -40,7 +40,7 @@ module RuboCop
|
|
|
40
40
|
# Returns true when the block node looks like Class or Module.new do ... .
|
|
41
41
|
def class_constructor?(block_node)
|
|
42
42
|
send_node = block_node.children.first
|
|
43
|
-
receiver_node, method_name,
|
|
43
|
+
receiver_node, method_name, = *send_node
|
|
44
44
|
return false unless method_name == :new
|
|
45
45
|
%w(Class Module).include?(Util.const_name(receiver_node))
|
|
46
46
|
end
|
|
@@ -16,8 +16,7 @@ module RuboCop
|
|
|
16
16
|
def split_comment(comment)
|
|
17
17
|
match = comment.text.match(/^(# ?)([A-Za-z]+)(\s*:)?(\s+)?(\S+)?/)
|
|
18
18
|
return false unless match
|
|
19
|
-
|
|
20
|
-
[margin, first_word, colon, space, note]
|
|
19
|
+
match.captures
|
|
21
20
|
end
|
|
22
21
|
|
|
23
22
|
def keyword_appearance?(first_word, colon, space)
|
|
@@ -6,27 +6,51 @@ module RuboCop
|
|
|
6
6
|
# the left or to the right, amount being determined by the instance
|
|
7
7
|
# variable @column_delta.
|
|
8
8
|
module AutocorrectAlignment
|
|
9
|
+
SPACE = ' '.freeze
|
|
10
|
+
|
|
9
11
|
def configured_indentation_width
|
|
10
12
|
config.for_cop('IndentationWidth')['Width']
|
|
11
13
|
end
|
|
12
14
|
|
|
15
|
+
def indentation(node)
|
|
16
|
+
offset(node) + (SPACE * configured_indentation_width)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def offset(node)
|
|
20
|
+
SPACE * node.loc.column
|
|
21
|
+
end
|
|
22
|
+
|
|
13
23
|
def check_alignment(items, base_column = nil)
|
|
14
24
|
base_column ||= items.first.loc.column unless items.empty?
|
|
15
25
|
prev_line = -1
|
|
16
26
|
items.each do |current|
|
|
17
27
|
if current.loc.line > prev_line && start_of_line?(current.loc)
|
|
18
28
|
@column_delta = base_column - current.loc.column
|
|
19
|
-
|
|
29
|
+
if @column_delta != 0
|
|
30
|
+
expr = current.loc.expression
|
|
31
|
+
if offenses.any? { |o| within?(expr, o.location) }
|
|
32
|
+
# If this offense is within a line range that is already being
|
|
33
|
+
# realigned by autocorrect, we report the offense without
|
|
34
|
+
# autocorrecting it. Two rewrites in the same area by the same
|
|
35
|
+
# cop can not be handled. The next iteration will find the
|
|
36
|
+
# offense again and correct it.
|
|
37
|
+
add_offense(nil, expr)
|
|
38
|
+
else
|
|
39
|
+
add_offense(current, :expression)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
20
42
|
end
|
|
21
43
|
prev_line = current.loc.line
|
|
22
44
|
end
|
|
23
45
|
end
|
|
24
46
|
|
|
25
47
|
def start_of_line?(loc)
|
|
26
|
-
loc.expression.source_line[0...loc.column]
|
|
48
|
+
loc.expression.source_line[0...loc.column].blank?
|
|
27
49
|
end
|
|
28
50
|
|
|
29
51
|
def autocorrect(arg)
|
|
52
|
+
return unless arg
|
|
53
|
+
|
|
30
54
|
heredoc_ranges = heredoc_ranges(arg)
|
|
31
55
|
expr = arg.respond_to?(:loc) ? arg.loc.expression : arg
|
|
32
56
|
|
|
@@ -58,7 +82,7 @@ module RuboCop
|
|
|
58
82
|
corrector.insert_before(range, ' ' * column_delta)
|
|
59
83
|
end
|
|
60
84
|
else
|
|
61
|
-
remove(range, corrector) if range.source =~
|
|
85
|
+
remove(range, corrector) if range.source =~ /\A[ \t]+\z/
|
|
62
86
|
end
|
|
63
87
|
end
|
|
64
88
|
|
|
@@ -75,7 +99,7 @@ module RuboCop
|
|
|
75
99
|
end
|
|
76
100
|
|
|
77
101
|
def block_comment_within?(expr)
|
|
78
|
-
processed_source.comments.select(&:document?).
|
|
102
|
+
processed_source.comments.select(&:document?).any? do |c|
|
|
79
103
|
within?(c.loc.expression, expr)
|
|
80
104
|
end
|
|
81
105
|
end
|
|
@@ -5,6 +5,10 @@ module RuboCop
|
|
|
5
5
|
# This module does auto-correction of nodes that could become grammatically
|
|
6
6
|
# different after the correction. If the code change would alter the
|
|
7
7
|
# abstract syntax tree, it is not done.
|
|
8
|
+
#
|
|
9
|
+
# However, if the code change merely introduces extraneous "begin" nodes
|
|
10
|
+
# which do not change the meaning of the code, it is still accepted.
|
|
11
|
+
#
|
|
8
12
|
module AutocorrectUnlessChangingAST
|
|
9
13
|
def autocorrect(node)
|
|
10
14
|
current_buffer_src = processed_source.buffer.source
|
|
@@ -12,11 +16,12 @@ module RuboCop
|
|
|
12
16
|
pre = current_buffer_src[0...replaced_range.begin_pos]
|
|
13
17
|
post = current_buffer_src[replaced_range.end_pos..-1]
|
|
14
18
|
new_buffer_src = pre + rewrite_node(node) + post
|
|
19
|
+
new_processed_src = ProcessedSource.new(new_buffer_src)
|
|
15
20
|
|
|
16
21
|
# Make the correction only if it doesn't change the AST for the buffer.
|
|
17
|
-
if
|
|
18
|
-
|
|
19
|
-
|
|
22
|
+
return if !new_processed_src.ast ||
|
|
23
|
+
(INLINE_BEGIN.process(processed_source.ast) !=
|
|
24
|
+
INLINE_BEGIN.process(new_processed_src.ast))
|
|
20
25
|
|
|
21
26
|
correction(node)
|
|
22
27
|
end
|
|
@@ -28,6 +33,24 @@ module RuboCop
|
|
|
28
33
|
c = correction(ps.ast)
|
|
29
34
|
Corrector.new(ps.buffer, [c]).rewrite
|
|
30
35
|
end
|
|
36
|
+
|
|
37
|
+
# 'begin' nodes with a single child can be removed without changing
|
|
38
|
+
# the semantics of an AST. Canonicalizing an AST in this way can help
|
|
39
|
+
# us determine whether it has really changed in a meaningful way, or
|
|
40
|
+
# not. This means we can auto-correct in cases where we would otherwise
|
|
41
|
+
# refrain from doing so.
|
|
42
|
+
#
|
|
43
|
+
# If any other simplifications can be done to an AST without changing
|
|
44
|
+
# its meaning, they should be added here (and the class renamed).
|
|
45
|
+
# This will make autocorrection more powerful across the board.
|
|
46
|
+
#
|
|
47
|
+
class InlineBeginNodes < Parser::AST::Processor
|
|
48
|
+
def on_begin(node)
|
|
49
|
+
node.children.one? ? node.children[0] : node
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
INLINE_BEGIN = InlineBeginNodes.new
|
|
31
54
|
end
|
|
32
55
|
end
|
|
33
56
|
end
|
|
@@ -23,13 +23,12 @@ module RuboCop
|
|
|
23
23
|
end
|
|
24
24
|
|
|
25
25
|
def on_send(node)
|
|
26
|
-
_receiver, method_name, *_, rhs = *node
|
|
27
|
-
|
|
28
26
|
# we only want to indent relative to the receiver
|
|
29
27
|
# when the method called looks like a setter
|
|
30
|
-
return unless
|
|
28
|
+
return unless node.asgn_method_call?
|
|
31
29
|
|
|
32
30
|
# This will match if, case, begin, blocks, etc.
|
|
31
|
+
rhs = node.children.last
|
|
33
32
|
check_assignment(node, rhs) if rhs.is_a?(AST::Node)
|
|
34
33
|
end
|
|
35
34
|
end
|