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
|
@@ -23,54 +23,60 @@ module RuboCop
|
|
|
23
23
|
include ConfigurableEnforcedStyle
|
|
24
24
|
include MinBodyLength
|
|
25
25
|
|
|
26
|
-
MSG = 'Use `next` to skip iteration.'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
MSG = 'Use `next` to skip iteration.'.freeze
|
|
27
|
+
EXIT_TYPES = [:break, :return].freeze
|
|
28
|
+
EACH_ = 'each_'.freeze
|
|
29
|
+
ENUMERATORS = [:collect, :collect_concat, :detect, :downto, :each,
|
|
30
|
+
:find, :find_all, :find_index, :inject, :loop, :map!,
|
|
31
|
+
:map, :reduce, :reject, :reject!, :reverse_each, :select,
|
|
32
|
+
:select!, :times, :upto].freeze
|
|
30
33
|
|
|
31
34
|
def on_block(node)
|
|
32
35
|
block_owner, _, body = *node
|
|
33
|
-
return unless block_owner.
|
|
34
|
-
return
|
|
36
|
+
return unless block_owner.send_type?
|
|
37
|
+
return unless body && ends_with_condition?(body)
|
|
35
38
|
|
|
36
39
|
_, method_name = *block_owner
|
|
37
40
|
return unless enumerator?(method_name)
|
|
38
|
-
return unless ends_with_condition?(body)
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
offense_node = offense_node(body)
|
|
43
|
+
add_offense(offense_node, offense_location(offense_node), MSG)
|
|
41
44
|
end
|
|
42
45
|
|
|
43
46
|
def on_while(node)
|
|
44
47
|
_, body = *node
|
|
45
48
|
return unless body && ends_with_condition?(body)
|
|
46
49
|
|
|
47
|
-
|
|
50
|
+
offense_node = offense_node(body)
|
|
51
|
+
add_offense(offense_node, offense_location(offense_node), MSG)
|
|
48
52
|
end
|
|
49
53
|
alias_method :on_until, :on_while
|
|
50
54
|
|
|
51
55
|
def on_for(node)
|
|
52
56
|
_, _, body = *node
|
|
53
|
-
return unless ends_with_condition?(body)
|
|
57
|
+
return unless body && ends_with_condition?(body)
|
|
54
58
|
|
|
55
|
-
|
|
59
|
+
offense_node = offense_node(body)
|
|
60
|
+
add_offense(offense_node, offense_location(offense_node), MSG)
|
|
56
61
|
end
|
|
57
62
|
|
|
58
63
|
private
|
|
59
64
|
|
|
60
65
|
def enumerator?(method_name)
|
|
61
|
-
ENUMERATORS.include?(method_name) ||
|
|
66
|
+
ENUMERATORS.include?(method_name) ||
|
|
67
|
+
method_name.to_s.start_with?(EACH_)
|
|
62
68
|
end
|
|
63
69
|
|
|
64
70
|
def ends_with_condition?(body)
|
|
65
71
|
return true if simple_if_without_break?(body)
|
|
66
72
|
|
|
67
|
-
body.
|
|
73
|
+
body.begin_type? && simple_if_without_break?(body.children.last)
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
def simple_if_without_break?(node)
|
|
77
|
+
return false unless node.if_type?
|
|
71
78
|
return false if ternary_op?(node)
|
|
72
79
|
return false if if_else?(node)
|
|
73
|
-
return false unless node.type == :if
|
|
74
80
|
return false if style == :skip_modifier_ifs && modifier_if?(node)
|
|
75
81
|
return false if !modifier_if?(node) && !min_body_length?(node)
|
|
76
82
|
|
|
@@ -79,7 +85,36 @@ module RuboCop
|
|
|
79
85
|
_conditional, if_body, _else_body = *node
|
|
80
86
|
return true unless if_body
|
|
81
87
|
|
|
82
|
-
!
|
|
88
|
+
!EXIT_TYPES.include?(if_body.type)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def offense_node(body)
|
|
92
|
+
*_, condition = *body
|
|
93
|
+
(condition && condition.if_type?) ? condition : body
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def offense_location(offense_node)
|
|
97
|
+
condition_expression, = *offense_node
|
|
98
|
+
offense_begin_pos = offense_node.loc.expression.begin
|
|
99
|
+
offense_begin_pos.join(condition_expression.loc.expression)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def autocorrect(node)
|
|
103
|
+
lambda do |corrector|
|
|
104
|
+
cond, if_body, else_body = *node
|
|
105
|
+
if if_body.nil?
|
|
106
|
+
opposite_kw = 'if'
|
|
107
|
+
body = else_body
|
|
108
|
+
else
|
|
109
|
+
opposite_kw = 'unless'
|
|
110
|
+
body = if_body
|
|
111
|
+
end
|
|
112
|
+
next_code = 'next ' << opposite_kw << ' ' <<
|
|
113
|
+
cond.loc.expression.source << "\n"
|
|
114
|
+
corrector.insert_before(node.loc.expression, next_code)
|
|
115
|
+
corrector.replace(node.loc.expression,
|
|
116
|
+
body.loc.expression.source)
|
|
117
|
+
end
|
|
83
118
|
end
|
|
84
119
|
end
|
|
85
120
|
end
|
|
@@ -84,15 +84,19 @@ module RuboCop
|
|
|
84
84
|
end
|
|
85
85
|
|
|
86
86
|
def autocorrect_comparison(node)
|
|
87
|
+
expr = node.loc.expression.source
|
|
88
|
+
|
|
89
|
+
new_code =
|
|
90
|
+
if include_semantic_changes?
|
|
91
|
+
expr.sub(/\s*!=\s*nil/, '')
|
|
92
|
+
else
|
|
93
|
+
expr.sub(/^(\S*)\s*!=\s*nil/, '!\1.nil?')
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
return if expr == new_code
|
|
97
|
+
|
|
87
98
|
lambda do |corrector|
|
|
88
|
-
|
|
89
|
-
new_code =
|
|
90
|
-
if include_semantic_changes?
|
|
91
|
-
expr.source.sub(/\s*!=\s*nil/, '')
|
|
92
|
-
else
|
|
93
|
-
expr.source.sub(/^(\S*)\s*!=\s*nil/, '!\1.nil?')
|
|
94
|
-
end
|
|
95
|
-
corrector.replace(expr, new_code)
|
|
99
|
+
corrector.replace(node.loc.expression, new_code)
|
|
96
100
|
end
|
|
97
101
|
end
|
|
98
102
|
|
|
@@ -8,12 +8,16 @@ module RuboCop
|
|
|
8
8
|
class OneLineConditional < Cop
|
|
9
9
|
include OnNormalIfUnless
|
|
10
10
|
|
|
11
|
-
MSG = 'Favor the ternary operator (
|
|
12
|
-
'over
|
|
11
|
+
MSG = 'Favor the ternary operator (`?:`) ' \
|
|
12
|
+
'over `%s/then/else/end` constructs.'
|
|
13
13
|
|
|
14
14
|
def on_normal_if_unless(node)
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
exp = node.loc.expression.source
|
|
16
|
+
return if exp.include?("\n")
|
|
17
|
+
return unless node.loc.respond_to?(:else) && node.loc.else
|
|
18
|
+
condition = exp.include?('if') ? 'if' : 'unless'
|
|
19
|
+
|
|
20
|
+
add_offense(node, :expression, format(MSG, condition))
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# This cop checks for options hashes and discourages them if the
|
|
7
|
+
# current Ruby version supports keyword arguments.
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# Instead of:
|
|
11
|
+
#
|
|
12
|
+
# def fry(options = {})
|
|
13
|
+
# temperature = options.fetch(:temperature, 300)
|
|
14
|
+
# ...
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# Prefer:
|
|
18
|
+
#
|
|
19
|
+
# def fry(temperature: 300)
|
|
20
|
+
# ...
|
|
21
|
+
# end
|
|
22
|
+
class OptionHash < Cop
|
|
23
|
+
MSG = 'Prefer keyword arguments to options hashes.'
|
|
24
|
+
|
|
25
|
+
def on_args(node)
|
|
26
|
+
return unless supports_keyword_arguments?
|
|
27
|
+
|
|
28
|
+
*_but_last, last_arg = *node
|
|
29
|
+
|
|
30
|
+
# asserting that there was an argument at all
|
|
31
|
+
return unless last_arg
|
|
32
|
+
|
|
33
|
+
# asserting last argument is an optional argument
|
|
34
|
+
return unless last_arg.optarg_type?
|
|
35
|
+
|
|
36
|
+
arg, default_value = *last_arg
|
|
37
|
+
|
|
38
|
+
# asserting default value is a hash
|
|
39
|
+
return unless default_value.hash_type?
|
|
40
|
+
|
|
41
|
+
# asserting default value is empty hash
|
|
42
|
+
*key_value_pairs = *default_value
|
|
43
|
+
return unless key_value_pairs.empty?
|
|
44
|
+
|
|
45
|
+
# Check for suspicious argument names
|
|
46
|
+
return unless name_in_suspicious_param_names?(arg)
|
|
47
|
+
|
|
48
|
+
add_offense(last_arg, :expression, MSG)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def supports_keyword_arguments?
|
|
54
|
+
RUBY_VERSION >= '2.0.0'
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def name_in_suspicious_param_names?(arg_name)
|
|
58
|
+
cop_config.key?('SuspiciousParamNames') &&
|
|
59
|
+
cop_config['SuspiciousParamNames'].include?(arg_name.to_s)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# This cop checks for optional arguments to methods
|
|
7
|
+
# that do not come at the end of the argument list
|
|
8
|
+
#
|
|
9
|
+
# @example
|
|
10
|
+
# # bad
|
|
11
|
+
# def foo(a = 1, b, c)
|
|
12
|
+
# end
|
|
13
|
+
#
|
|
14
|
+
# # good
|
|
15
|
+
# def baz(a, b, c = 1)
|
|
16
|
+
# end
|
|
17
|
+
#
|
|
18
|
+
# def foobar(a = 1, b = 2, c = 3)
|
|
19
|
+
# end
|
|
20
|
+
class OptionalArguments < Cop
|
|
21
|
+
MSG =
|
|
22
|
+
'Optional arguments should appear at the end of the argument list.'
|
|
23
|
+
|
|
24
|
+
def on_def(node)
|
|
25
|
+
_method, arguments, = *node
|
|
26
|
+
arguments = *arguments
|
|
27
|
+
optarg_positions = []
|
|
28
|
+
arg_positions = []
|
|
29
|
+
|
|
30
|
+
arguments.each_with_index do |argument, index|
|
|
31
|
+
optarg_positions << index if argument.optarg_type?
|
|
32
|
+
arg_positions << index if argument.arg_type?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
return if optarg_positions.empty? || arg_positions.empty?
|
|
36
|
+
|
|
37
|
+
optarg_positions.each do |optarg_position|
|
|
38
|
+
# there can only be one group of optional arguments
|
|
39
|
+
break if optarg_position > arg_positions.max
|
|
40
|
+
argument = arguments[optarg_position]
|
|
41
|
+
arg, = *argument
|
|
42
|
+
|
|
43
|
+
add_offense(argument, :expression, format(MSG, arg))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
require 'tsort'
|
|
4
|
+
|
|
5
|
+
module RuboCop
|
|
6
|
+
module Cop
|
|
7
|
+
module Style
|
|
8
|
+
# Checks for simple usages of parallel assignment.
|
|
9
|
+
# This will only complain when the number of variables
|
|
10
|
+
# being assigned matched the number of assigning variables.
|
|
11
|
+
#
|
|
12
|
+
# @example
|
|
13
|
+
# # bad
|
|
14
|
+
# a, b, c = 1, 2, 3
|
|
15
|
+
# a, b, c = [1, 2, 3]
|
|
16
|
+
#
|
|
17
|
+
# # good
|
|
18
|
+
# one, two = *foo
|
|
19
|
+
# a, b = foo()
|
|
20
|
+
# a, b = b, a
|
|
21
|
+
#
|
|
22
|
+
# a = 1
|
|
23
|
+
# b = 2
|
|
24
|
+
# c = 3
|
|
25
|
+
class ParallelAssignment < Cop
|
|
26
|
+
include IfNode
|
|
27
|
+
|
|
28
|
+
MSG = 'Do not use parallel assignment.'
|
|
29
|
+
|
|
30
|
+
def on_masgn(node)
|
|
31
|
+
left, right = *node
|
|
32
|
+
left_elements = *left
|
|
33
|
+
right_elements = [*right].compact # edge case for one constant
|
|
34
|
+
|
|
35
|
+
# only complain when the number of variables matches
|
|
36
|
+
return if left_elements.size != right_elements.size
|
|
37
|
+
|
|
38
|
+
# account for edge cases using one variable with a comma
|
|
39
|
+
return if left_elements.size == 1
|
|
40
|
+
|
|
41
|
+
# account for edge case of Constant::CONSTANT
|
|
42
|
+
return unless right.array_type?
|
|
43
|
+
|
|
44
|
+
# allow mass assignment as the return of a method call
|
|
45
|
+
return if right.block_type? || right.send_type?
|
|
46
|
+
|
|
47
|
+
# allow mass assignment when using splat
|
|
48
|
+
return if (left_elements + right_elements).any?(&:splat_type?)
|
|
49
|
+
|
|
50
|
+
order = find_valid_order(left_elements, right_elements)
|
|
51
|
+
# For `a, b = b, a` or similar, there is no valid order
|
|
52
|
+
return if order.nil?
|
|
53
|
+
|
|
54
|
+
add_offense(node, :expression)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def autocorrect(node)
|
|
58
|
+
lambda do |corrector|
|
|
59
|
+
left, right = *node
|
|
60
|
+
left_elements = *left
|
|
61
|
+
right_elements = [*right].compact
|
|
62
|
+
order = find_valid_order(left_elements, right_elements)
|
|
63
|
+
|
|
64
|
+
assignment_corrector =
|
|
65
|
+
if modifier_statement?(node.parent)
|
|
66
|
+
ModifierCorrector.new(node, config, order)
|
|
67
|
+
elsif rescue_modifier?(node.parent)
|
|
68
|
+
RescueCorrector.new(node, config, order)
|
|
69
|
+
else
|
|
70
|
+
GenericCorrector.new(node, config, order)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
corrector.replace(assignment_corrector.correction_range,
|
|
74
|
+
assignment_corrector.correction)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def find_valid_order(left_elements, right_elements)
|
|
81
|
+
# arrange left_elements in an order such that no corresponding right
|
|
82
|
+
# element refers to a left element earlier in the sequence
|
|
83
|
+
# this can be done using an algorithm called a "topological sort"
|
|
84
|
+
# fortunately for us, Ruby's stdlib contains an implementation
|
|
85
|
+
assignments = left_elements.zip(right_elements)
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
AssignmentSorter.new(assignments).tsort
|
|
89
|
+
rescue TSort::Cyclic
|
|
90
|
+
nil
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Helper class necessitated by silly design of TSort prior to Ruby 2.1
|
|
95
|
+
# Newer versions have a better API, but that doesn't help us
|
|
96
|
+
class AssignmentSorter
|
|
97
|
+
include TSort
|
|
98
|
+
extend RuboCop::NodePattern::Macros
|
|
99
|
+
|
|
100
|
+
def_node_matcher :var_name, '{(casgn _ $_) (_ $_)}'
|
|
101
|
+
def_node_search :uses_var?, '{({lvar ivar cvar gvar} %) (const _ %)}'
|
|
102
|
+
|
|
103
|
+
def initialize(assignments)
|
|
104
|
+
@assignments = assignments
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def tsort_each_node
|
|
108
|
+
@assignments.each { |a| yield a }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def tsort_each_child(assignment)
|
|
112
|
+
# yield all the assignments which must come after `assignment`
|
|
113
|
+
# (due to dependencies on the previous value of the assigned var)
|
|
114
|
+
my_lhs, _my_rhs = *assignment
|
|
115
|
+
|
|
116
|
+
@assignments.each do |other|
|
|
117
|
+
_other_lhs, other_rhs = *other
|
|
118
|
+
yield other if uses_var?(other_rhs, var_name(my_lhs))
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def modifier_statement?(node)
|
|
124
|
+
node &&
|
|
125
|
+
((node.if_type? && modifier_if?(node)) ||
|
|
126
|
+
((node.while_type? || node.until_type?) && modifier_while?(node)))
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def modifier_while?(node)
|
|
130
|
+
node.loc.respond_to?(:keyword) &&
|
|
131
|
+
%w(while until).include?(node.loc.keyword.source) &&
|
|
132
|
+
node.loc.respond_to?(:end) && node.loc.end.nil?
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def rescue_modifier?(node)
|
|
136
|
+
node &&
|
|
137
|
+
node.rescue_type? &&
|
|
138
|
+
(node.parent.nil? || !node.parent.kwbegin_type?)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# An internal class for correcting parallel assignment
|
|
142
|
+
class GenericCorrector
|
|
143
|
+
include AutocorrectAlignment
|
|
144
|
+
|
|
145
|
+
attr_reader :config, :node, :correction, :correction_range
|
|
146
|
+
|
|
147
|
+
def initialize(node, config, new_elements)
|
|
148
|
+
@node = node
|
|
149
|
+
@config = config
|
|
150
|
+
@new_elements = new_elements
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def correction
|
|
154
|
+
"#{assignment.join("\n#{offset(node)}")}"
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
def correction_range
|
|
158
|
+
node.loc.expression
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
protected
|
|
162
|
+
|
|
163
|
+
def assignment
|
|
164
|
+
@new_elements.map do |lhs, rhs|
|
|
165
|
+
"#{lhs.loc.expression.source} = #{rhs.loc.expression.source}"
|
|
166
|
+
end
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
|
|
171
|
+
def extract_sources(node)
|
|
172
|
+
node.children.map { |child| child.loc.expression.source }
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# An internal class for correcting parallel assignment
|
|
177
|
+
# protected by rescue
|
|
178
|
+
class RescueCorrector < GenericCorrector
|
|
179
|
+
def correction
|
|
180
|
+
_node, rescue_clause = *node.parent
|
|
181
|
+
_, _, rescue_result = *rescue_clause
|
|
182
|
+
|
|
183
|
+
"begin\n" <<
|
|
184
|
+
indentation(node) << assignment.join("\n#{indentation(node)}") <<
|
|
185
|
+
"\n#{offset(node)}rescue\n" <<
|
|
186
|
+
indentation(node) << rescue_result.loc.expression.source <<
|
|
187
|
+
"\n#{offset(node)}end"
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
def correction_range
|
|
191
|
+
node.parent.loc.expression
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# An internal class for correcting parallel assignment
|
|
196
|
+
# guarded by if, unless, while, or until
|
|
197
|
+
class ModifierCorrector < GenericCorrector
|
|
198
|
+
def correction
|
|
199
|
+
parent = node.parent
|
|
200
|
+
|
|
201
|
+
modifier_range =
|
|
202
|
+
Parser::Source::Range.new(parent.loc.expression.source_buffer,
|
|
203
|
+
parent.loc.keyword.begin_pos,
|
|
204
|
+
parent.loc.expression.end_pos)
|
|
205
|
+
|
|
206
|
+
"#{modifier_range.source}\n" <<
|
|
207
|
+
indentation(node) << assignment.join("\n#{indentation(node)}") <<
|
|
208
|
+
"\n#{offset(node)}end"
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
def correction_range
|
|
212
|
+
node.parent.loc.expression
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
@@ -33,7 +33,7 @@ module RuboCop
|
|
|
33
33
|
delimiters = preferred_delimiters(type)
|
|
34
34
|
|
|
35
35
|
"`#{type}`-literals should be delimited by " \
|
|
36
|
-
"`#{delimiters[0]}` and `#{delimiters[1]}
|
|
36
|
+
"`#{delimiters[0]}` and `#{delimiters[1]}`."
|
|
37
37
|
end
|
|
38
38
|
|
|
39
39
|
private
|
|
@@ -43,20 +43,9 @@ module RuboCop
|
|
|
43
43
|
|
|
44
44
|
opening_delimiter, closing_delimiter = preferred_delimiters(type)
|
|
45
45
|
|
|
46
|
-
first_child, *_middle, last_child = *node
|
|
47
|
-
opening_newline = new_line(node.loc.begin, first_child)
|
|
48
|
-
expression_indentation = leading_whitespace(first_child, :expression)
|
|
49
|
-
closing_newline = new_line(node.loc.end, last_child)
|
|
50
|
-
closing_indentation = leading_whitespace(node, :end)
|
|
51
|
-
expression, reg_opt = *contents(node)
|
|
52
|
-
|
|
53
|
-
corrected_source =
|
|
54
|
-
type + opening_delimiter + opening_newline +
|
|
55
|
-
expression_indentation + expression + closing_newline +
|
|
56
|
-
closing_indentation + closing_delimiter + reg_opt
|
|
57
|
-
|
|
58
46
|
lambda do |corrector|
|
|
59
|
-
corrector.replace(node.loc.
|
|
47
|
+
corrector.replace(node.loc.begin, "#{type}#{opening_delimiter}")
|
|
48
|
+
corrector.replace(node.loc.end, closing_delimiter)
|
|
60
49
|
end
|
|
61
50
|
end
|
|
62
51
|
|
|
@@ -72,49 +61,6 @@ module RuboCop
|
|
|
72
61
|
cop_config['PreferredDelimiters'][type].split(//)
|
|
73
62
|
end
|
|
74
63
|
|
|
75
|
-
def leading_whitespace(object, part)
|
|
76
|
-
case object
|
|
77
|
-
when String
|
|
78
|
-
''
|
|
79
|
-
when NilClass
|
|
80
|
-
''
|
|
81
|
-
when Parser::AST::Node
|
|
82
|
-
part_range = object.loc.send(part)
|
|
83
|
-
left_of_part = part_range.source_line[0...part_range.column]
|
|
84
|
-
/^(\s*)$/.match(left_of_part) ? left_of_part : ''
|
|
85
|
-
else
|
|
86
|
-
fail "Unsupported object #{object}"
|
|
87
|
-
end
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def contents(node)
|
|
91
|
-
first_child, *middle, last_child = *node
|
|
92
|
-
last_child ||= first_child
|
|
93
|
-
if node.type == :regexp
|
|
94
|
-
*_, next_to_last_child = *middle
|
|
95
|
-
next_to_last_child ||= first_child
|
|
96
|
-
expression = source(node, first_child, next_to_last_child)
|
|
97
|
-
reg_opt = last_child.loc.expression.source
|
|
98
|
-
else
|
|
99
|
-
expression = if first_child.is_a?(Parser::AST::Node)
|
|
100
|
-
source(node, first_child, last_child)
|
|
101
|
-
else
|
|
102
|
-
first_child.to_s
|
|
103
|
-
end
|
|
104
|
-
reg_opt = ''
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
[expression, reg_opt]
|
|
108
|
-
end
|
|
109
|
-
|
|
110
|
-
def source(node, begin_node, end_node)
|
|
111
|
-
Parser::Source::Range.new(
|
|
112
|
-
node.loc.expression.source_buffer,
|
|
113
|
-
begin_node.loc.expression.begin_pos,
|
|
114
|
-
end_node.loc.expression.end_pos
|
|
115
|
-
).source
|
|
116
|
-
end
|
|
117
|
-
|
|
118
64
|
def uses_preferred_delimiter?(node, type)
|
|
119
65
|
preferred_delimiters(type)[0] == begin_source(node)[-1]
|
|
120
66
|
end
|
|
@@ -133,15 +79,6 @@ module RuboCop
|
|
|
133
79
|
node.loc.expression.source
|
|
134
80
|
end
|
|
135
81
|
end
|
|
136
|
-
|
|
137
|
-
def new_line(range, child_node)
|
|
138
|
-
same_line?(range, child_node) ? '' : "\n"
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
def same_line?(range, child_node)
|
|
142
|
-
!child_node.is_a?(Parser::AST::Node) ||
|
|
143
|
-
range.begin.line == child_node.loc.line
|
|
144
|
-
end
|
|
145
82
|
end
|
|
146
83
|
end
|
|
147
84
|
end
|
|
@@ -23,10 +23,11 @@ module RuboCop
|
|
|
23
23
|
private
|
|
24
24
|
|
|
25
25
|
def on_method_def(node, method_name, _args, _body)
|
|
26
|
-
|
|
26
|
+
predicate_prefixes.each do |prefix|
|
|
27
27
|
method_name = method_name.to_s
|
|
28
28
|
next unless method_name.start_with?(prefix)
|
|
29
29
|
next if method_name == expected_name(method_name, prefix)
|
|
30
|
+
next if predicate_whitelist.include?(method_name)
|
|
30
31
|
add_offense(
|
|
31
32
|
node,
|
|
32
33
|
:name,
|
|
@@ -53,9 +54,13 @@ module RuboCop
|
|
|
53
54
|
cop_config['NamePrefixBlacklist']
|
|
54
55
|
end
|
|
55
56
|
|
|
56
|
-
def
|
|
57
|
+
def predicate_prefixes
|
|
57
58
|
cop_config['NamePrefix']
|
|
58
59
|
end
|
|
60
|
+
|
|
61
|
+
def predicate_whitelist
|
|
62
|
+
cop_config['NameWhitelist']
|
|
63
|
+
end
|
|
59
64
|
end
|
|
60
65
|
end
|
|
61
66
|
end
|
|
@@ -37,19 +37,8 @@ module RuboCop
|
|
|
37
37
|
|
|
38
38
|
def autocorrect(node)
|
|
39
39
|
lambda do |corrector|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
begin_indent = node.loc.column
|
|
43
|
-
child_indent = child.loc.column
|
|
44
|
-
|
|
45
|
-
indent_diff = child_indent - begin_indent
|
|
46
|
-
|
|
47
|
-
corrector.replace(
|
|
48
|
-
range_with_surrounding_space(node.loc.expression),
|
|
49
|
-
range_with_surrounding_space(
|
|
50
|
-
child.loc.expression
|
|
51
|
-
).source.gsub(/^[ \t]{#{indent_diff}}/, '')
|
|
52
|
-
)
|
|
40
|
+
corrector.remove(node.loc.begin)
|
|
41
|
+
corrector.remove(node.loc.end)
|
|
53
42
|
end
|
|
54
43
|
end
|
|
55
44
|
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# encoding: utf-8
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# This cop check for uses of Object#freeze on immutable objects.
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# # bad
|
|
10
|
+
# CONST = 1.freeze
|
|
11
|
+
#
|
|
12
|
+
# # good
|
|
13
|
+
# CONST = 1
|
|
14
|
+
class RedundantFreeze < Cop
|
|
15
|
+
MSG = 'Freezing immutable objects is pointless.'.freeze
|
|
16
|
+
|
|
17
|
+
TARGET_NODES = [:int, :float, :sym].freeze
|
|
18
|
+
|
|
19
|
+
def on_send(node)
|
|
20
|
+
receiver, method_name, *args = *node
|
|
21
|
+
|
|
22
|
+
return unless receiver && TARGET_NODES.include?(receiver.type)
|
|
23
|
+
return unless method_name == :freeze && args.empty?
|
|
24
|
+
|
|
25
|
+
add_offense(node, :expression)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def autocorrect(node)
|
|
29
|
+
lambda do |corrector|
|
|
30
|
+
corrector.remove(node.loc.dot)
|
|
31
|
+
corrector.remove(node.loc.selector)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|