rubocop 0.38.0 → 0.39.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 +11 -3
- data/config/default.yml +13 -0
- data/config/enabled.yml +15 -3
- data/lib/rubocop.rb +2 -0
- data/lib/rubocop/ast_node/builder.rb +6 -0
- data/lib/rubocop/cli.rb +2 -2
- data/lib/rubocop/config.rb +13 -13
- data/lib/rubocop/config_loader.rb +5 -28
- data/lib/rubocop/config_loader_resolver.rb +42 -0
- data/lib/rubocop/cop/cop.rb +4 -5
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +1 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +9 -2
- data/lib/rubocop/cop/mixin/statement_modifier.rb +5 -4
- data/lib/rubocop/cop/performance/case_when_splat.rb +1 -1
- data/lib/rubocop/cop/performance/count.rb +20 -0
- data/lib/rubocop/cop/performance/detect.rb +12 -0
- data/lib/rubocop/cop/performance/redundant_merge.rb +10 -1
- data/lib/rubocop/cop/performance/times_map.rb +14 -0
- data/lib/rubocop/cop/style/case_indentation.rb +14 -7
- data/lib/rubocop/cop/style/conditional_assignment.rb +221 -20
- data/lib/rubocop/cop/style/file_name.rb +31 -17
- data/lib/rubocop/cop/style/if_unless_modifier.rb +8 -0
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +45 -0
- data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +3 -2
- data/lib/rubocop/cop/style/next.rb +6 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +37 -8
- data/lib/rubocop/cop/style/redundant_parentheses.rb +9 -0
- data/lib/rubocop/cop/style/redundant_self.rb +1 -1
- data/lib/rubocop/cop/style/space_around_keyword.rb +10 -1
- data/lib/rubocop/cop/style/special_global_vars.rb +23 -14
- data/lib/rubocop/cop/style/zero_length_predicate.rb +25 -0
- data/lib/rubocop/formatter/html_formatter.rb +3 -3
- data/lib/rubocop/result_cache.rb +2 -2
- data/lib/rubocop/runner.rb +3 -3
- data/lib/rubocop/target_finder.rb +2 -6
- data/lib/rubocop/version.rb +1 -1
- metadata +6 -4
@@ -21,28 +21,42 @@ module RuboCop
|
|
21
21
|
file_path = processed_source.buffer.name
|
22
22
|
return if config.file_to_include?(file_path)
|
23
23
|
|
24
|
+
for_bad_filename(file_path) do |range, msg|
|
25
|
+
add_offense(nil, range, msg)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def for_bad_filename(file_path)
|
24
32
|
basename = File.basename(file_path)
|
25
|
-
if filename_good?(basename)
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
33
|
+
msg = if filename_good?(basename)
|
34
|
+
return unless expect_matching_definition?
|
35
|
+
return if find_class_or_module(processed_source.ast,
|
36
|
+
to_namespace(file_path))
|
37
|
+
no_definition_message(basename, file_path)
|
38
|
+
else
|
39
|
+
return if cop_config['IgnoreExecutableScripts'] &&
|
40
|
+
shebang?(first_line)
|
41
|
+
other_message(basename)
|
42
|
+
end
|
43
|
+
|
44
|
+
yield source_range(processed_source.buffer, 1, 0), msg
|
45
|
+
end
|
37
46
|
|
38
|
-
|
39
|
-
|
40
|
-
|
47
|
+
def first_line
|
48
|
+
processed_source.lines.first
|
49
|
+
end
|
41
50
|
|
42
|
-
|
51
|
+
def no_definition_message(basename, file_path)
|
52
|
+
format(MSG_NO_DEFINITION,
|
53
|
+
basename,
|
54
|
+
to_namespace(file_path).join('::'))
|
43
55
|
end
|
44
56
|
|
45
|
-
|
57
|
+
def other_message(basename)
|
58
|
+
regex ? format(MSG_REGEX, basename, regex) : MSG_SNAKE_CASE
|
59
|
+
end
|
46
60
|
|
47
61
|
def shebang?(line)
|
48
62
|
line && line.start_with?('#!')
|
@@ -26,6 +26,7 @@ module RuboCop
|
|
26
26
|
return if if_else?(node)
|
27
27
|
return if node.chained?
|
28
28
|
return unless fit_within_line_as_modifier_form?(node)
|
29
|
+
return if nested_conditional?(node)
|
29
30
|
add_offense(node, :keyword, message(node.loc.keyword.source))
|
30
31
|
end
|
31
32
|
|
@@ -64,6 +65,13 @@ module RuboCop
|
|
64
65
|
|
65
66
|
->(corrector) { corrector.replace(node.source_range, oneline) }
|
66
67
|
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
# returns false if the then or else children are conditionals
|
72
|
+
def nested_conditional?(node)
|
73
|
+
node.children[1, 2].any? { |child| child && child.type == :if }
|
74
|
+
end
|
67
75
|
end
|
68
76
|
end
|
69
77
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module RuboCop
|
5
|
+
module Cop
|
6
|
+
module Style
|
7
|
+
# Checks for if and unless statements used as modifers of other if or
|
8
|
+
# unless statements.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
#
|
12
|
+
# # bad
|
13
|
+
# tired? ? 'stop' : 'go faster' if running?
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# if tired?
|
17
|
+
# "please stop"
|
18
|
+
# else
|
19
|
+
# "keep going"
|
20
|
+
# end if running?
|
21
|
+
#
|
22
|
+
# # good
|
23
|
+
# if running?
|
24
|
+
# tired? ? 'stop' : 'go faster'
|
25
|
+
# end
|
26
|
+
class IfUnlessModifierOfIfUnless < Cop
|
27
|
+
include StatementModifier
|
28
|
+
|
29
|
+
MESSAGE = 'Avoid modifier `%s` after another conditional.'.freeze
|
30
|
+
|
31
|
+
def message(keyword)
|
32
|
+
format(MESSAGE, keyword)
|
33
|
+
end
|
34
|
+
|
35
|
+
def on_if(node)
|
36
|
+
return unless modifier_if?(node)
|
37
|
+
_cond, body, _else = if_node_parts(node)
|
38
|
+
if body.type == :if
|
39
|
+
add_offense(node, :keyword, message(node.loc.keyword.source))
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -126,9 +126,10 @@ module RuboCop
|
|
126
126
|
|
127
127
|
def operation_rhs(node)
|
128
128
|
receiver, = *node
|
129
|
-
receiver.each_ancestor
|
129
|
+
receiver.each_ancestor(:send) do |a|
|
130
130
|
_, method, args = *a
|
131
|
-
return args if operator?(method) &&
|
131
|
+
return args if operator?(method) && args &&
|
132
|
+
within_node?(receiver, args)
|
132
133
|
end
|
133
134
|
nil
|
134
135
|
end
|
@@ -146,9 +146,14 @@ module RuboCop
|
|
146
146
|
end
|
147
147
|
|
148
148
|
def cond_range(node, cond)
|
149
|
+
end_pos = if node.loc.begin
|
150
|
+
node.loc.begin.end_pos # after "then"
|
151
|
+
else
|
152
|
+
cond.source_range.end_pos
|
153
|
+
end
|
149
154
|
Parser::Source::Range.new(node.source_range.source_buffer,
|
150
155
|
node.source_range.begin_pos,
|
151
|
-
|
156
|
+
end_pos)
|
152
157
|
end
|
153
158
|
|
154
159
|
def end_range(node)
|
@@ -28,18 +28,47 @@ module RuboCop
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def replacement(node)
|
31
|
+
return ternary(node) unless node.parent
|
32
|
+
return "(#{ternary(node)})" if [:and, :or].include?(node.parent.type)
|
33
|
+
|
34
|
+
if node.parent.send_type? && operator?(node.parent.method_name)
|
35
|
+
return "(#{ternary(node)})"
|
36
|
+
end
|
37
|
+
|
38
|
+
ternary(node)
|
39
|
+
end
|
40
|
+
|
41
|
+
def ternary(node)
|
31
42
|
cond, body, else_clause = *node
|
32
|
-
|
43
|
+
"#{expr_replacement(cond)} ? #{expr_replacement(body)} : " +
|
44
|
+
expr_replacement(else_clause)
|
45
|
+
end
|
46
|
+
|
47
|
+
def expr_replacement(node)
|
48
|
+
requires_parentheses?(node) ? "(#{node.source})" : node.source
|
49
|
+
end
|
33
50
|
|
34
|
-
|
35
|
-
return
|
51
|
+
def requires_parentheses?(node)
|
52
|
+
return true if [:and, :or, :if].include?(node.type)
|
53
|
+
return true if node.assignment?
|
54
|
+
return true if method_call_with_changed_precedence?(node)
|
36
55
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
56
|
+
keyword_with_changed_precedence?(node)
|
57
|
+
end
|
58
|
+
|
59
|
+
def method_call_with_changed_precedence?(node)
|
60
|
+
return false unless node.send_type?
|
61
|
+
return false if node.method_args.empty?
|
62
|
+
return false if parenthesized_call?(node)
|
63
|
+
|
64
|
+
!operator?(node.method_name)
|
65
|
+
end
|
66
|
+
|
67
|
+
def keyword_with_changed_precedence?(node)
|
68
|
+
return false unless node.keyword?
|
69
|
+
return true if node.keyword_not?
|
41
70
|
|
42
|
-
|
71
|
+
!parenthesized_call?(node)
|
43
72
|
end
|
44
73
|
end
|
45
74
|
end
|
@@ -69,6 +69,8 @@ module RuboCop
|
|
69
69
|
offense(begin_node, 'an unary operation')
|
70
70
|
else
|
71
71
|
return unless method_call_with_redundant_parentheses?(node)
|
72
|
+
return if call_chain_starts_with_int?(begin_node, node)
|
73
|
+
|
72
74
|
offense(begin_node, 'a method call')
|
73
75
|
end
|
74
76
|
end
|
@@ -115,6 +117,13 @@ module RuboCop
|
|
115
117
|
_receiver, _method_name, *args = *send_node
|
116
118
|
node.equal?(args.first)
|
117
119
|
end
|
120
|
+
|
121
|
+
def call_chain_starts_with_int?(begin_node, send_node)
|
122
|
+
recv = first_part_of_call_chain(send_node)
|
123
|
+
recv && recv.int_type? && (parent = begin_node.parent) &&
|
124
|
+
parent.send_type? &&
|
125
|
+
(parent.method_name == :-@ || parent.method_name == :+@)
|
126
|
+
end
|
118
127
|
end
|
119
128
|
end
|
120
129
|
end
|
@@ -120,7 +120,7 @@ module RuboCop
|
|
120
120
|
end
|
121
121
|
|
122
122
|
def keyword?(method_name)
|
123
|
-
[:alias, :and, :begin, :break, :case, :class, :def, :defined
|
123
|
+
[:alias, :and, :begin, :break, :case, :class, :def, :defined?, :do,
|
124
124
|
:else, :elsif, :end, :ensure, :false, :for, :if, :in, :module,
|
125
125
|
:next, :nil, :not, :or, :redo, :rescue, :retry, :return, :self,
|
126
126
|
:super, :then, :true, :undef, :unless, :until, :when, :while,
|
@@ -32,6 +32,8 @@ module RuboCop
|
|
32
32
|
DO = 'do'.freeze
|
33
33
|
ACCEPT_LEFT_PAREN =
|
34
34
|
%w(break defined? next not rescue return super yield).freeze
|
35
|
+
ACCEPT_LEFT_SQUARE_BRACKET =
|
36
|
+
%w(super yield).freeze
|
35
37
|
|
36
38
|
def on_and(node)
|
37
39
|
check(node, [:operator].freeze) if node.keyword?
|
@@ -177,7 +179,10 @@ module RuboCop
|
|
177
179
|
pos = range.end_pos
|
178
180
|
char = range.source_buffer.source[pos]
|
179
181
|
return false unless char
|
180
|
-
return false if accept_left_parenthesis?(range) &&
|
182
|
+
return false if accept_left_parenthesis?(range) &&
|
183
|
+
char == '('.freeze
|
184
|
+
return false if accept_left_square_bracket?(range) &&
|
185
|
+
char == '['.freeze
|
181
186
|
|
182
187
|
char !~ /[\s;,#\\\)\}\]\.]/
|
183
188
|
end
|
@@ -186,6 +191,10 @@ module RuboCop
|
|
186
191
|
ACCEPT_LEFT_PAREN.include?(range.source)
|
187
192
|
end
|
188
193
|
|
194
|
+
def accept_left_square_bracket?(range)
|
195
|
+
ACCEPT_LEFT_SQUARE_BRACKET.include?(range.source)
|
196
|
+
end
|
197
|
+
|
189
198
|
def preceded_by_operator?(node, _range)
|
190
199
|
# regular dotted method calls bind more tightly than operators
|
191
200
|
# so we need to climb up the AST past them
|
@@ -104,25 +104,28 @@ module RuboCop
|
|
104
104
|
node.parent.children.one?
|
105
105
|
node = node.parent
|
106
106
|
end
|
107
|
-
|
108
|
-
|
109
|
-
if [:dstr, :xstr, :regexp].include?(parent_type)
|
110
|
-
if style == :use_english_names
|
111
|
-
corrector.replace(node.source_range,
|
112
|
-
"{#{preferred_names(global_var).first}}")
|
113
|
-
else
|
114
|
-
corrector.replace(node.source_range,
|
115
|
-
"##{preferred_names(global_var).first}")
|
116
|
-
end
|
117
|
-
else
|
118
|
-
corrector.replace(node.source_range,
|
119
|
-
preferred_names(global_var).first.to_s)
|
120
|
-
end
|
107
|
+
|
108
|
+
corrector.replace(node.source_range, replacement(node, global_var))
|
121
109
|
end
|
122
110
|
end
|
123
111
|
|
124
112
|
private
|
125
113
|
|
114
|
+
def replacement(node, global_var)
|
115
|
+
parent_type = node.parent && node.parent.type
|
116
|
+
preferred_name = preferred_names(global_var).first
|
117
|
+
|
118
|
+
unless [:dstr, :xstr, :regexp].include?(parent_type)
|
119
|
+
return preferred_name.to_s
|
120
|
+
end
|
121
|
+
|
122
|
+
if style == :use_english_names
|
123
|
+
return english_name_replacement(preferred_name, node)
|
124
|
+
end
|
125
|
+
|
126
|
+
"##{preferred_name}"
|
127
|
+
end
|
128
|
+
|
126
129
|
def preferred_names(global)
|
127
130
|
if style == :use_english_names
|
128
131
|
ENGLISH_VARS[global]
|
@@ -130,6 +133,12 @@ module RuboCop
|
|
130
133
|
PERL_VARS[global]
|
131
134
|
end
|
132
135
|
end
|
136
|
+
|
137
|
+
def english_name_replacement(preferred_name, node)
|
138
|
+
return "\#{#{preferred_name}}" if node.begin_type?
|
139
|
+
|
140
|
+
"{#{preferred_name}}"
|
141
|
+
end
|
133
142
|
end
|
134
143
|
end
|
135
144
|
end
|
@@ -51,6 +51,31 @@ module RuboCop
|
|
51
51
|
{(send (send _ ${:length :size}) ${:> :!=} (int $0))
|
52
52
|
(send (int $0) ${:< :!=} (send _ ${:length :size}))}
|
53
53
|
END
|
54
|
+
|
55
|
+
def autocorrect(node)
|
56
|
+
lambda do |corrector|
|
57
|
+
corrector.replace(node.loc.expression, replacement(node))
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def replacement(node)
|
62
|
+
receiver = zero_length_receiver(node)
|
63
|
+
return "#{receiver.source}.empty?" if receiver
|
64
|
+
|
65
|
+
"!#{other_receiver(node).source}.empty?"
|
66
|
+
end
|
67
|
+
|
68
|
+
def_node_matcher :zero_length_receiver, <<-END
|
69
|
+
{(send (send $_ _) :== (int 0))
|
70
|
+
(send (int 0) :== (send $_ _))
|
71
|
+
(send (send $_ _) :< (int 1))
|
72
|
+
(send (int 1) :> (send $_ _))}
|
73
|
+
END
|
74
|
+
|
75
|
+
def_node_matcher :other_receiver, <<-END
|
76
|
+
{(send (send $_ _) _ _)
|
77
|
+
(send _ _ (send $_ _))}
|
78
|
+
END
|
54
79
|
end
|
55
80
|
end
|
56
81
|
end
|
@@ -9,11 +9,11 @@ require 'rubocop/formatter/text_util'
|
|
9
9
|
|
10
10
|
module RuboCop
|
11
11
|
module Formatter
|
12
|
-
# This formatter saves the output as
|
12
|
+
# This formatter saves the output as an html file.
|
13
13
|
class HTMLFormatter < BaseFormatter
|
14
14
|
ELLIPSES = '<span class="extra-code">...</span>'.freeze
|
15
|
-
TEMPLATE_PATH =
|
16
|
-
|
15
|
+
TEMPLATE_PATH = File.expand_path('../../../../assets/output.html.erb',
|
16
|
+
__FILE__).encode('utf-8')
|
17
17
|
|
18
18
|
Color = Struct.new(:red, :green, :blue, :alpha) do
|
19
19
|
def to_s
|
data/lib/rubocop/result_cache.rb
CHANGED
@@ -24,7 +24,7 @@ module RuboCop
|
|
24
24
|
return unless File.exist?(cache_root)
|
25
25
|
|
26
26
|
files, dirs = Find.find(cache_root).partition { |path| File.file?(path) }
|
27
|
-
if files.length > config_store.for('.')['
|
27
|
+
if files.length > config_store.for('.').for_all_cops['MaxFilesInCache'] &&
|
28
28
|
files.length > 1
|
29
29
|
# Add 1 to half the number of files, so that we remove the file if
|
30
30
|
# there's only 1 left.
|
@@ -51,7 +51,7 @@ module RuboCop
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def self.cache_root(config_store)
|
54
|
-
root = config_store.for('.')['
|
54
|
+
root = config_store.for('.').for_all_cops['CacheRootDirectory']
|
55
55
|
if root == '/tmp'
|
56
56
|
tmpdir = File.realpath(Dir.tmpdir)
|
57
57
|
# Include user ID in the path to make sure the user has write access.
|
data/lib/rubocop/runner.rb
CHANGED
@@ -122,7 +122,7 @@ module RuboCop
|
|
122
122
|
@cached_run ||=
|
123
123
|
(@options[:cache] == 'true' ||
|
124
124
|
@options[:cache] != 'false' &&
|
125
|
-
@config_store.for(Dir.pwd)['
|
125
|
+
@config_store.for(Dir.pwd).for_all_cops['UseCache']) &&
|
126
126
|
# When running --auto-gen-config, there's some processing done in the
|
127
127
|
# cops related to calculating the Max parameters for Metrics cops. We
|
128
128
|
# need to do that processing and can not use caching.
|
@@ -240,7 +240,7 @@ module RuboCop
|
|
240
240
|
end
|
241
241
|
|
242
242
|
def style_guide_cops_only?(config)
|
243
|
-
@options[:only_guide_cops] || config['
|
243
|
+
@options[:only_guide_cops] || config.for_all_cops['StyleGuideCopsOnly']
|
244
244
|
end
|
245
245
|
|
246
246
|
def formatter_set
|
@@ -271,7 +271,7 @@ module RuboCop
|
|
271
271
|
end
|
272
272
|
|
273
273
|
def get_processed_source(file)
|
274
|
-
ruby_version = @config_store.for(file)['
|
274
|
+
ruby_version = @config_store.for(file).for_all_cops['TargetRubyVersion']
|
275
275
|
|
276
276
|
if @options[:stdin]
|
277
277
|
ProcessedSource.new(@options[:stdin], ruby_version, file)
|
@@ -110,7 +110,7 @@ module RuboCop
|
|
110
110
|
end
|
111
111
|
|
112
112
|
def excluded_dirs(base_dir)
|
113
|
-
all_cops_config = @config_store.for(base_dir)
|
113
|
+
all_cops_config = @config_store.for(base_dir).for_all_cops
|
114
114
|
dir_tree_excludes = all_cops_config['Exclude'].select do |pattern|
|
115
115
|
pattern.is_a?(String) && pattern.end_with?('/**/*')
|
116
116
|
end
|
@@ -127,11 +127,7 @@ module RuboCop
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def process_explicit_path(path)
|
130
|
-
files =
|
131
|
-
Dir[path]
|
132
|
-
else
|
133
|
-
[path]
|
134
|
-
end
|
130
|
+
files = path.include?('*') ? Dir[path] : [path]
|
135
131
|
|
136
132
|
return files unless force_exclusion?
|
137
133
|
|