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
@@ -20,30 +20,10 @@ module RuboCop
|
|
20
20
|
|
21
21
|
def check(offenses, cop_disabled_line_ranges, comments)
|
22
22
|
unneeded_cops = Hash.new { |h, k| h[k] = Set.new }
|
23
|
-
disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
|
24
23
|
|
25
|
-
cop_disabled_line_ranges
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
# If a cop is disabled in a range that begins on the same line as
|
30
|
-
# the end of the previous range, it means that the cop was
|
31
|
-
# already disabled by an earlier comment. So it's unneeded
|
32
|
-
# whether there are offenses or not.
|
33
|
-
comment = comments.find { |c| c.loc.line == range.begin }
|
34
|
-
unneeded_cops[comment].add(cop)
|
35
|
-
end
|
36
|
-
|
37
|
-
line_ranges.each do |line_range|
|
38
|
-
comment = comments.find { |c| c.loc.line == line_range.begin }
|
39
|
-
|
40
|
-
unless all_disabled?(comment)
|
41
|
-
next if ignore_offense?(disabled_ranges, line_range)
|
42
|
-
end
|
43
|
-
|
44
|
-
unneeded_cop = find_unneeded(comment, offenses, cop, line_range)
|
45
|
-
unneeded_cops[comment].add(unneeded_cop) if unneeded_cop
|
46
|
-
end
|
24
|
+
each_unneeded_disable(cop_disabled_line_ranges,
|
25
|
+
offenses, comments) do |comment, unneeded_cop|
|
26
|
+
unneeded_cops[comment].add(unneeded_cop)
|
47
27
|
end
|
48
28
|
|
49
29
|
add_offenses(unneeded_cops)
|
@@ -77,6 +57,39 @@ module RuboCop
|
|
77
57
|
|
78
58
|
private
|
79
59
|
|
60
|
+
def each_unneeded_disable(cop_disabled_line_ranges, offenses, comments)
|
61
|
+
disabled_ranges = cop_disabled_line_ranges[COP_NAME] || [0..0]
|
62
|
+
|
63
|
+
cop_disabled_line_ranges.each do |cop, line_ranges|
|
64
|
+
each_already_disabled(line_ranges, comments) do |comment|
|
65
|
+
yield comment, cop
|
66
|
+
end
|
67
|
+
|
68
|
+
line_ranges.each do |line_range|
|
69
|
+
comment = comments.find { |c| c.loc.line == line_range.begin }
|
70
|
+
|
71
|
+
unless all_disabled?(comment)
|
72
|
+
next if ignore_offense?(disabled_ranges, line_range)
|
73
|
+
end
|
74
|
+
|
75
|
+
unneeded_cop = find_unneeded(comment, offenses, cop, line_range)
|
76
|
+
yield comment, unneeded_cop if unneeded_cop
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def each_already_disabled(line_ranges, comments)
|
82
|
+
line_ranges.each_cons(2) do |previous_range, range|
|
83
|
+
next if previous_range.end != range.begin
|
84
|
+
|
85
|
+
# If a cop is disabled in a range that begins on the same line as
|
86
|
+
# the end of the previous range, it means that the cop was
|
87
|
+
# already disabled by an earlier comment. So it's unneeded
|
88
|
+
# whether there are offenses or not.
|
89
|
+
yield comments.find { |c| c.loc.line == range.begin }
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
80
93
|
def find_unneeded(comment, offenses, cop, line_range)
|
81
94
|
if all_disabled?(comment)
|
82
95
|
'all' if offenses.none? { |o| line_range.cover?(o.line) }
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
55
55
|
def check_scope(node, cur_vis = :public)
|
56
56
|
unused = nil
|
57
57
|
|
58
|
-
node.
|
58
|
+
node.child_nodes.each do |child|
|
59
59
|
if (new_vis = access_modifier(child))
|
60
60
|
# does this modifier just repeat the existing visibility?
|
61
61
|
if new_vis == cur_vis
|
@@ -70,14 +70,14 @@ module RuboCop
|
|
70
70
|
cur_vis = new_vis
|
71
71
|
elsif method_definition?(child)
|
72
72
|
unused = nil
|
73
|
-
elsif child.kwbegin_type?
|
74
|
-
cur_vis = check_scope(child, cur_vis)
|
73
|
+
elsif child.kwbegin_type? || child.send_type?
|
74
|
+
cur_vis, unused = check_scope(child, cur_vis)
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
78
78
|
add_offense(unused, :expression, format(MSG, cur_vis)) if unused
|
79
79
|
|
80
|
-
cur_vis
|
80
|
+
[cur_vis, unused]
|
81
81
|
end
|
82
82
|
end
|
83
83
|
end
|
@@ -16,30 +16,37 @@ module RuboCop
|
|
16
16
|
def investigate(processed_source)
|
17
17
|
heredocs = extract_heredocs(processed_source.ast) if allow_heredoc?
|
18
18
|
processed_source.lines.each_with_index do |line, index|
|
19
|
-
|
19
|
+
check_line(line, index, heredocs)
|
20
|
+
end
|
21
|
+
end
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
def check_line(line, index, heredocs)
|
24
|
+
return unless line.length > max
|
25
|
+
return if heredocs &&
|
26
|
+
line_in_whitelisted_heredoc?(heredocs, index.succ)
|
24
27
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
28
|
+
if allow_uri?
|
29
|
+
uri_range = find_excessive_uri_range(line)
|
30
|
+
return if uri_range && allowed_uri_position?(line, uri_range)
|
31
|
+
end
|
29
32
|
|
30
|
-
|
33
|
+
offense(excess_range(uri_range, line, index), line)
|
34
|
+
end
|
31
35
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
36
|
+
def offense(loc, line)
|
37
|
+
message = format(MSG, line.length, max)
|
38
|
+
add_offense(nil, loc, message) { self.max = line.length }
|
39
|
+
end
|
37
40
|
|
38
|
-
|
39
|
-
|
41
|
+
def excess_range(uri_range, line, index)
|
42
|
+
excessive_position = if uri_range && uri_range.begin < max
|
43
|
+
uri_range.end
|
44
|
+
else
|
45
|
+
max
|
46
|
+
end
|
40
47
|
|
41
|
-
|
42
|
-
|
48
|
+
source_range(processed_source.buffer, index + 1,
|
49
|
+
excessive_position...(line.length))
|
43
50
|
end
|
44
51
|
|
45
52
|
def max
|
@@ -11,10 +11,6 @@ module RuboCop
|
|
11
11
|
node.loc.respond_to?(:end) && node.loc.end.nil?
|
12
12
|
end
|
13
13
|
|
14
|
-
def ternary_op?(node)
|
15
|
-
node.loc.respond_to?(:question)
|
16
|
-
end
|
17
|
-
|
18
14
|
def elsif?(node)
|
19
15
|
node.loc.respond_to?(:keyword) && node.loc.keyword &&
|
20
16
|
node.loc.keyword.is?('elsif')
|
@@ -109,7 +109,11 @@ module RuboCop
|
|
109
109
|
end
|
110
110
|
|
111
111
|
def argument_in_method_call(node)
|
112
|
-
node.each_ancestor(:send).find do |a|
|
112
|
+
node.each_ancestor(:send, :block).find do |a|
|
113
|
+
# If the node is inside a block, it makes no difference if that block
|
114
|
+
# is an argument in a method call. It doesn't count.
|
115
|
+
break false if a.block_type?
|
116
|
+
|
113
117
|
_, method_name, *args = *a
|
114
118
|
next if assignment_call?(method_name)
|
115
119
|
args.any? { |arg| within_node?(node, arg) }
|
@@ -27,12 +27,12 @@ module RuboCop
|
|
27
27
|
corrector.insert_before(node.loc.end, "\n".freeze)
|
28
28
|
end
|
29
29
|
else
|
30
|
-
|
31
|
-
node.
|
32
|
-
|
33
|
-
node.
|
34
|
-
|
35
|
-
|
30
|
+
lambda do |corrector|
|
31
|
+
corrector.remove(range_with_surrounding_space(node.loc.end,
|
32
|
+
:left))
|
33
|
+
corrector.insert_after(children(node).last.source_range,
|
34
|
+
node.loc.end.source)
|
35
|
+
end
|
36
36
|
end
|
37
37
|
end
|
38
38
|
|
@@ -65,34 +65,36 @@ module RuboCop
|
|
65
65
|
# on different lines, and each item within is on its own line, and the
|
66
66
|
# closing bracket is on its own line.
|
67
67
|
def multiline?(node)
|
68
|
-
elements = if node.type == :send
|
69
|
-
_receiver, _method_name, *args = *node
|
70
|
-
args.flat_map do |a|
|
71
|
-
# For each argument, if it is a multi-line hash,
|
72
|
-
# then promote the hash elements to method arguments
|
73
|
-
# for the purpose of determining multi-line-ness.
|
74
|
-
if a.hash_type? && a.loc.first_line != a.loc.last_line
|
75
|
-
a.children
|
76
|
-
else
|
77
|
-
a
|
78
|
-
end
|
79
|
-
end
|
80
|
-
else
|
81
|
-
node.children
|
82
|
-
end
|
83
|
-
|
84
68
|
# No need to process anything if the whole node is not multiline
|
85
69
|
# Without the 2nd check, Foo.new({}) is considered multiline, which
|
86
70
|
# it should not be. Essentially, if there are no elements, the
|
87
71
|
# expression can not be multiline.
|
88
|
-
return
|
89
|
-
|
90
|
-
items = elements.map(&:source_range)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
72
|
+
return false unless node.multiline?
|
73
|
+
|
74
|
+
items = elements(node).map(&:source_range)
|
75
|
+
return false if items.empty?
|
76
|
+
|
77
|
+
# If brackets are on different lines and there is one item at least,
|
78
|
+
# then comma is needed anytime for consistent_comma.
|
79
|
+
return true if style == :consistent_comma
|
80
|
+
|
81
|
+
items << node.loc.end
|
82
|
+
items.each_cons(2).all? { |a, b| !on_same_line?(a, b) }
|
83
|
+
end
|
84
|
+
|
85
|
+
def elements(node)
|
86
|
+
return node.children unless node.send_type?
|
87
|
+
|
88
|
+
_receiver, _method_name, *args = *node
|
89
|
+
args.flat_map do |a|
|
90
|
+
# For each argument, if it is a multi-line hash,
|
91
|
+
# then promote the hash elements to method arguments
|
92
|
+
# for the purpose of determining multi-line-ness.
|
93
|
+
if a.hash_type? && a.loc.first_line != a.loc.last_line
|
94
|
+
a.children
|
95
|
+
else
|
96
|
+
a
|
97
|
+
end
|
96
98
|
end
|
97
99
|
end
|
98
100
|
|
@@ -27,7 +27,15 @@ module RuboCop
|
|
27
27
|
def autocorrect(node)
|
28
28
|
return if [:kwarg, :kwoptarg].include?(node.type)
|
29
29
|
|
30
|
-
|
30
|
+
if node.blockarg_type?
|
31
|
+
lambda do |corrector|
|
32
|
+
range = range_with_surrounding_space(node.source_range, :left)
|
33
|
+
range = range_with_surrounding_comma(range, :left)
|
34
|
+
corrector.remove(range)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
->(corrector) { corrector.insert_before(node.loc.name, '_') }
|
38
|
+
end
|
31
39
|
end
|
32
40
|
end
|
33
41
|
end
|
data/lib/rubocop/cop/offense.rb
CHANGED
@@ -87,6 +87,22 @@ module RuboCop
|
|
87
87
|
@status == :disabled
|
88
88
|
end
|
89
89
|
|
90
|
+
# @api public
|
91
|
+
#
|
92
|
+
# @return [Parser::Source::Range]
|
93
|
+
# the range of the code that is highlighted
|
94
|
+
def highlighted_area
|
95
|
+
column_length = if location.first_line == location.last_line
|
96
|
+
location.column_range.count
|
97
|
+
else
|
98
|
+
location.source_line.length - location.column
|
99
|
+
end
|
100
|
+
|
101
|
+
Parser::Source::Range.new(location.source_line,
|
102
|
+
location.column,
|
103
|
+
location.column + column_length)
|
104
|
+
end
|
105
|
+
|
90
106
|
# @api private
|
91
107
|
# This is just for debugging purpose.
|
92
108
|
def to_s
|
@@ -9,26 +9,31 @@ module RuboCop
|
|
9
9
|
#
|
10
10
|
# @example
|
11
11
|
# @bad
|
12
|
-
#
|
13
|
-
#
|
14
|
-
# 'abc' ==
|
15
|
-
# 'ABC'.eql?
|
16
|
-
#
|
12
|
+
# str.downcase == 'abc'
|
13
|
+
# str.upcase.eql? 'ABC'
|
14
|
+
# 'abc' == str.downcase
|
15
|
+
# 'ABC'.eql? str.upcase
|
16
|
+
# str.downcase == str.downcase
|
17
17
|
#
|
18
18
|
# @good
|
19
|
-
#
|
20
|
-
# 'abc'.casecmp(
|
21
|
-
# 'abc'.casecmp('ABC'.downcase).zero?
|
19
|
+
# str.casecmp('ABC').zero?
|
20
|
+
# 'abc'.casecmp(str).zero?
|
22
21
|
class Casecmp < Cop
|
23
22
|
MSG = 'Use `casecmp` instead of `%s %s`.'.freeze
|
24
23
|
CASE_METHODS = [:downcase, :upcase].freeze
|
25
24
|
|
26
25
|
def_node_matcher :downcase_eq, <<-END
|
27
|
-
(send
|
26
|
+
(send
|
27
|
+
$(send _ ${:downcase :upcase})
|
28
|
+
${:== :eql? :!=}
|
29
|
+
${str (send _ {:downcase :upcase} ...) (begin str)})
|
28
30
|
END
|
29
31
|
|
30
32
|
def_node_matcher :eq_downcase, <<-END
|
31
|
-
(send
|
33
|
+
(send
|
34
|
+
{str (send _ {:downcase :upcase} ...) (begin str)}
|
35
|
+
${:== :eql? :!=}
|
36
|
+
$(send _ ${:downcase :upcase}))
|
32
37
|
END
|
33
38
|
|
34
39
|
def on_send(node)
|
@@ -4,8 +4,9 @@
|
|
4
4
|
module RuboCop
|
5
5
|
module Cop
|
6
6
|
module Performance
|
7
|
-
# This cop is used to identify usages of `count` on an
|
8
|
-
# `
|
7
|
+
# This cop is used to identify usages of `count` on an `Enumerable` that
|
8
|
+
# follow calls to `select` or `reject`. Querying logic can instead be
|
9
|
+
# passed to the `count` call.
|
9
10
|
#
|
10
11
|
# @example
|
11
12
|
# # bad
|
@@ -7,21 +7,22 @@ module RuboCop
|
|
7
7
|
# Do not compute the size of statically sized objects.
|
8
8
|
class FixedSize < Cop
|
9
9
|
MSG = 'Do not compute the size of statically sized objects.'.freeze
|
10
|
-
|
11
|
-
|
10
|
+
|
11
|
+
def_node_matcher :counter, <<-MATCHER
|
12
|
+
(send ${array hash str sym} {:count :length :size} $...)
|
13
|
+
MATCHER
|
12
14
|
|
13
15
|
def on_send(node)
|
14
|
-
variable,
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
16
|
+
counter(node) do |variable, arg|
|
17
|
+
return if contains_splat?(variable)
|
18
|
+
return if contains_double_splat?(variable)
|
19
|
+
return if !arg.nil? && string_argument?(arg.first)
|
20
|
+
if node.parent
|
21
|
+
return if node.parent.casgn_type? || node.parent.block_type?
|
22
|
+
end
|
23
|
+
|
24
|
+
add_offense(node, :expression)
|
23
25
|
end
|
24
|
-
add_offense(node, :expression)
|
25
26
|
end
|
26
27
|
|
27
28
|
private
|
@@ -15,26 +15,27 @@ module RuboCop
|
|
15
15
|
# [].reverse_each
|
16
16
|
class ReverseEach < Cop
|
17
17
|
MSG = 'Use `reverse_each` instead of `reverse.each`.'.freeze
|
18
|
+
UNDERSCORE = '_'.freeze
|
18
19
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
return if receiver.nil?
|
23
|
-
_, first_method = *receiver
|
24
|
-
return unless first_method == :reverse
|
20
|
+
def_node_matcher :reverse_each?, <<-MATCHER
|
21
|
+
(send $(send array :reverse) :each)
|
22
|
+
MATCHER
|
25
23
|
|
26
|
-
|
27
|
-
|
28
|
-
|
24
|
+
def on_send(node)
|
25
|
+
reverse_each?(node) do |receiver|
|
26
|
+
source_buffer = node.source_range.source_buffer
|
27
|
+
location_of_reverse = receiver.loc.selector.begin_pos
|
28
|
+
end_location = node.loc.selector.end_pos
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
range = Parser::Source::Range.new(source_buffer,
|
31
|
+
location_of_reverse,
|
32
|
+
end_location)
|
33
|
+
add_offense(node, range, MSG)
|
34
|
+
end
|
34
35
|
end
|
35
36
|
|
36
37
|
def autocorrect(node)
|
37
|
-
->(corrector) { corrector.replace(node.loc.dot,
|
38
|
+
->(corrector) { corrector.replace(node.loc.dot, UNDERSCORE) }
|
38
39
|
end
|
39
40
|
end
|
40
41
|
end
|