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
@@ -12,12 +12,12 @@ module RuboCop
|
|
12
12
|
# The cop also reports warnings when you are using 'to_time' method,
|
13
13
|
# because it doesn't know about Rails time zone too.
|
14
14
|
#
|
15
|
-
# Two styles are supported for this cop. When EnforcedStyle is '
|
15
|
+
# Two styles are supported for this cop. When EnforcedStyle is 'strict'
|
16
16
|
# then the Date methods (today, current, yesterday, tomorrow)
|
17
17
|
# are prohibited and the usage of both 'to_time'
|
18
18
|
# and 'to_time_in_current_zone' is reported as warning.
|
19
19
|
#
|
20
|
-
# When EnforcedStyle is '
|
20
|
+
# When EnforcedStyle is 'flexible' then only 'Date.today' is prohibited
|
21
21
|
# and only 'to_time' is reported as warning.
|
22
22
|
#
|
23
23
|
# @example
|
@@ -46,9 +46,9 @@ module RuboCop
|
|
46
46
|
BAD_DAYS = [:today, :current, :yesterday, :tomorrow]
|
47
47
|
|
48
48
|
def on_const(node)
|
49
|
-
|
50
|
-
|
51
|
-
return unless method_send?(node)
|
49
|
+
mod, klass = *node.children
|
50
|
+
# we should only check core Date class (`Date` or `::Date`)
|
51
|
+
return unless (mod.nil? || mod.cbase_type?) && method_send?(node)
|
52
52
|
|
53
53
|
check_date_node(node.parent) if klass == :Date
|
54
54
|
end
|
@@ -75,7 +75,7 @@ module RuboCop
|
|
75
75
|
add_offense(node, :selector,
|
76
76
|
format(MSG,
|
77
77
|
"Date.#{method_name}",
|
78
|
-
|
78
|
+
"Time.zone.#{method_name}")
|
79
79
|
)
|
80
80
|
end
|
81
81
|
|
@@ -104,7 +104,7 @@ module RuboCop
|
|
104
104
|
end
|
105
105
|
|
106
106
|
def good_days
|
107
|
-
style == :
|
107
|
+
style == :strict ? [] : [:current, :yesterday, :tomorrow]
|
108
108
|
end
|
109
109
|
|
110
110
|
def bad_days
|
@@ -112,7 +112,7 @@ module RuboCop
|
|
112
112
|
end
|
113
113
|
|
114
114
|
def bad_methods
|
115
|
-
style == :
|
115
|
+
style == :strict ? [:to_time, :to_time_in_current_zone] : [:to_time]
|
116
116
|
end
|
117
117
|
end
|
118
118
|
end
|
@@ -8,10 +8,10 @@ module RuboCop
|
|
8
8
|
# Built on top of Ruby on Rails style guide (https://github.com/bbatsov/rails-style-guide#time)
|
9
9
|
# and the article http://danilenko.org/2012/7/6/rails_timezones/ .
|
10
10
|
#
|
11
|
-
# Two styles are supported for this cop. When EnforcedStyle is '
|
11
|
+
# Two styles are supported for this cop. When EnforcedStyle is 'strict'
|
12
12
|
# then only use of Time.zone is allowed.
|
13
13
|
#
|
14
|
-
# When EnforcedStyle is '
|
14
|
+
# When EnforcedStyle is 'flexible' then it's also allowed
|
15
15
|
# to use Time.in_time_zone.
|
16
16
|
#
|
17
17
|
# @example
|
@@ -24,6 +24,7 @@ module RuboCop
|
|
24
24
|
# Time.zone.parse('2015-03-02 19:05:37')
|
25
25
|
#
|
26
26
|
# # no offense only if style is 'acceptable'
|
27
|
+
# Time.current
|
27
28
|
# DateTime.strptime(str, "%Y-%m-%d %H:%M %Z").in_time_zone
|
28
29
|
# Time.at(timestamp).in_time_zone
|
29
30
|
class TimeZone < Cop
|
@@ -35,18 +36,22 @@ module RuboCop
|
|
35
36
|
|
36
37
|
MSG_LOCALTIME = 'Do not use `Time.localtime` without offset or zone.'
|
37
38
|
|
39
|
+
MSG_CURRENT = 'Do not use `%s`. Use `Time.zone.now` instead.'
|
40
|
+
|
38
41
|
TIMECLASS = [:Time, :DateTime]
|
39
42
|
|
40
|
-
|
43
|
+
DANGEROUS_METHODS = [:now, :local, :new, :strftime,
|
44
|
+
:parse, :at, :current]
|
41
45
|
|
42
|
-
ACCEPTED_METHODS = [:in_time_zone, :utc, :getlocal,
|
46
|
+
ACCEPTED_METHODS = [:current, :in_time_zone, :utc, :getlocal,
|
43
47
|
:iso8601, :jisx0301, :rfc3339,
|
44
48
|
:to_i, :to_f]
|
45
49
|
|
46
50
|
def on_const(node)
|
47
|
-
|
48
|
-
|
49
|
-
|
51
|
+
mod, klass = *node
|
52
|
+
# we should only check core class
|
53
|
+
# (`DateTime`/`Time` or `::Date`/`::DateTime`)
|
54
|
+
return unless (mod.nil? || mod.cbase_type?) && method_send?(node)
|
50
55
|
|
51
56
|
check_time_node(klass, node.parent) if TIMECLASS.include?(klass)
|
52
57
|
end
|
@@ -59,7 +64,7 @@ module RuboCop
|
|
59
64
|
|
60
65
|
return check_localtime(node) if need_check_localtime?(chain)
|
61
66
|
|
62
|
-
method_name = (chain &
|
67
|
+
method_name = (chain & DANGEROUS_METHODS).join('.')
|
63
68
|
|
64
69
|
message = build_message(klass, method_name, node)
|
65
70
|
|
@@ -72,11 +77,15 @@ module RuboCop
|
|
72
77
|
"#{klass}.#{method_name}",
|
73
78
|
acceptable_methods(klass, method_name, node).join(', ')
|
74
79
|
)
|
80
|
+
elsif method_name == 'current'
|
81
|
+
format(MSG_CURRENT,
|
82
|
+
"#{klass}.#{method_name}"
|
83
|
+
)
|
75
84
|
else
|
76
85
|
safe_method_name = safe_method(method_name, node)
|
77
86
|
format(MSG,
|
78
87
|
"#{klass}.#{method_name}",
|
79
|
-
"
|
88
|
+
"Time.zone.#{safe_method_name}"
|
80
89
|
)
|
81
90
|
end
|
82
91
|
end
|
@@ -128,7 +137,7 @@ module RuboCop
|
|
128
137
|
end
|
129
138
|
|
130
139
|
def danger_chain?(chain)
|
131
|
-
(chain &
|
140
|
+
(chain & DANGEROUS_METHODS).empty? || !(chain & good_methods).empty?
|
132
141
|
end
|
133
142
|
|
134
143
|
def need_check_localtime?(chain)
|
@@ -136,16 +145,16 @@ module RuboCop
|
|
136
145
|
end
|
137
146
|
|
138
147
|
def acceptable?
|
139
|
-
style == :
|
148
|
+
style == :flexible
|
140
149
|
end
|
141
150
|
|
142
151
|
def good_methods
|
143
|
-
style == :
|
152
|
+
style == :strict ? [:zone] : [:zone] + ACCEPTED_METHODS
|
144
153
|
end
|
145
154
|
|
146
155
|
def acceptable_methods(klass, method_name, node)
|
147
156
|
acceptable = [
|
148
|
-
"
|
157
|
+
"`Time.zone.#{safe_method(method_name, node)}`"
|
149
158
|
]
|
150
159
|
|
151
160
|
ACCEPTED_METHODS.each do |am|
|
@@ -155,7 +155,8 @@ module RuboCop
|
|
155
155
|
def return_value_of_scope?(node)
|
156
156
|
return unless node.parent
|
157
157
|
|
158
|
-
conditional?(node.parent) || node.parent
|
158
|
+
conditional?(node.parent) || array_or_range?(node.parent) ||
|
159
|
+
node.parent.children.last == node
|
159
160
|
end
|
160
161
|
|
161
162
|
def procedural_methods
|
@@ -173,6 +174,10 @@ module RuboCop
|
|
173
174
|
def conditional?(node)
|
174
175
|
node.if_type? || node.or_type? || node.and_type?
|
175
176
|
end
|
177
|
+
|
178
|
+
def array_or_range?(node)
|
179
|
+
node.array_type? || node.irange_type? || node.erange_type?
|
180
|
+
end
|
176
181
|
end
|
177
182
|
end
|
178
183
|
end
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
# This cop checks for missing top-level documentation of
|
7
7
|
# classes and modules. Classes with no body are exempt from the
|
8
8
|
# check and so are namespace modules - modules that have nothing in
|
9
|
-
# their bodies except classes or other
|
9
|
+
# their bodies except classes or other modules.
|
10
10
|
#
|
11
11
|
# The documentation requirement is annulled if the class or module has
|
12
12
|
# a "#:nodoc:" comment next to it. Likewise, "#:nodoc: all" does the
|
@@ -7,19 +7,29 @@ module RuboCop
|
|
7
7
|
#
|
8
8
|
# @example
|
9
9
|
#
|
10
|
-
#
|
10
|
+
# # good if AllowForAlignment is true
|
11
|
+
# name = "RuboCop"
|
12
|
+
# # Some comment and an empty line
|
13
|
+
#
|
14
|
+
# website += "/bbatsov/rubocop" unless cond
|
15
|
+
# puts "rubocop" if debug
|
16
|
+
#
|
17
|
+
# # bad for any configuration
|
18
|
+
# set_app("RuboCop")
|
11
19
|
# website = "https://github.com/bbatsov/rubocop"
|
12
20
|
class ExtraSpacing < Cop
|
13
21
|
MSG = 'Unnecessary spacing detected.'
|
14
22
|
|
15
23
|
def investigate(processed_source)
|
16
24
|
processed_source.tokens.each_cons(2) do |t1, t2|
|
17
|
-
next
|
18
|
-
next
|
19
|
-
|
25
|
+
next if t2.type == :tNL
|
26
|
+
next if t1.pos.line != t2.pos.line
|
27
|
+
next if t2.pos.begin_pos - 1 <= t1.pos.end_pos
|
28
|
+
next if allow_for_alignment? && aligned_with_something?(t2)
|
20
29
|
start_pos = t1.pos.end_pos
|
21
30
|
end_pos = t2.pos.begin_pos - 1
|
22
|
-
range = Parser::Source::Range.new(buffer,
|
31
|
+
range = Parser::Source::Range.new(processed_source.buffer,
|
32
|
+
start_pos, end_pos)
|
23
33
|
add_offense(range, range, MSG)
|
24
34
|
end
|
25
35
|
end
|
@@ -27,6 +37,75 @@ module RuboCop
|
|
27
37
|
def autocorrect(range)
|
28
38
|
->(corrector) { corrector.remove(range) }
|
29
39
|
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def allow_for_alignment?
|
44
|
+
cop_config['AllowForAlignment']
|
45
|
+
end
|
46
|
+
|
47
|
+
def aligned_with_something?(token)
|
48
|
+
return aligned_comments?(token) if token.type == :tCOMMENT
|
49
|
+
|
50
|
+
pre = (token.pos.line - 2).downto(0)
|
51
|
+
post = token.pos.line.upto(processed_source.lines.size - 1)
|
52
|
+
aligned_with?(pre, token) || aligned_with?(post, token)
|
53
|
+
end
|
54
|
+
|
55
|
+
def aligned_comments?(token)
|
56
|
+
ix = processed_source.comments.index do |c|
|
57
|
+
c.loc.expression.begin_pos == token.pos.begin_pos
|
58
|
+
end
|
59
|
+
aligned_with_previous_comment?(ix) || aligned_with_next_comment?(ix)
|
60
|
+
end
|
61
|
+
|
62
|
+
def aligned_with_previous_comment?(ix)
|
63
|
+
ix > 0 && comment_column(ix - 1) == comment_column(ix)
|
64
|
+
end
|
65
|
+
|
66
|
+
def aligned_with_next_comment?(ix)
|
67
|
+
ix < processed_source.comments.length - 1 &&
|
68
|
+
comment_column(ix + 1) == comment_column(ix)
|
69
|
+
end
|
70
|
+
|
71
|
+
def comment_column(ix)
|
72
|
+
processed_source.comments[ix].loc.column
|
73
|
+
end
|
74
|
+
|
75
|
+
def aligned_with?(indices_to_check, token)
|
76
|
+
indices_to_check.each do |ix|
|
77
|
+
next if comment_lines.include?(ix + 1)
|
78
|
+
line = processed_source.lines[ix]
|
79
|
+
next if line.strip.empty?
|
80
|
+
return (aligned_words?(token, line) ||
|
81
|
+
aligned_assignments?(token, line) ||
|
82
|
+
aligned_same_character?(token, line))
|
83
|
+
end
|
84
|
+
false # No line to check was found.
|
85
|
+
end
|
86
|
+
|
87
|
+
def comment_lines
|
88
|
+
@comment_lines ||=
|
89
|
+
begin
|
90
|
+
whole_line_comments = processed_source.comments.select do |c|
|
91
|
+
begins_its_line?(c.loc.expression)
|
92
|
+
end
|
93
|
+
whole_line_comments.map(&:loc).map(&:line)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def aligned_words?(token, line)
|
98
|
+
line[token.pos.column - 1, 2] =~ /\s\S/
|
99
|
+
end
|
100
|
+
|
101
|
+
def aligned_assignments?(token, line)
|
102
|
+
token.type == :tOP_ASGN &&
|
103
|
+
line[token.pos.column + token.text.length] == '='
|
104
|
+
end
|
105
|
+
|
106
|
+
def aligned_same_character?(token, line)
|
107
|
+
line[token.pos.column] == token.text[0]
|
108
|
+
end
|
30
109
|
end
|
31
110
|
end
|
32
111
|
end
|
@@ -39,6 +39,8 @@ module RuboCop
|
|
39
39
|
private
|
40
40
|
|
41
41
|
def message(arg_node)
|
42
|
+
return 'Bad indentation of the first parameter.' if arg_node.nil?
|
43
|
+
|
42
44
|
send_node = arg_node.parent
|
43
45
|
text = base_range(send_node, arg_node).source.strip
|
44
46
|
base = if text !~ /\n/ && special_inner_call_indentation?(send_node)
|
@@ -82,10 +82,11 @@ module RuboCop
|
|
82
82
|
def on_send(node)
|
83
83
|
super
|
84
84
|
receiver, method_name, *args = *node
|
85
|
-
return unless
|
86
|
-
|
85
|
+
return unless modifier_and_def_on_same_line?(receiver, method_name,
|
86
|
+
args)
|
87
|
+
|
88
|
+
*_, body = *args.first
|
87
89
|
|
88
|
-
_method_name, _args, body = *args.first
|
89
90
|
def_end_config = config.for_cop('Lint/DefEndAlignment')
|
90
91
|
style = if def_end_config['Enabled']
|
91
92
|
def_end_config['AlignWith']
|
@@ -211,13 +212,36 @@ module RuboCop
|
|
211
212
|
body_node = body_node.children.first
|
212
213
|
end
|
213
214
|
|
215
|
+
# Since autocorrect changes a number of lines, and not only the line
|
216
|
+
# where the reported offending range is, we avoid auto-correction if
|
217
|
+
# this cop has already found other offenses is the same
|
218
|
+
# range. Otherwise, two corrections can interfere with each other,
|
219
|
+
# resulting in corrupted code.
|
220
|
+
node = if autocorrect? && other_offense_in_same_range?(body_node)
|
221
|
+
nil
|
222
|
+
else
|
223
|
+
body_node
|
224
|
+
end
|
225
|
+
|
214
226
|
indentation_name = style == 'normal' ? '' : "#{style} "
|
215
|
-
add_offense(
|
227
|
+
add_offense(node, offending_range(body_node, indentation),
|
216
228
|
format("Use #{configured_indentation_width} (not %d) " \
|
217
229
|
"spaces for #{indentation_name}indentation.",
|
218
230
|
indentation))
|
219
231
|
end
|
220
232
|
|
233
|
+
# Returns true if the given node is within another node that has
|
234
|
+
# already been marked for auto-correction by this cop.
|
235
|
+
def other_offense_in_same_range?(node)
|
236
|
+
expr = node.loc.expression
|
237
|
+
@offense_ranges ||= []
|
238
|
+
|
239
|
+
return true if @offense_ranges.any? { |r| within?(expr, r) }
|
240
|
+
|
241
|
+
@offense_ranges << expr
|
242
|
+
false
|
243
|
+
end
|
244
|
+
|
221
245
|
def indentation_to_check?(base_loc, body_node)
|
222
246
|
return false unless body_node
|
223
247
|
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cops checks for indentation of the first non-blank non-comment
|
7
|
+
# line in a file.
|
8
|
+
class InitialIndentation < Cop
|
9
|
+
MSG = 'Indentation of first line in file detected.'
|
10
|
+
|
11
|
+
def investigate(processed_source)
|
12
|
+
first_token = processed_source.tokens.find do |t|
|
13
|
+
!t.text.start_with?('#')
|
14
|
+
end
|
15
|
+
return unless first_token
|
16
|
+
return if first_token.pos.column == 0
|
17
|
+
|
18
|
+
with_space = range_with_surrounding_space(first_token.pos, :left,
|
19
|
+
nil, !:with_newline)
|
20
|
+
space = Parser::Source::Range.new(processed_source.buffer,
|
21
|
+
with_space.begin_pos,
|
22
|
+
first_token.pos.begin_pos)
|
23
|
+
add_offense(space, first_token.pos)
|
24
|
+
end
|
25
|
+
|
26
|
+
def autocorrect(range)
|
27
|
+
->(corrector) { corrector.remove(range) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -7,13 +7,17 @@ module RuboCop
|
|
7
7
|
class MethodCallParentheses < Cop
|
8
8
|
MSG = 'Do not use parentheses for method calls with no arguments.'
|
9
9
|
|
10
|
+
ASGN_NODES = [:lvasgn, :masgn] + Util::SHORTHAND_ASGN_NODES
|
11
|
+
|
10
12
|
def on_send(node)
|
11
13
|
_receiver, method_name, *args = *node
|
12
14
|
|
13
15
|
# methods starting with a capital letter should be skipped
|
14
16
|
return if method_name =~ /\A[A-Z]/
|
17
|
+
return unless args.empty? && node.loc.begin
|
18
|
+
return if same_name_assignment?(node)
|
15
19
|
|
16
|
-
add_offense(node, :begin)
|
20
|
+
add_offense(node, :begin)
|
17
21
|
end
|
18
22
|
|
19
23
|
def autocorrect(node)
|
@@ -22,6 +26,21 @@ module RuboCop
|
|
22
26
|
corrector.remove(node.loc.end)
|
23
27
|
end
|
24
28
|
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def same_name_assignment?(node)
|
33
|
+
_receiver, method_name, *_args = *node
|
34
|
+
|
35
|
+
node.each_ancestor(ASGN_NODES).any? do |asgn_node|
|
36
|
+
if asgn_node.masgn_type?
|
37
|
+
mlhs_node, _mrhs_node = *asgn_node
|
38
|
+
asgn_node = mlhs_node.children[node.sibling_index]
|
39
|
+
end
|
40
|
+
|
41
|
+
asgn_node.loc.name.source == method_name.to_s
|
42
|
+
end
|
43
|
+
end
|
25
44
|
end
|
26
45
|
end
|
27
46
|
end
|
@@ -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
|