rubocop 0.32.1 → 0.33.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 +58 -0
- data/README.md +22 -4
- data/config/default.yml +29 -10
- data/config/disabled.yml +8 -4
- data/config/enabled.yml +40 -1
- data/lib/rubocop.rb +8 -0
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config_loader.rb +23 -2
- data/lib/rubocop/cop/lint/block_alignment.rb +1 -1
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +38 -0
- data/lib/rubocop/cop/lint/def_end_alignment.rb +8 -4
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +38 -21
- data/lib/rubocop/cop/lint/duplicate_methods.rb +1 -1
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +95 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/on_method_def.rb +4 -5
- data/lib/rubocop/cop/mixin/string_literals_help.rb +1 -1
- data/lib/rubocop/cop/performance/count.rb +2 -0
- data/lib/rubocop/cop/performance/detect.rb +11 -2
- data/lib/rubocop/cop/performance/flat_map.rb +3 -3
- data/lib/rubocop/cop/performance/string_replacement.rb +161 -0
- data/lib/rubocop/cop/rails/date.rb +8 -8
- data/lib/rubocop/cop/rails/time_zone.rb +22 -13
- data/lib/rubocop/cop/style/block_delimiters.rb +6 -1
- data/lib/rubocop/cop/style/documentation.rb +1 -1
- data/lib/rubocop/cop/style/extra_spacing.rb +84 -5
- data/lib/rubocop/cop/style/first_parameter_indentation.rb +2 -0
- data/lib/rubocop/cop/style/indentation_width.rb +28 -4
- data/lib/rubocop/cop/style/initial_indentation.rb +32 -0
- data/lib/rubocop/cop/style/method_call_parentheses.rb +20 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +8 -4
- data/lib/rubocop/cop/style/option_hash.rb +56 -0
- data/lib/rubocop/cop/style/optional_arguments.rb +49 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +3 -0
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +3 -66
- data/lib/rubocop/cop/style/redundant_return.rb +20 -3
- data/lib/rubocop/cop/style/rescue_ensure_alignment.rb +77 -0
- data/lib/rubocop/cop/style/rescue_modifier.rb +4 -28
- data/lib/rubocop/cop/style/send.rb +18 -0
- data/lib/rubocop/cop/style/space_inside_string_interpolation.rb +32 -13
- data/lib/rubocop/cop/style/symbol_literal.rb +1 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +10 -1
- data/lib/rubocop/cop/style/while_until_do.rb +1 -1
- data/lib/rubocop/cop/style/word_array.rb +13 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +54 -5
- data/lib/rubocop/options.rb +81 -55
- data/lib/rubocop/version.rb +1 -1
- data/relnotes/v0.33.0.md +157 -0
- metadata +11 -2
@@ -20,7 +20,7 @@ module RuboCop
|
|
20
20
|
include OnMethodDef
|
21
21
|
include EndKeywordAlignment
|
22
22
|
|
23
|
-
MSG = '`end` at %d, %d is not aligned with `%s` at %d, %d'
|
23
|
+
MSG = '`end` at %d, %d is not aligned with `%s` at %d, %d.'
|
24
24
|
|
25
25
|
def on_method_def(node, _method_name, _args, _body)
|
26
26
|
check_offset_of_node(node)
|
@@ -28,8 +28,8 @@ module RuboCop
|
|
28
28
|
|
29
29
|
def on_send(node)
|
30
30
|
receiver, method_name, *args = *node
|
31
|
-
return unless
|
32
|
-
|
31
|
+
return unless modifier_and_def_on_same_line?(receiver, method_name,
|
32
|
+
args)
|
33
33
|
|
34
34
|
method_def = args.first
|
35
35
|
if style == :start_of_line
|
@@ -49,7 +49,11 @@ module RuboCop
|
|
49
49
|
private
|
50
50
|
|
51
51
|
def autocorrect(node)
|
52
|
-
|
52
|
+
if style == :start_of_line && node.parent && node.parent.send_type?
|
53
|
+
align(node, node.parent)
|
54
|
+
else
|
55
|
+
align(node, node)
|
56
|
+
end
|
53
57
|
end
|
54
58
|
end
|
55
59
|
end
|
@@ -7,20 +7,27 @@ module RuboCop
|
|
7
7
|
class DeprecatedClassMethods < Cop
|
8
8
|
include AST::Sexp
|
9
9
|
|
10
|
-
|
10
|
+
# Inner class to DeprecatedClassMethods.
|
11
|
+
# This class exists to add abstraction and clean naming to the
|
12
|
+
# objects that are going to be operated on.
|
13
|
+
class DeprecatedClassMethod
|
14
|
+
attr_reader :class_constant, :deprecated_method, :replacement_method
|
11
15
|
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
def initialize(class_constant, deprecated_method, replacement_method)
|
17
|
+
@class_constant = class_constant
|
18
|
+
@deprecated_method = deprecated_method
|
19
|
+
@replacement_method = replacement_method
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
MSG = '`%s` is deprecated in favor of `%s`.'
|
24
|
+
DEPRECATED_METHODS_OBJECT = [
|
25
|
+
DeprecatedClassMethod.new(:File, :exists?, :exist?),
|
26
|
+
DeprecatedClassMethod.new(:Dir, :exists?, :exist?)
|
15
27
|
]
|
16
28
|
|
17
29
|
def on_send(node)
|
18
|
-
|
19
|
-
|
20
|
-
DEPRECATED_METHODS.each do |data|
|
21
|
-
next unless class_nodes(data).include?(receiver)
|
22
|
-
next unless method_name == data[1]
|
23
|
-
|
30
|
+
check(node) do |data|
|
24
31
|
add_offense(node, :selector,
|
25
32
|
format(MSG,
|
26
33
|
deprecated_method(data),
|
@@ -30,31 +37,41 @@ module RuboCop
|
|
30
37
|
|
31
38
|
def autocorrect(node)
|
32
39
|
lambda do |corrector|
|
33
|
-
|
34
|
-
|
35
|
-
DEPRECATED_METHODS.each do |data|
|
36
|
-
next unless class_nodes(data).include?(receiver)
|
37
|
-
next unless method_name == data[1]
|
38
|
-
|
40
|
+
check(node) do |data|
|
39
41
|
corrector.replace(node.loc.selector,
|
40
|
-
data
|
42
|
+
data.replacement_method.to_s)
|
41
43
|
end
|
42
44
|
end
|
43
45
|
end
|
44
46
|
|
45
47
|
private
|
46
48
|
|
49
|
+
def check(node, &block)
|
50
|
+
receiver, method_name, *_args = *node
|
51
|
+
|
52
|
+
DEPRECATED_METHODS_OBJECT.each do |data|
|
53
|
+
next unless class_nodes(data).include?(receiver)
|
54
|
+
next unless method_name == data.deprecated_method
|
55
|
+
|
56
|
+
block.call(data)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
47
60
|
def class_nodes(data)
|
48
|
-
[s(:const, nil, data
|
49
|
-
s(:const, s(:cbase), data
|
61
|
+
[s(:const, nil, data.class_constant),
|
62
|
+
s(:const, s(:cbase), data.class_constant)]
|
50
63
|
end
|
51
64
|
|
52
65
|
def deprecated_method(data)
|
53
|
-
|
66
|
+
method_call(data.class_constant, data.deprecated_method)
|
54
67
|
end
|
55
68
|
|
56
69
|
def replacement_method(data)
|
57
|
-
|
70
|
+
method_call(data.class_constant, data.replacement_method)
|
71
|
+
end
|
72
|
+
|
73
|
+
def method_call(class_constant, method)
|
74
|
+
format('%s.%s', class_constant, method)
|
58
75
|
end
|
59
76
|
end
|
60
77
|
end
|
@@ -0,0 +1,95 @@
|
|
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/HdWs2uXZv4
|
16
|
+
MSG = 'Number arguments (%i) to `%s` mismatches expected fields (%i).'
|
17
|
+
FIELDS_REGEX = /%([\s#+-0\*])?([0-9]*)?(.[0-9]+)?[bBdiouxXeEfgGacps]/
|
18
|
+
|
19
|
+
def fields_regex
|
20
|
+
FIELDS_REGEX
|
21
|
+
end
|
22
|
+
|
23
|
+
def on_send(node)
|
24
|
+
add_offense(node, :selector) if offending_node?(node)
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def offending_node?(node)
|
30
|
+
if sprintf?(node) || format?(node) || percent?(node)
|
31
|
+
num_of_args_for_format, num_of_expected_fields = count_matches(node)
|
32
|
+
num_of_expected_fields != num_of_args_for_format
|
33
|
+
else
|
34
|
+
false
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def count_matches(node)
|
39
|
+
receiver_node, _method_name, *args = *node
|
40
|
+
|
41
|
+
if sprintf?(node) || format?(node)
|
42
|
+
number_of_args_for_format = (args.size - 1)
|
43
|
+
number_of_expected_fields = expected_fields(args.first).size
|
44
|
+
elsif percent?(node)
|
45
|
+
number_of_args_for_format = args.first.child_nodes.size
|
46
|
+
number_of_expected_fields = expected_fields(receiver_node).size
|
47
|
+
end
|
48
|
+
|
49
|
+
[number_of_args_for_format, number_of_expected_fields]
|
50
|
+
end
|
51
|
+
|
52
|
+
def format_method?(name, node)
|
53
|
+
receiver, method_name, *args = *node
|
54
|
+
|
55
|
+
# commands have no explicit receiver
|
56
|
+
return false unless !receiver && method_name == name
|
57
|
+
|
58
|
+
args.size > 1 && :str == args.first.type
|
59
|
+
end
|
60
|
+
|
61
|
+
def expected_fields(node)
|
62
|
+
node
|
63
|
+
.loc
|
64
|
+
.expression
|
65
|
+
.source
|
66
|
+
.scan(FIELDS_REGEX)
|
67
|
+
end
|
68
|
+
|
69
|
+
def format?(node)
|
70
|
+
format_method?(:format, node)
|
71
|
+
end
|
72
|
+
|
73
|
+
def sprintf?(node)
|
74
|
+
format_method?(:sprintf, node)
|
75
|
+
end
|
76
|
+
|
77
|
+
def percent?(node)
|
78
|
+
receiver_node, method_name, *arg_nodes = *node
|
79
|
+
|
80
|
+
method_name == :% &&
|
81
|
+
([:str, :dstr].include?(receiver_node.type) ||
|
82
|
+
arg_nodes[0].type == :array)
|
83
|
+
end
|
84
|
+
|
85
|
+
def message(node)
|
86
|
+
_receiver, method_name, *_args = *node
|
87
|
+
num_args_for_format, num_expected_fields = count_matches(node)
|
88
|
+
|
89
|
+
method_name = 'String#%' if '%' == method_name.to_s
|
90
|
+
format(MSG, num_args_for_format, method_name, num_expected_fields)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -19,12 +19,11 @@ module RuboCop
|
|
19
19
|
# Returns true for constructs such as
|
20
20
|
# private def my_method
|
21
21
|
# which are allowed in Ruby 2.1 and later.
|
22
|
-
def
|
22
|
+
def modifier_and_def_on_same_line?(receiver, method_name, args)
|
23
23
|
!receiver &&
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
args.size == 1 && [:def, :defs].include?(args.first.type)
|
24
|
+
method_name != :def &&
|
25
|
+
args.size == 1 &&
|
26
|
+
[:def, :defs].include?(args.first.type)
|
28
27
|
end
|
29
28
|
end
|
30
29
|
end
|
@@ -68,6 +68,8 @@ module RuboCop
|
|
68
68
|
if selector.is_a?(Symbol)
|
69
69
|
if expression && expression.parent.loc.respond_to?(:selector)
|
70
70
|
expression.parent.loc.selector
|
71
|
+
else
|
72
|
+
left.loc.selector if left.loc.respond_to?(:selector)
|
71
73
|
end
|
72
74
|
else
|
73
75
|
_enumerable, selector, params = *expression
|
@@ -22,21 +22,24 @@ module RuboCop
|
|
22
22
|
REVERSE_MSG = 'Use `reverse.%s` instead of `%s.%s`.'
|
23
23
|
|
24
24
|
SELECT_METHODS = [:select, :find_all]
|
25
|
+
DANGEROUS_METHODS = [:first, :last]
|
25
26
|
|
26
27
|
def on_send(node)
|
27
28
|
receiver, second_method = *node
|
28
|
-
return unless second_method == :first || second_method == :last
|
29
29
|
return if receiver.nil?
|
30
|
+
return unless DANGEROUS_METHODS.include?(second_method)
|
30
31
|
|
31
32
|
receiver, _args, body = *receiver if receiver.block_type?
|
32
33
|
|
33
|
-
|
34
|
+
caller, first_method, args = *receiver
|
34
35
|
|
35
36
|
# check that we have usual block or block pass
|
36
37
|
return if body.nil? && (args.nil? || !args.block_pass_type?)
|
37
38
|
|
38
39
|
return unless SELECT_METHODS.include?(first_method)
|
39
40
|
|
41
|
+
return if lazy?(caller)
|
42
|
+
|
40
43
|
range = receiver.loc.selector.join(node.loc.selector)
|
41
44
|
|
42
45
|
message = second_method == :last ? REVERSE_MSG : MSG
|
@@ -70,6 +73,12 @@ module RuboCop
|
|
70
73
|
config.for_cop('Style/CollectionMethods') \
|
71
74
|
['PreferredMethods']['detect'] || 'detect'
|
72
75
|
end
|
76
|
+
|
77
|
+
def lazy?(node)
|
78
|
+
return false if node.nil?
|
79
|
+
receiver, method, _args = *node
|
80
|
+
method == :lazy && !receiver.nil?
|
81
|
+
end
|
73
82
|
end
|
74
83
|
end
|
75
84
|
end
|
@@ -18,11 +18,11 @@ module RuboCop
|
|
18
18
|
MSG = 'Use `flat_map` instead of `%s...%s`.'
|
19
19
|
FLATTEN_MULTIPLE_LEVELS = ' Beware, `flat_map` only flattens 1 level ' \
|
20
20
|
'and `flatten` can be used to flatten ' \
|
21
|
-
'multiple levels'
|
21
|
+
'multiple levels.'
|
22
22
|
FLATTEN = [:flatten, :flatten!]
|
23
23
|
|
24
24
|
def on_send(node)
|
25
|
-
left, second_method, flatten_param
|
25
|
+
left, second_method, flatten_param = *node
|
26
26
|
return unless FLATTEN.include?(second_method)
|
27
27
|
flatten_level, = *flatten_param
|
28
28
|
expression, = *left
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def autocorrect(node)
|
47
|
-
receiver, _flatten, flatten_param
|
47
|
+
receiver, _flatten, flatten_param = *node
|
48
48
|
flatten_level, = *flatten_param
|
49
49
|
return if flatten_level.nil?
|
50
50
|
|
@@ -0,0 +1,161 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Performance
|
6
|
+
# This cop identifies places where `gsub` can be replaced by
|
7
|
+
# `tr` or `delete`.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# @bad
|
11
|
+
# 'abc'.gsub('b', 'd')
|
12
|
+
# 'abc'.gsub('a', '')
|
13
|
+
# 'abc'.gsub(/a/, 'd')
|
14
|
+
# 'abc'.gsub!('a', 'd')
|
15
|
+
#
|
16
|
+
# @good
|
17
|
+
# 'abc'.gsub(/.*/, 'a')
|
18
|
+
# 'abc'.gsub(/a+/, 'd')
|
19
|
+
# 'abc'.tr('b', 'd')
|
20
|
+
# 'a b c'.delete(' ')
|
21
|
+
class StringReplacement < Cop
|
22
|
+
MSG = 'Use `%s` instead of `%s`.'
|
23
|
+
DETERMINISTIC_REGEX = /^[\w\s\-,."']+$/
|
24
|
+
REGEXP_CONSTRUCTOR_METHODS = [:new, :compile]
|
25
|
+
GSUB_METHODS = [:gsub, :gsub!]
|
26
|
+
DETERMINISTIC_TYPES = [:regexp, :str, :send]
|
27
|
+
|
28
|
+
def on_send(node)
|
29
|
+
_string, method, first_param, second_param = *node
|
30
|
+
return unless GSUB_METHODS.include?(method)
|
31
|
+
return unless second_param && second_param.str_type?
|
32
|
+
return unless DETERMINISTIC_TYPES.include?(first_param.type)
|
33
|
+
|
34
|
+
first_source = first_source(first_param)
|
35
|
+
second_source, = *second_param
|
36
|
+
|
37
|
+
return if first_source.nil?
|
38
|
+
|
39
|
+
if regex?(first_param)
|
40
|
+
return unless first_source =~ DETERMINISTIC_REGEX
|
41
|
+
end
|
42
|
+
|
43
|
+
return if first_source.length != 1
|
44
|
+
return unless second_source.length <= 1
|
45
|
+
|
46
|
+
message = message(method, first_source, second_source)
|
47
|
+
add_offense(node, range(node), message)
|
48
|
+
end
|
49
|
+
|
50
|
+
def autocorrect(node)
|
51
|
+
_string, method, first_param, second_param = *node
|
52
|
+
first_source = first_source(first_param)
|
53
|
+
second_source, = *second_param
|
54
|
+
replacement_method = replacement_method(first_source, second_source)
|
55
|
+
|
56
|
+
lambda do |corrector|
|
57
|
+
replacement =
|
58
|
+
if second_source.empty? && first_source.length == 1
|
59
|
+
"#{replacement_method}#{'!' if bang_method?(method)}" \
|
60
|
+
"(#{escape(first_source)})"
|
61
|
+
else
|
62
|
+
"#{replacement_method}#{'!' if bang_method?(method)}" \
|
63
|
+
"(#{escape(first_source)}, #{escape(second_source)})"
|
64
|
+
end
|
65
|
+
|
66
|
+
corrector.replace(range(node), replacement)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
private
|
71
|
+
|
72
|
+
def first_source(first_param)
|
73
|
+
case first_param.type
|
74
|
+
when :regexp, :send
|
75
|
+
return nil unless regex?(first_param)
|
76
|
+
|
77
|
+
source, = extract_source(first_param)
|
78
|
+
when :str
|
79
|
+
source, = *first_param
|
80
|
+
end
|
81
|
+
|
82
|
+
source
|
83
|
+
end
|
84
|
+
|
85
|
+
def extract_source(node)
|
86
|
+
case node.type
|
87
|
+
when :regexp
|
88
|
+
source_from_regex_literal(node)
|
89
|
+
when :send
|
90
|
+
source_from_regex_constructor(node)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def source_from_regex_literal(node)
|
95
|
+
regex, = *node
|
96
|
+
source, = *regex
|
97
|
+
source
|
98
|
+
end
|
99
|
+
|
100
|
+
def source_from_regex_constructor(node)
|
101
|
+
_const, _init, regex = *node
|
102
|
+
case regex.type
|
103
|
+
when :regexp
|
104
|
+
source_from_regex_literal(regex)
|
105
|
+
when :str
|
106
|
+
source, = *regex
|
107
|
+
source
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def regex?(node)
|
112
|
+
return true if node.regexp_type?
|
113
|
+
|
114
|
+
const, init, = *node
|
115
|
+
_, klass = *const
|
116
|
+
|
117
|
+
klass == :Regexp && REGEXP_CONSTRUCTOR_METHODS.include?(init)
|
118
|
+
end
|
119
|
+
|
120
|
+
def range(node)
|
121
|
+
Parser::Source::Range.new(node.loc.expression.source_buffer,
|
122
|
+
node.loc.selector.begin_pos,
|
123
|
+
node.loc.expression.end_pos)
|
124
|
+
end
|
125
|
+
|
126
|
+
def replacement_method(first_source, second_source)
|
127
|
+
if second_source.empty? && first_source.length == 1
|
128
|
+
'delete'
|
129
|
+
else
|
130
|
+
'tr'
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def message(method, first_source, second_source)
|
135
|
+
replacement_method = replacement_method(first_source, second_source)
|
136
|
+
|
137
|
+
format(MSG,
|
138
|
+
"#{replacement_method}#{'!' if bang_method?(method)}",
|
139
|
+
method)
|
140
|
+
end
|
141
|
+
|
142
|
+
def bang_method?(method)
|
143
|
+
method.to_s.end_with?('!')
|
144
|
+
end
|
145
|
+
|
146
|
+
def escape(string)
|
147
|
+
if require_double_quotes?(string)
|
148
|
+
string.inspect
|
149
|
+
else
|
150
|
+
"'#{string}'"
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def require_double_quotes?(string)
|
155
|
+
/'/ =~ string.inspect ||
|
156
|
+
StringHelp::ESCAPED_CHAR_REGEXP =~ string.inspect
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|