rubocop 0.33.0 → 0.34.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/CHANGELOG.md +62 -1
- data/README.md +65 -6
- data/config/default.yml +30 -0
- data/config/disabled.yml +5 -0
- data/config/enabled.yml +19 -3
- data/lib/rubocop.rb +7 -2
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/config.rb +11 -6
- data/lib/rubocop/config_loader.rb +7 -3
- data/lib/rubocop/cop/commissioner.rb +6 -11
- data/lib/rubocop/cop/cop.rb +7 -3
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -8
- data/lib/rubocop/cop/lint/duplicated_key.rb +37 -0
- data/lib/rubocop/cop/lint/end_alignment.rb +6 -6
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +57 -14
- data/lib/rubocop/cop/metrics/method_length.rb +1 -3
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -2
- data/lib/rubocop/cop/mixin/autocorrect_alignment.rb +11 -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 +9 -0
- data/lib/rubocop/cop/mixin/method_preference.rb +28 -0
- data/lib/rubocop/cop/mixin/safe_assignment.rb +2 -2
- data/lib/rubocop/cop/performance/case_when_splat.rb +132 -0
- data/lib/rubocop/cop/performance/string_replacement.rb +45 -28
- data/lib/rubocop/cop/rails/action_filter.rb +31 -5
- data/lib/rubocop/cop/rails/date.rb +6 -5
- data/lib/rubocop/cop/rails/read_write_attribute.rb +1 -1
- data/lib/rubocop/cop/rails/time_zone.rb +12 -1
- data/lib/rubocop/cop/style/alias.rb +1 -0
- data/lib/rubocop/cop/style/block_delimiters.rb +6 -5
- data/lib/rubocop/cop/style/collection_methods.rb +2 -20
- data/lib/rubocop/cop/style/else_alignment.rb +2 -1
- data/lib/rubocop/cop/style/empty_line_between_defs.rb +42 -13
- data/lib/rubocop/cop/style/extra_spacing.rb +17 -3
- data/lib/rubocop/cop/style/first_parameter_indentation.rb +1 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +5 -0
- data/lib/rubocop/cop/style/indentation_width.rb +7 -1
- data/lib/rubocop/cop/style/initial_indentation.rb +5 -0
- data/lib/rubocop/cop/style/multiline_operation_indentation.rb +1 -1
- data/lib/rubocop/cop/style/mutable_constant.rb +35 -0
- data/lib/rubocop/cop/style/next.rb +31 -14
- data/lib/rubocop/cop/style/option_hash.rb +9 -1
- data/lib/rubocop/cop/style/parallel_assignment.rb +21 -44
- data/lib/rubocop/cop/style/redundant_freeze.rb +37 -0
- data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +2 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +35 -3
- data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +1 -0
- data/lib/rubocop/cop/style/string_methods.rb +32 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +54 -12
- data/lib/rubocop/cop/style/trailing_blank_lines.rb +1 -1
- data/lib/rubocop/cop/team.rb +2 -2
- data/lib/rubocop/cop/util.rb +2 -2
- data/lib/rubocop/cop/variable_force.rb +10 -10
- data/lib/rubocop/cop/variable_force/locatable.rb +5 -5
- data/lib/rubocop/formatter/formatter_set.rb +1 -0
- data/lib/rubocop/options.rb +24 -1
- data/lib/rubocop/result_cache.rb +121 -0
- data/lib/rubocop/runner.rb +55 -15
- data/lib/rubocop/target_finder.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/relnotes/v0.34.0.md +182 -0
- data/rubocop.gemspec +1 -1
- metadata +12 -4
@@ -46,15 +46,12 @@ module RuboCop
|
|
46
46
|
|
47
47
|
def investigate(processed_source)
|
48
48
|
reset_errors
|
49
|
+
remove_irrelevant_cops(processed_source.buffer.name)
|
49
50
|
prepare(processed_source)
|
50
51
|
invoke_custom_processing(@cops, processed_source)
|
51
52
|
invoke_custom_processing(@forces, processed_source)
|
52
53
|
process(processed_source.ast) if processed_source.ast
|
53
|
-
@cops.
|
54
|
-
filename = processed_source.buffer.name
|
55
|
-
# ignore files that are of no interest to the cop in question
|
56
|
-
offenses.concat(cop.offenses) if cop.relevant_file?(filename)
|
57
|
-
end
|
54
|
+
@cops.flat_map(&:offenses)
|
58
55
|
end
|
59
56
|
|
60
57
|
private
|
@@ -63,6 +60,10 @@ module RuboCop
|
|
63
60
|
@errors = Hash.new { |hash, k| hash[k] = [] }
|
64
61
|
end
|
65
62
|
|
63
|
+
def remove_irrelevant_cops(filename)
|
64
|
+
@cops.reject! { |cop| cop.excluded_file?(filename) }
|
65
|
+
end
|
66
|
+
|
66
67
|
# TODO: Bad design.
|
67
68
|
def prepare(processed_source)
|
68
69
|
@cops.each { |cop| cop.processed_source = processed_source }
|
@@ -76,12 +77,6 @@ module RuboCop
|
|
76
77
|
cops_or_forces.each do |cop|
|
77
78
|
next unless cop.respond_to?(:investigate)
|
78
79
|
|
79
|
-
if cop.respond_to?(:relevant_file?)
|
80
|
-
# ignore files that are of no interest to the cop in question
|
81
|
-
filename = processed_source.buffer.name
|
82
|
-
next unless cop.relevant_file?(filename)
|
83
|
-
end
|
84
|
-
|
85
80
|
with_cop_error_handling(cop) do
|
86
81
|
cop.investigate(processed_source)
|
87
82
|
end
|
data/lib/rubocop/cop/cop.rb
CHANGED
@@ -198,6 +198,10 @@ module RuboCop
|
|
198
198
|
!file_name_matches_any?(file, 'Exclude', false)
|
199
199
|
end
|
200
200
|
|
201
|
+
def excluded_file?(file)
|
202
|
+
!relevant_file?(file)
|
203
|
+
end
|
204
|
+
|
201
205
|
def style_guide_url
|
202
206
|
url = cop_config && cop_config['StyleGuide']
|
203
207
|
(url.nil? || url.empty?) ? nil : url
|
@@ -249,9 +253,9 @@ module RuboCop
|
|
249
253
|
if Severity::NAMES.include?(severity.to_sym)
|
250
254
|
severity.to_sym
|
251
255
|
else
|
252
|
-
|
253
|
-
|
254
|
-
|
256
|
+
message = "Warning: Invalid severity '#{severity}'. " \
|
257
|
+
"Valid severities are #{Severity::NAMES.join(', ')}."
|
258
|
+
warn(Rainbow(message).red)
|
255
259
|
end
|
256
260
|
end
|
257
261
|
end
|
@@ -5,12 +5,12 @@ module RuboCop
|
|
5
5
|
module Lint
|
6
6
|
# This cop checks for uses of the deprecated class method usages.
|
7
7
|
class DeprecatedClassMethods < Cop
|
8
|
-
include AST::Sexp
|
9
|
-
|
10
8
|
# Inner class to DeprecatedClassMethods.
|
11
9
|
# This class exists to add abstraction and clean naming to the
|
12
10
|
# objects that are going to be operated on.
|
13
11
|
class DeprecatedClassMethod
|
12
|
+
include AST::Sexp
|
13
|
+
|
14
14
|
attr_reader :class_constant, :deprecated_method, :replacement_method
|
15
15
|
|
16
16
|
def initialize(class_constant, deprecated_method, replacement_method)
|
@@ -18,6 +18,13 @@ module RuboCop
|
|
18
18
|
@deprecated_method = deprecated_method
|
19
19
|
@replacement_method = replacement_method
|
20
20
|
end
|
21
|
+
|
22
|
+
def class_nodes
|
23
|
+
@class_nodes ||= [
|
24
|
+
s(:const, nil, class_constant),
|
25
|
+
s(:const, s(:cbase), class_constant)
|
26
|
+
]
|
27
|
+
end
|
21
28
|
end
|
22
29
|
|
23
30
|
MSG = '`%s` is deprecated in favor of `%s`.'
|
@@ -50,18 +57,13 @@ module RuboCop
|
|
50
57
|
receiver, method_name, *_args = *node
|
51
58
|
|
52
59
|
DEPRECATED_METHODS_OBJECT.each do |data|
|
53
|
-
next unless
|
60
|
+
next unless data.class_nodes.include?(receiver)
|
54
61
|
next unless method_name == data.deprecated_method
|
55
62
|
|
56
63
|
block.call(data)
|
57
64
|
end
|
58
65
|
end
|
59
66
|
|
60
|
-
def class_nodes(data)
|
61
|
-
[s(:const, nil, data.class_constant),
|
62
|
-
s(:const, s(:cbase), data.class_constant)]
|
63
|
-
end
|
64
|
-
|
65
67
|
def deprecated_method(data)
|
66
68
|
method_call(data.class_constant, data.deprecated_method)
|
67
69
|
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# This cop checks for duplicated keys in hash literals.
|
7
|
+
#
|
8
|
+
# This cop mirrors a warning in Ruby 2.2.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# hash = { food: 'apple', food: 'orange' }
|
12
|
+
class DuplicatedKey < Cop
|
13
|
+
MSG = 'Duplicated key in hash literal.'
|
14
|
+
|
15
|
+
LITERALS = [:sym, :str, :float, :int]
|
16
|
+
|
17
|
+
def on_hash(node)
|
18
|
+
keys = []
|
19
|
+
|
20
|
+
hash_pairs = *node
|
21
|
+
hash_pairs.each do |pair|
|
22
|
+
key, _value = *pair
|
23
|
+
if keys.include?(key) && LITERALS.include?(key.type)
|
24
|
+
add_offense(key, :expression)
|
25
|
+
elsif keys.include?(key) && key.type == :array
|
26
|
+
key.children.each do |child|
|
27
|
+
return false unless LITERALS.include?(child.type)
|
28
|
+
end
|
29
|
+
add_offense(key, :expression)
|
30
|
+
end
|
31
|
+
keys << key
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -40,6 +40,10 @@ module RuboCop
|
|
40
40
|
check_offset_of_node(node)
|
41
41
|
end
|
42
42
|
|
43
|
+
def on_case(node)
|
44
|
+
check_offset_of_node(node)
|
45
|
+
end
|
46
|
+
|
43
47
|
private
|
44
48
|
|
45
49
|
def check_assignment(node, rhs)
|
@@ -50,11 +54,11 @@ module RuboCop
|
|
50
54
|
|
51
55
|
return unless rhs
|
52
56
|
|
53
|
-
return unless [:if, :while, :until].include?(rhs.type)
|
57
|
+
return unless [:if, :while, :until, :case].include?(rhs.type)
|
54
58
|
return if ternary_op?(rhs)
|
55
59
|
|
56
60
|
expr = node.loc.expression
|
57
|
-
if
|
61
|
+
if variable_alignment?(expr, rhs, style)
|
58
62
|
range = Parser::Source::Range.new(expr.source_buffer,
|
59
63
|
expr.begin_pos,
|
60
64
|
rhs.loc.keyword.end_pos)
|
@@ -68,10 +72,6 @@ module RuboCop
|
|
68
72
|
ignore_node(rhs) # Don't check again.
|
69
73
|
end
|
70
74
|
|
71
|
-
def line_break_before_keyword?(whole_expression, rhs)
|
72
|
-
rhs.loc.keyword.line > whole_expression.line
|
73
|
-
end
|
74
|
-
|
75
75
|
def autocorrect(node)
|
76
76
|
align(node,
|
77
77
|
style == :variable ? node.each_ancestor(:lvasgn).first : node)
|
@@ -12,12 +12,14 @@ module RuboCop
|
|
12
12
|
# format('A value: %s and another: %i', a_value)
|
13
13
|
#
|
14
14
|
class FormatParameterMismatch < Cop
|
15
|
-
# http://rubular.com/r/
|
15
|
+
# http://rubular.com/r/CvpbxkcTzy
|
16
16
|
MSG = 'Number arguments (%i) to `%s` mismatches expected fields (%i).'
|
17
|
-
|
17
|
+
# rubocop:disable Metrics/LineLength
|
18
|
+
FIELD_REGEX = /(%(([\s#+-0\*])?(\d*)?(.\d+)?(\.)?[bBdiouxXeEfgGaAcps]|%))/
|
19
|
+
NAMED_FIELD_REGEX = /%\{[_a-zA-Z][_a-zA-Z]+\}/
|
18
20
|
|
19
21
|
def fields_regex
|
20
|
-
|
22
|
+
FIELD_REGEX
|
21
23
|
end
|
22
24
|
|
23
25
|
def on_send(node)
|
@@ -28,22 +30,55 @@ module RuboCop
|
|
28
30
|
|
29
31
|
def offending_node?(node)
|
30
32
|
if sprintf?(node) || format?(node) || percent?(node)
|
31
|
-
|
32
|
-
|
33
|
+
if named_mode?(node)
|
34
|
+
false
|
35
|
+
else
|
36
|
+
num_of_args_for_format, num_of_expected_fields = count_matches(node)
|
37
|
+
num_of_expected_fields != num_of_args_for_format
|
38
|
+
end
|
33
39
|
else
|
34
40
|
false
|
35
41
|
end
|
36
42
|
end
|
37
43
|
|
44
|
+
def named_mode?(node)
|
45
|
+
receiver_node, _method_name, *args = *node
|
46
|
+
|
47
|
+
relevant_node = if sprintf?(node) || format?(node)
|
48
|
+
args.first
|
49
|
+
elsif percent?(node)
|
50
|
+
receiver_node
|
51
|
+
end
|
52
|
+
|
53
|
+
relevant_node
|
54
|
+
.loc
|
55
|
+
.expression
|
56
|
+
.source
|
57
|
+
.scan(NAMED_FIELD_REGEX).count > 0
|
58
|
+
end
|
59
|
+
|
60
|
+
def heredoc?(node)
|
61
|
+
_receiver, _name, args = *node
|
62
|
+
|
63
|
+
args.loc.expression.source[0, 2] == '<<'
|
64
|
+
end
|
65
|
+
|
38
66
|
def count_matches(node)
|
39
67
|
receiver_node, _method_name, *args = *node
|
40
68
|
|
41
|
-
if sprintf?(node) || format?(node)
|
69
|
+
if (sprintf?(node) || format?(node)) && !heredoc?(node)
|
42
70
|
number_of_args_for_format = (args.size - 1)
|
43
|
-
number_of_expected_fields =
|
71
|
+
number_of_expected_fields = expected_fields_count(args.first)
|
44
72
|
elsif percent?(node)
|
45
|
-
|
46
|
-
|
73
|
+
first_child_argument = args.first
|
74
|
+
|
75
|
+
if first_child_argument.type == :array
|
76
|
+
number_of_args_for_format = args.first.child_nodes.size
|
77
|
+
number_of_expected_fields = expected_fields_count(receiver_node)
|
78
|
+
else
|
79
|
+
number_of_args_for_format = 1
|
80
|
+
number_of_expected_fields = expected_fields_count(receiver_node)
|
81
|
+
end
|
47
82
|
end
|
48
83
|
|
49
84
|
[number_of_args_for_format, number_of_expected_fields]
|
@@ -58,12 +93,14 @@ module RuboCop
|
|
58
93
|
args.size > 1 && :str == args.first.type
|
59
94
|
end
|
60
95
|
|
61
|
-
def
|
96
|
+
def expected_fields_count(node)
|
62
97
|
node
|
63
98
|
.loc
|
64
99
|
.expression
|
65
100
|
.source
|
66
|
-
.scan(
|
101
|
+
.scan(FIELD_REGEX)
|
102
|
+
.select { |x| x.first != '%%' }
|
103
|
+
.reduce(0) { |a, e| a + (e[2] == '*' ? 2 : 1) }
|
67
104
|
end
|
68
105
|
|
69
106
|
def format?(node)
|
@@ -77,9 +114,15 @@ module RuboCop
|
|
77
114
|
def percent?(node)
|
78
115
|
receiver_node, method_name, *arg_nodes = *node
|
79
116
|
|
80
|
-
method_name == :% &&
|
81
|
-
|
82
|
-
|
117
|
+
percent = method_name == :% &&
|
118
|
+
([:str, :dstr].include?(receiver_node.type) ||
|
119
|
+
arg_nodes[0].type == :array)
|
120
|
+
|
121
|
+
if percent && [:str, :dstr].include?(receiver_node.type)
|
122
|
+
return false if heredoc?(node)
|
123
|
+
end
|
124
|
+
|
125
|
+
percent
|
83
126
|
end
|
84
127
|
|
85
128
|
def message(node)
|
@@ -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,10 +6,20 @@ 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
|
@@ -35,7 +45,7 @@ module RuboCop
|
|
35
45
|
end
|
36
46
|
|
37
47
|
def start_of_line?(loc)
|
38
|
-
loc.expression.source_line[0...loc.column]
|
48
|
+
loc.expression.source_line[0...loc.column].blank?
|
39
49
|
end
|
40
50
|
|
41
51
|
def autocorrect(arg)
|
@@ -13,7 +13,7 @@ module RuboCop
|
|
13
13
|
def check_name(node, name, name_range)
|
14
14
|
return if operator?(name)
|
15
15
|
|
16
|
-
if valid_name?(name)
|
16
|
+
if valid_name?(node, name)
|
17
17
|
correct_style_detected
|
18
18
|
else
|
19
19
|
add_offense(node, name_range, message(style)) do
|
@@ -22,9 +22,20 @@ module RuboCop
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
def valid_name?(name)
|
25
|
+
def valid_name?(node, name)
|
26
26
|
pattern = (style == :snake_case ? SNAKE_CASE : CAMEL_CASE)
|
27
|
-
name.match(pattern)
|
27
|
+
name.match(pattern) || class_emitter_method?(node, name)
|
28
|
+
end
|
29
|
+
|
30
|
+
# A class emitter method is a singleton method in a class/module, where
|
31
|
+
# the method has the same name as a class defined in the class/module.
|
32
|
+
def class_emitter_method?(node, name)
|
33
|
+
return false unless node.defs_type?
|
34
|
+
return false unless node.parent
|
35
|
+
|
36
|
+
node.parent.children.any? do |c|
|
37
|
+
c.class_type? && c.loc.name.is?(name.to_s)
|
38
|
+
end
|
28
39
|
end
|
29
40
|
end
|
30
41
|
end
|
@@ -34,9 +34,7 @@ module RuboCop
|
|
34
34
|
def check_source(start_line, end_line)
|
35
35
|
case style
|
36
36
|
when :no_empty_lines
|
37
|
-
check_both(start_line, end_line, MSG_EXTRA)
|
38
|
-
line.empty?
|
39
|
-
end
|
37
|
+
check_both(start_line, end_line, MSG_EXTRA, &:empty?)
|
40
38
|
when :empty_lines
|
41
39
|
check_both(start_line, end_line, MSG_MISSING) do |line|
|
42
40
|
!line.empty?
|
@@ -38,6 +38,15 @@ module RuboCop
|
|
38
38
|
'AlignWith'
|
39
39
|
end
|
40
40
|
|
41
|
+
def variable_alignment?(whole_expression, rhs, end_alignment_style)
|
42
|
+
end_alignment_style == :variable &&
|
43
|
+
!line_break_before_keyword?(whole_expression, rhs)
|
44
|
+
end
|
45
|
+
|
46
|
+
def line_break_before_keyword?(whole_expression, rhs)
|
47
|
+
rhs.loc.line > whole_expression.line
|
48
|
+
end
|
49
|
+
|
41
50
|
def align(node, alignment_node)
|
42
51
|
source_buffer = node.loc.expression.source_buffer
|
43
52
|
begin_pos = node.loc.end.begin_pos
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Common code for cops that deal with preferred methods.
|
6
|
+
module MethodPreference
|
7
|
+
def preferred_method(method)
|
8
|
+
preferred_methods[method.to_sym]
|
9
|
+
end
|
10
|
+
|
11
|
+
def preferred_methods
|
12
|
+
@preferred_methods ||=
|
13
|
+
begin
|
14
|
+
# Make sure default configuration 'foo' => 'bar' is removed from
|
15
|
+
# the total configuration if there is a 'bar' => 'foo' override.
|
16
|
+
default = default_cop_config['PreferredMethods']
|
17
|
+
merged = cop_config['PreferredMethods']
|
18
|
+
overrides = merged.values - default.values
|
19
|
+
merged.reject { |key, _| overrides.include?(key) }.symbolize_keys
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def default_cop_config
|
24
|
+
ConfigLoader.default_configuration[cop_name]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -12,11 +12,11 @@ module RuboCop
|
|
12
12
|
|
13
13
|
child = node.children.first
|
14
14
|
case child.type
|
15
|
-
when *Util::EQUALS_ASGN_NODES
|
16
|
-
true
|
17
15
|
when :send
|
18
16
|
_receiver, method_name, _args = *child
|
19
17
|
method_name.to_s.end_with?('=')
|
18
|
+
when *Util::EQUALS_ASGN_NODES
|
19
|
+
true
|
20
20
|
else
|
21
21
|
false
|
22
22
|
end
|