rubocop 0.37.2 → 0.38.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +2 -1
- data/assets/output.html.erb +3 -0
- data/config/default.yml +14 -1
- data/config/disabled.yml +0 -4
- data/lib/rubocop.rb +0 -1
- data/lib/rubocop/ast_node.rb +6 -0
- data/lib/rubocop/ast_node/traversal.rb +1 -1
- data/lib/rubocop/cached_data.rb +6 -2
- data/lib/rubocop/config_loader.rb +7 -3
- data/lib/rubocop/cop/cop.rb +1 -1
- data/lib/rubocop/cop/lint/block_alignment.rb +91 -17
- data/lib/rubocop/cop/lint/else_layout.rb +24 -14
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +6 -12
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +16 -10
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/unneeded_disable.rb +36 -23
- data/lib/rubocop/cop/lint/unused_block_argument.rb +0 -1
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +4 -4
- data/lib/rubocop/cop/metrics/line_length.rb +25 -18
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/code_length.rb +1 -1
- data/lib/rubocop/cop/mixin/if_node.rb +0 -4
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +5 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +6 -6
- data/lib/rubocop/cop/mixin/trailing_comma.rb +26 -24
- data/lib/rubocop/cop/mixin/unused_argument.rb +9 -1
- data/lib/rubocop/cop/offense.rb +16 -0
- data/lib/rubocop/cop/performance/casecmp.rb +15 -10
- data/lib/rubocop/cop/performance/count.rb +3 -2
- data/lib/rubocop/cop/performance/fixed_size.rb +13 -12
- data/lib/rubocop/cop/performance/reverse_each.rb +15 -14
- data/lib/rubocop/cop/rails/date.rb +13 -1
- data/lib/rubocop/cop/rails/output.rb +2 -1
- data/lib/rubocop/cop/rails/pluralization_grammar.rb +1 -1
- data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
- data/lib/rubocop/cop/style/and_or.rb +1 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +41 -48
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +4 -2
- data/lib/rubocop/cop/style/class_and_module_children.rb +3 -3
- data/lib/rubocop/cop/style/comment_annotation.rb +8 -10
- data/lib/rubocop/cop/style/conditional_assignment.rb +7 -2
- data/lib/rubocop/cop/style/encoding.rb +15 -10
- data/lib/rubocop/cop/style/extra_spacing.rb +14 -12
- data/lib/rubocop/cop/style/file_name.rb +3 -7
- data/lib/rubocop/cop/style/lambda.rb +17 -6
- data/lib/rubocop/cop/style/method_call_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_def_parentheses.rb +0 -4
- data/lib/rubocop/cop/style/nested_modifier.rb +7 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +0 -4
- data/lib/rubocop/cop/style/not.rb +27 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +21 -0
- data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -1
- data/lib/rubocop/cop/style/signal_exception.rb +28 -26
- data/lib/rubocop/cop/style/space_around_keyword.rb +10 -6
- data/lib/rubocop/cop/style/string_literals.rb +1 -0
- data/lib/rubocop/cop/style/unless_else.rb +24 -0
- data/lib/rubocop/cop/style/unneeded_interpolation.rb +26 -13
- data/lib/rubocop/cop/style/zero_length_predicate.rb +5 -3
- data/lib/rubocop/cop/team.rb +6 -1
- data/lib/rubocop/cop/util.rb +13 -6
- data/lib/rubocop/formatter/clang_style_formatter.rb +18 -14
- data/lib/rubocop/formatter/html_formatter.rb +9 -10
- data/lib/rubocop/rake_task.rb +4 -4
- data/lib/rubocop/remote_config.rb +4 -2
- data/lib/rubocop/result_cache.rb +14 -8
- data/lib/rubocop/runner.rb +9 -0
- data/lib/rubocop/version.rb +1 -1
- data/spec/support/cop_helper.rb +81 -0
- metadata +13 -7
- data/lib/rubocop/cop/mixin/autocorrect_unless_changing_ast.rb +0 -57
@@ -15,7 +15,7 @@ module RuboCop
|
|
15
15
|
'called `%s`.'.freeze
|
16
16
|
MSG_REGEX = '`%s` should match `%s`.'.freeze
|
17
17
|
|
18
|
-
SNAKE_CASE = /^[\da-z_]+$/
|
18
|
+
SNAKE_CASE = /^[\da-z_.?!]+$/
|
19
19
|
|
20
20
|
def investigate(processed_source)
|
21
21
|
file_path = processed_source.buffer.name
|
@@ -44,12 +44,8 @@ module RuboCop
|
|
44
44
|
|
45
45
|
private
|
46
46
|
|
47
|
-
def snake_case?(basename)
|
48
|
-
basename.split('.').all? { |fragment| fragment =~ SNAKE_CASE }
|
49
|
-
end
|
50
|
-
|
51
47
|
def shebang?(line)
|
52
|
-
line.start_with?('#!')
|
48
|
+
line && line.start_with?('#!')
|
53
49
|
end
|
54
50
|
|
55
51
|
def expect_matching_definition?
|
@@ -62,7 +58,7 @@ module RuboCop
|
|
62
58
|
|
63
59
|
def filename_good?(basename)
|
64
60
|
basename = basename.sub(/\.[^\.]+$/, '')
|
65
|
-
|
61
|
+
basename =~ (regex || SNAKE_CASE)
|
66
62
|
end
|
67
63
|
|
68
64
|
def find_class_or_module(node, namespace)
|
@@ -8,8 +8,6 @@ module RuboCop
|
|
8
8
|
# anonymous functions and uses of the 1.9 lambda syntax for multi-line
|
9
9
|
# anonymous functions.
|
10
10
|
class Lambda < Cop
|
11
|
-
include AutocorrectUnlessChangingAST
|
12
|
-
|
13
11
|
SINGLE_MSG = 'Use the new lambda literal syntax ' \
|
14
12
|
'`->(params) {...}`.'.freeze
|
15
13
|
SINGLE_NO_ARG_MSG = 'Use the new lambda literal syntax ' \
|
@@ -26,7 +24,7 @@ module RuboCop
|
|
26
24
|
block_method, args, = *node
|
27
25
|
|
28
26
|
return unless block_method == TARGET
|
29
|
-
selector = block_method.
|
27
|
+
selector = block_method.source
|
30
28
|
length = lambda_length(node)
|
31
29
|
|
32
30
|
if selector != '->' && length == 1
|
@@ -53,10 +51,14 @@ module RuboCop
|
|
53
51
|
end_line - start_line + 1
|
54
52
|
end
|
55
53
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
54
|
+
def autocorrect(node)
|
55
|
+
block_method, _args = *node
|
56
|
+
selector = block_method.source
|
59
57
|
|
58
|
+
# Don't autocorrect if this would change the meaning of the code
|
59
|
+
return if selector == '->' && arg_to_unparenthesized_call?(node)
|
60
|
+
|
61
|
+
lambda do |corrector|
|
60
62
|
if block_method.source == 'lambda'
|
61
63
|
autocorrect_old_to_new(corrector, node)
|
62
64
|
else
|
@@ -102,6 +104,15 @@ module RuboCop
|
|
102
104
|
def lambda_arg_string(args)
|
103
105
|
args.children.map(&:source).join(', ')
|
104
106
|
end
|
107
|
+
|
108
|
+
def arg_to_unparenthesized_call?(node)
|
109
|
+
parent = node.parent
|
110
|
+
return false unless parent && parent.send_type?
|
111
|
+
return false if parenthesized_call?(parent)
|
112
|
+
|
113
|
+
index = parent.children.index { |c| c.equal?(node) }
|
114
|
+
index >= 2
|
115
|
+
end
|
105
116
|
end
|
106
117
|
end
|
107
118
|
end
|
@@ -9,7 +9,7 @@ module RuboCop
|
|
9
9
|
MSG = 'Do not use parentheses for method calls with ' \
|
10
10
|
'no arguments.'.freeze
|
11
11
|
|
12
|
-
ASGN_NODES = [:lvasgn, :masgn] +
|
12
|
+
ASGN_NODES = [:lvasgn, :masgn] + SHORTHAND_ASGN_NODES
|
13
13
|
|
14
14
|
def on_send(node)
|
15
15
|
_receiver, method_name, *args = *node
|
@@ -89,12 +89,18 @@ module RuboCop
|
|
89
89
|
outer_expr = "(#{outer_expr})" if outer_cond.or_type? &&
|
90
90
|
operator == '&&'.freeze
|
91
91
|
inner_expr = inner_cond.source
|
92
|
-
|
92
|
+
|
93
|
+
inner_expr = "(#{inner_expr})" if requires_parens?(inner_cond)
|
93
94
|
inner_expr = "!#{inner_expr}" unless outer_keyword == inner_keyword
|
94
95
|
|
95
96
|
"#{outer_node.loc.keyword.source} " \
|
96
97
|
"#{outer_expr} #{operator} #{inner_expr}"
|
97
98
|
end
|
99
|
+
|
100
|
+
def requires_parens?(node)
|
101
|
+
node.or_type? ||
|
102
|
+
!(RuboCop::Node::COMPARISON_OPERATORS & node.children).empty?
|
103
|
+
end
|
98
104
|
end
|
99
105
|
end
|
100
106
|
end
|
@@ -6,10 +6,16 @@ module RuboCop
|
|
6
6
|
module Style
|
7
7
|
# This cop checks for uses if the keyword *not* instead of !.
|
8
8
|
class Not < Cop
|
9
|
-
include AutocorrectUnlessChangingAST
|
10
|
-
|
11
9
|
MSG = 'Use `!` instead of `not`.'.freeze
|
12
10
|
|
11
|
+
OPPOSITE_METHODS = {
|
12
|
+
:== => :!=,
|
13
|
+
:!= => :==,
|
14
|
+
:<= => :>,
|
15
|
+
:> => :<=,
|
16
|
+
:< => :>=,
|
17
|
+
:>= => :< }.freeze
|
18
|
+
|
13
19
|
def on_send(node)
|
14
20
|
return unless node.keyword_not?
|
15
21
|
|
@@ -18,9 +24,25 @@ module RuboCop
|
|
18
24
|
|
19
25
|
private
|
20
26
|
|
21
|
-
def
|
22
|
-
|
23
|
-
|
27
|
+
def autocorrect(node)
|
28
|
+
range = range_with_surrounding_space(node.loc.selector, :right)
|
29
|
+
child = node.children.first
|
30
|
+
|
31
|
+
if child.send_type? && OPPOSITE_METHODS.key?(child.method_name)
|
32
|
+
lambda do |corrector|
|
33
|
+
corrector.remove(range)
|
34
|
+
corrector.replace(child.loc.selector,
|
35
|
+
OPPOSITE_METHODS[child.method_name].to_s)
|
36
|
+
end
|
37
|
+
elsif child.and_type? || child.or_type? || child.binary_operation? ||
|
38
|
+
ternary_op?(child)
|
39
|
+
lambda do |corrector|
|
40
|
+
corrector.replace(range, '!(')
|
41
|
+
corrector.insert_after(node.source_range, ')')
|
42
|
+
end
|
43
|
+
else
|
44
|
+
->(corrector) { corrector.replace(range, '!') }
|
45
|
+
end
|
24
46
|
end
|
25
47
|
end
|
26
48
|
end
|
@@ -20,6 +20,27 @@ module RuboCop
|
|
20
20
|
|
21
21
|
add_offense(node, :expression, format(MSG, condition))
|
22
22
|
end
|
23
|
+
|
24
|
+
def autocorrect(node)
|
25
|
+
lambda do |corrector|
|
26
|
+
corrector.replace(node.source_range, replacement(node))
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def replacement(node)
|
31
|
+
cond, body, else_clause = *node
|
32
|
+
ternary = "#{cond.source} ? #{body.source} : #{else_clause.source}"
|
33
|
+
|
34
|
+
return ternary unless node.parent
|
35
|
+
return "(#{ternary})" if [:and, :or].include?(node.parent.type)
|
36
|
+
|
37
|
+
if node.parent.send_type?
|
38
|
+
_receiver, method_name, = *node.parent
|
39
|
+
return "(#{ternary})" if operator?(method_name)
|
40
|
+
end
|
41
|
+
|
42
|
+
ternary
|
43
|
+
end
|
23
44
|
end
|
24
45
|
end
|
25
46
|
end
|
@@ -58,7 +58,15 @@ module RuboCop
|
|
58
58
|
|
59
59
|
def check_send(begin_node, node)
|
60
60
|
if node.unary_operation?
|
61
|
-
|
61
|
+
return if begin_node.chained?
|
62
|
+
|
63
|
+
# parens are not redundant in `(!recv.method arg)`
|
64
|
+
node = node.children.first while node.unary_operation?
|
65
|
+
if node.send_type?
|
66
|
+
return unless method_call_with_redundant_parentheses?(node)
|
67
|
+
end
|
68
|
+
|
69
|
+
offense(begin_node, 'an unary operation')
|
62
70
|
else
|
63
71
|
return unless method_call_with_redundant_parentheses?(node)
|
64
72
|
offense(begin_node, 'a method call')
|
@@ -12,14 +12,23 @@ module RuboCop
|
|
12
12
|
RAISE_MSG = 'Use `raise` instead of `fail` to ' \
|
13
13
|
'rethrow exceptions.'.freeze
|
14
14
|
|
15
|
+
def_node_matcher :kernel_call?, '(send (const nil :Kernel) %1 ...)'
|
16
|
+
def_node_search :custom_fail_methods,
|
17
|
+
'{(def :fail ...) (defs _ :fail ...)}'
|
18
|
+
|
19
|
+
def investigate(processed_source)
|
20
|
+
ast = processed_source.ast
|
21
|
+
@custom_fail_defined = ast && custom_fail_methods(ast).any?
|
22
|
+
end
|
23
|
+
|
15
24
|
def on_rescue(node)
|
16
25
|
return unless style == :semantic
|
17
26
|
|
18
27
|
begin_node, *rescue_nodes, _else_node = *node
|
19
|
-
|
28
|
+
check_scope(:raise, begin_node)
|
20
29
|
|
21
30
|
rescue_nodes.each do |rescue_node|
|
22
|
-
|
31
|
+
check_scope(:fail, rescue_node)
|
23
32
|
allow(:raise, rescue_node)
|
24
33
|
end
|
25
34
|
end
|
@@ -27,11 +36,12 @@ module RuboCop
|
|
27
36
|
def on_send(node)
|
28
37
|
case style
|
29
38
|
when :semantic
|
30
|
-
|
39
|
+
check_send(:raise, node) unless ignored_node?(node)
|
31
40
|
when :only_raise
|
32
|
-
|
41
|
+
return if @custom_fail_defined
|
42
|
+
check_send(:fail, node)
|
33
43
|
when :only_fail
|
34
|
-
|
44
|
+
check_send(:raise, node)
|
35
45
|
end
|
36
46
|
end
|
37
47
|
|
@@ -62,35 +72,27 @@ module RuboCop
|
|
62
72
|
end
|
63
73
|
end
|
64
74
|
|
65
|
-
def
|
75
|
+
def check_scope(method_name, node)
|
66
76
|
return unless node
|
67
77
|
|
68
|
-
|
69
|
-
|
70
|
-
next if ignored_node?(send_node)
|
78
|
+
each_command_or_kernel_call(method_name, node) do |send_node|
|
79
|
+
next if ignored_node?(send_node)
|
71
80
|
|
72
|
-
|
73
|
-
|
74
|
-
end
|
75
|
-
elsif command_or_kernel_call?(method_name == :raise ? :fail : :raise,
|
76
|
-
node)
|
77
|
-
add_offense(node, :selector, message(method_name))
|
81
|
+
add_offense(send_node, :selector, message(method_name))
|
82
|
+
ignore_node(send_node)
|
78
83
|
end
|
79
84
|
end
|
80
85
|
|
81
|
-
def
|
82
|
-
|
83
|
-
end
|
84
|
-
|
85
|
-
def kernel_call?(name, node)
|
86
|
-
return false unless node.type == :send
|
87
|
-
receiver, selector, _args = *node
|
86
|
+
def check_send(method_name, node)
|
87
|
+
return unless node
|
88
88
|
|
89
|
-
|
90
|
-
|
89
|
+
if command_or_kernel_call?(method_name, node)
|
90
|
+
add_offense(node, :selector, message(method_name))
|
91
|
+
end
|
92
|
+
end
|
91
93
|
|
92
|
-
|
93
|
-
|
94
|
+
def command_or_kernel_call?(name, node)
|
95
|
+
node.command?(name) || kernel_call?(node, name)
|
94
96
|
end
|
95
97
|
|
96
98
|
def allow(method_name, node)
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
31
31
|
|
32
32
|
DO = 'do'.freeze
|
33
33
|
ACCEPT_LEFT_PAREN =
|
34
|
-
%w(break next not return super yield
|
34
|
+
%w(break defined? next not rescue return super yield).freeze
|
35
35
|
|
36
36
|
def on_and(node)
|
37
37
|
check(node, [:operator].freeze) if node.keyword?
|
@@ -187,11 +187,15 @@ module RuboCop
|
|
187
187
|
end
|
188
188
|
|
189
189
|
def preceded_by_operator?(node, _range)
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
190
|
+
# regular dotted method calls bind more tightly than operators
|
191
|
+
# so we need to climb up the AST past them
|
192
|
+
while (ancestor = node.parent)
|
193
|
+
return true if ancestor.and_type? || ancestor.or_type?
|
194
|
+
return false unless ancestor.send_type?
|
195
|
+
return true if operator?(ancestor.method_name)
|
196
|
+
node = ancestor
|
197
|
+
end
|
198
|
+
false
|
195
199
|
end
|
196
200
|
|
197
201
|
def autocorrect(range)
|
@@ -17,6 +17,7 @@ module RuboCop
|
|
17
17
|
# If one part of that continued string contains interpolations,
|
18
18
|
# then it will be parsed as a nested `dstr` node
|
19
19
|
return unless consistent_multiline?
|
20
|
+
return if node.loc.is_a?(Parser::Source::Map::Heredoc)
|
20
21
|
|
21
22
|
children = node.children
|
22
23
|
return unless children.all? { |c| c.str_type? || c.dstr_type? }
|
@@ -18,6 +18,30 @@ module RuboCop
|
|
18
18
|
|
19
19
|
add_offense(node, :expression)
|
20
20
|
end
|
21
|
+
|
22
|
+
def autocorrect(node)
|
23
|
+
condition, = *node
|
24
|
+
body_range = range_between_condition_and_else(node, condition)
|
25
|
+
else_range = range_between_else_and_end(node)
|
26
|
+
|
27
|
+
lambda do |corrector|
|
28
|
+
corrector.replace(node.loc.keyword, 'if'.freeze)
|
29
|
+
corrector.replace(body_range, else_range.source)
|
30
|
+
corrector.replace(else_range, body_range.source)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def range_between_condition_and_else(node, condition)
|
35
|
+
Parser::Source::Range.new(node.source_range.source_buffer,
|
36
|
+
condition.source_range.end_pos,
|
37
|
+
node.loc.else.begin_pos)
|
38
|
+
end
|
39
|
+
|
40
|
+
def range_between_else_and_end(node)
|
41
|
+
Parser::Source::Range.new(node.source_range.source_buffer,
|
42
|
+
node.loc.else.end_pos,
|
43
|
+
node.loc.end.begin_pos)
|
44
|
+
end
|
21
45
|
end
|
22
46
|
end
|
23
47
|
end
|
@@ -61,24 +61,37 @@ module RuboCop
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def autocorrect(node)
|
64
|
-
loc = node.loc
|
65
64
|
embedded_node = node.children.first
|
66
|
-
embedded_loc = embedded_node.loc
|
67
65
|
|
68
66
|
if variable_interpolation?(embedded_node)
|
69
|
-
|
70
|
-
->(corrector) { corrector.replace(loc.expression, replacement) }
|
67
|
+
autocorrect_variable_interpolation(embedded_node, node)
|
71
68
|
elsif single_variable_interpolation?(embedded_node)
|
72
|
-
|
73
|
-
replacement = "#{variable_loc.expression.source}.to_s"
|
74
|
-
->(corrector) { corrector.replace(loc.expression, replacement) }
|
69
|
+
autocorrect_single_variable_interpolation(embedded_node, node)
|
75
70
|
else
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
71
|
+
autocorrect_other(embedded_node, node)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def autocorrect_variable_interpolation(embedded_node, node)
|
76
|
+
replacement = "#{embedded_node.loc.expression.source}.to_s"
|
77
|
+
->(corrector) { corrector.replace(node.loc.expression, replacement) }
|
78
|
+
end
|
79
|
+
|
80
|
+
def autocorrect_single_variable_interpolation(embedded_node, node)
|
81
|
+
variable_loc = embedded_node.children.first.loc
|
82
|
+
replacement = "#{variable_loc.expression.source}.to_s"
|
83
|
+
->(corrector) { corrector.replace(node.loc.expression, replacement) }
|
84
|
+
end
|
85
|
+
|
86
|
+
def autocorrect_other(embedded_node, node)
|
87
|
+
loc = node.loc
|
88
|
+
embedded_loc = embedded_node.loc
|
89
|
+
|
90
|
+
lambda do |corrector|
|
91
|
+
corrector.replace(loc.begin, '')
|
92
|
+
corrector.replace(loc.end, '')
|
93
|
+
corrector.replace(embedded_loc.begin, '(')
|
94
|
+
corrector.replace(embedded_loc.end, ').to_s')
|
82
95
|
end
|
83
96
|
end
|
84
97
|
end
|
@@ -21,7 +21,7 @@ module RuboCop
|
|
21
21
|
# "foobar".empty?
|
22
22
|
# !hash.empty?
|
23
23
|
class ZeroLengthPredicate < Cop
|
24
|
-
ZERO_MSG = 'Use `empty?` instead of `%s
|
24
|
+
ZERO_MSG = 'Use `empty?` instead of `%s %s %s`.'.freeze
|
25
25
|
NONZERO_MSG = 'Use `!empty?` instead of `%s %s %s`.'.freeze
|
26
26
|
|
27
27
|
def on_send(node)
|
@@ -41,8 +41,10 @@ module RuboCop
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def_node_matcher :zero_length_predicate, <<-END
|
44
|
-
{(send (send _ ${:length :size})
|
45
|
-
(send (int 0)
|
44
|
+
{(send (send _ ${:length :size}) $:== (int $0))
|
45
|
+
(send (int $0) $:== (send _ ${:length :size}))
|
46
|
+
(send (send _ ${:length :size}) $:< (int $1))
|
47
|
+
(send (int $1) $:> (send _ ${:length :size}))}
|
46
48
|
END
|
47
49
|
|
48
50
|
def_node_matcher :nonzero_length_predicate, <<-END
|