rubocop 0.57.2 → 0.58.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +12 -9
- data/bin/setup +7 -0
- data/config/default.yml +18 -2
- data/config/disabled.yml +4 -0
- data/lib/rubocop.rb +1 -0
- data/lib/rubocop/ast/node.rb +5 -0
- data/lib/rubocop/ast/node/str_node.rb +2 -0
- data/lib/rubocop/cli.rb +4 -7
- data/lib/rubocop/config.rb +4 -4
- data/lib/rubocop/config_loader.rb +4 -8
- data/lib/rubocop/cop/corrector.rb +25 -0
- data/lib/rubocop/cop/layout/closing_heredoc_indentation.rb +3 -7
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +9 -1
- data/lib/rubocop/cop/layout/leading_blank_lines.rb +1 -1
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +28 -51
- data/lib/rubocop/cop/lint/redundant_with_object.rb +1 -1
- data/lib/rubocop/cop/lint/shadowed_argument.rb +7 -3
- data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +1 -1
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +17 -4
- data/lib/rubocop/cop/metrics/line_length.rb +28 -6
- data/lib/rubocop/cop/mixin/check_assignment.rb +0 -2
- data/lib/rubocop/cop/mixin/statement_modifier.rb +6 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +79 -4
- data/lib/rubocop/cop/performance/inefficient_hash_search.rb +9 -5
- data/lib/rubocop/cop/performance/range_include.rb +9 -3
- data/lib/rubocop/cop/performance/sample.rb +6 -4
- data/lib/rubocop/cop/rails/bulk_change_table.rb +11 -7
- data/lib/rubocop/cop/rails/create_table_with_timestamps.rb +3 -1
- data/lib/rubocop/cop/registry.rb +11 -2
- data/lib/rubocop/cop/style/encoding.rb +5 -0
- data/lib/rubocop/cop/style/end_block.rb +8 -0
- data/lib/rubocop/cop/style/if_unless_modifier.rb +2 -1
- data/lib/rubocop/cop/style/ip_addresses.rb +76 -0
- data/lib/rubocop/cop/style/multiple_comparison.rb +16 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +4 -2
- data/lib/rubocop/cop/style/unneeded_condition.rb +19 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +3 -3
- data/lib/rubocop/options.rb +20 -12
- data/lib/rubocop/processed_source.rb +2 -5
- data/lib/rubocop/rspec/cop_helper.rb +0 -4
- data/lib/rubocop/rspec/shared_contexts.rb +0 -4
- data/lib/rubocop/rspec/shared_examples.rb +0 -23
- data/lib/rubocop/version.rb +1 -1
- metadata +7 -11
@@ -59,7 +59,9 @@ module RuboCop
|
|
59
59
|
PATTERN
|
60
60
|
|
61
61
|
def_node_search :created_at_or_updated_at_included?, <<-PATTERN
|
62
|
-
(send _var :datetime
|
62
|
+
(send _var :datetime
|
63
|
+
{(sym {:created_at :updated_at})(str {"created_at" "updated_at"})}
|
64
|
+
...)
|
63
65
|
PATTERN
|
64
66
|
|
65
67
|
def on_send(node)
|
data/lib/rubocop/cop/registry.rb
CHANGED
@@ -23,8 +23,9 @@ module RuboCop
|
|
23
23
|
# Registry that tracks all cops by their badge and department.
|
24
24
|
class Registry
|
25
25
|
def initialize(cops = [])
|
26
|
-
@registry
|
26
|
+
@registry = {}
|
27
27
|
@departments = {}
|
28
|
+
@cops_by_cop_name = Hash.new { |hash, key| hash[key] = [] }
|
28
29
|
|
29
30
|
cops.each { |cop| enlist(cop) }
|
30
31
|
end
|
@@ -33,6 +34,7 @@ module RuboCop
|
|
33
34
|
@registry[cop.badge] = cop
|
34
35
|
@departments[cop.department] ||= []
|
35
36
|
@departments[cop.department] << cop
|
37
|
+
@cops_by_cop_name[cop.cop_name] << cop
|
36
38
|
end
|
37
39
|
|
38
40
|
# @return [Array<Symbol>] list of departments for current cops.
|
@@ -102,8 +104,9 @@ module RuboCop
|
|
102
104
|
end
|
103
105
|
end
|
104
106
|
|
107
|
+
# @return [Hash{String => Array<Class>}]
|
105
108
|
def to_h
|
106
|
-
|
109
|
+
@cops_by_cop_name
|
107
110
|
end
|
108
111
|
|
109
112
|
def cops
|
@@ -142,6 +145,12 @@ module RuboCop
|
|
142
145
|
cops.each(&block)
|
143
146
|
end
|
144
147
|
|
148
|
+
# @param [String] cop_name
|
149
|
+
# @return [Class, nil]
|
150
|
+
def find_by_cop_name(cop_name)
|
151
|
+
@cops_by_cop_name[cop_name].first
|
152
|
+
end
|
153
|
+
|
145
154
|
private
|
146
155
|
|
147
156
|
def with(cops)
|
@@ -4,6 +4,14 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
6
|
# This cop checks for END blocks.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# # bad
|
10
|
+
# END { puts 'Goodbye!' }
|
11
|
+
#
|
12
|
+
# # good
|
13
|
+
# at_exit { puts 'Goodbye!' }
|
14
|
+
#
|
7
15
|
class EndBlock < Cop
|
8
16
|
MSG = 'Avoid the use of `END` blocks. ' \
|
9
17
|
'Use `Kernel#at_exit` instead.'.freeze
|
@@ -5,7 +5,8 @@ module RuboCop
|
|
5
5
|
module Style
|
6
6
|
# Checks for if and unless statements that would fit on one line
|
7
7
|
# if written as a modifier if/unless. The maximum line length is
|
8
|
-
# configured in the `Metrics/LineLength` cop.
|
8
|
+
# configured in the `Metrics/LineLength` cop. The tab size is configured
|
9
|
+
# in the `IndentationWidth` of the `Layout/Tab` cop.
|
9
10
|
#
|
10
11
|
# @example
|
11
12
|
# # bad
|
@@ -0,0 +1,76 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'resolv'
|
4
|
+
|
5
|
+
module RuboCop
|
6
|
+
module Cop
|
7
|
+
module Style
|
8
|
+
# This cop checks for hardcoded IP addresses, which can make code
|
9
|
+
# brittle. IP addresses are likely to need to be changed when code
|
10
|
+
# is deployed to a different server or environment, which may break
|
11
|
+
# a deployment if forgotten. Prefer setting IP addresses in ENV or
|
12
|
+
# other configuration.
|
13
|
+
#
|
14
|
+
# @example
|
15
|
+
#
|
16
|
+
# # bad
|
17
|
+
# ip_address = '127.59.241.29'
|
18
|
+
#
|
19
|
+
# # good
|
20
|
+
# ip_address = ENV['DEPLOYMENT_IP_ADDRESS']
|
21
|
+
class IpAddresses < Cop
|
22
|
+
include StringHelp
|
23
|
+
|
24
|
+
IPV6_MAX_SIZE = 45 # IPv4-mapped IPv6 is the longest
|
25
|
+
MSG = 'Do not hardcode IP addresses.'.freeze
|
26
|
+
|
27
|
+
def offense?(node)
|
28
|
+
contents = node.source[1...-1]
|
29
|
+
return false if contents.empty?
|
30
|
+
|
31
|
+
return false if whitelist.include?(contents.downcase)
|
32
|
+
|
33
|
+
# To try to avoid doing two regex checks on every string,
|
34
|
+
# shortcut out if the string does not look like an IP address
|
35
|
+
return false unless could_be_ip?(contents)
|
36
|
+
|
37
|
+
contents =~ ::Resolv::IPv4::Regex || contents =~ ::Resolv::IPv6::Regex
|
38
|
+
end
|
39
|
+
|
40
|
+
# Dummy implementation of method in ConfigurableEnforcedStyle that is
|
41
|
+
# called from StringHelp.
|
42
|
+
def opposite_style_detected; end
|
43
|
+
|
44
|
+
# Dummy implementation of method in ConfigurableEnforcedStyle that is
|
45
|
+
# called from StringHelp.
|
46
|
+
def correct_style_detected; end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def whitelist
|
51
|
+
whitelist = cop_config['Whitelist']
|
52
|
+
Array(whitelist).map(&:downcase)
|
53
|
+
end
|
54
|
+
|
55
|
+
def could_be_ip?(str)
|
56
|
+
# If the string is too long, it can't be an IP
|
57
|
+
return false if too_long?(str)
|
58
|
+
|
59
|
+
# If the string doesn't start with a colon or hexadecimal char,
|
60
|
+
# we know it's not an IP address
|
61
|
+
starts_with_hex_or_colon?(str)
|
62
|
+
end
|
63
|
+
|
64
|
+
def too_long?(str)
|
65
|
+
str.size > IPV6_MAX_SIZE
|
66
|
+
end
|
67
|
+
|
68
|
+
def starts_with_hex_or_colon?(str)
|
69
|
+
first_char = str[0].ord
|
70
|
+
(48..58).cover?(first_char) || (65..70).cover?(first_char) ||
|
71
|
+
(97..102).cover?(first_char)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -18,8 +18,12 @@ module RuboCop
|
|
18
18
|
MSG = 'Avoid comparing a variable with multiple items ' \
|
19
19
|
'in a conditional, use `Array#include?` instead.'.freeze
|
20
20
|
|
21
|
-
def
|
22
|
-
|
21
|
+
def on_or(node)
|
22
|
+
root_of_or_node = root_of_or_node(node)
|
23
|
+
|
24
|
+
return unless node == root_of_or_node
|
25
|
+
return unless nested_variable_comparison?(root_of_or_node)
|
26
|
+
|
23
27
|
add_offense(node)
|
24
28
|
end
|
25
29
|
|
@@ -71,6 +75,16 @@ module RuboCop
|
|
71
75
|
def comparison?(node)
|
72
76
|
simple_comparison?(node) || nested_comparison?(node)
|
73
77
|
end
|
78
|
+
|
79
|
+
def root_of_or_node(or_node)
|
80
|
+
return or_node unless or_node.parent
|
81
|
+
|
82
|
+
if or_node.parent.or_type?
|
83
|
+
root_of_or_node(or_node.parent)
|
84
|
+
else
|
85
|
+
or_node
|
86
|
+
end
|
87
|
+
end
|
74
88
|
end
|
75
89
|
end
|
76
90
|
end
|
@@ -22,7 +22,7 @@ module RuboCop
|
|
22
22
|
def_node_matcher :symbol_proc?, <<-PATTERN
|
23
23
|
(block
|
24
24
|
${(send ...) (super ...) zsuper}
|
25
|
-
(args (arg _var))
|
25
|
+
$(args (arg _var))
|
26
26
|
(send (lvar _var) $_))
|
27
27
|
PATTERN
|
28
28
|
|
@@ -31,7 +31,7 @@ module RuboCop
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def on_block(node)
|
34
|
-
symbol_proc?(node) do |send_or_super, method|
|
34
|
+
symbol_proc?(node) do |send_or_super, block_args, method|
|
35
35
|
block_method_name = resolve_block_method_name(send_or_super)
|
36
36
|
|
37
37
|
# TODO: Rails-specific handling that we should probably make
|
@@ -40,6 +40,8 @@ module RuboCop
|
|
40
40
|
return if proc_node?(send_or_super)
|
41
41
|
return if %i[lambda proc].include?(block_method_name)
|
42
42
|
return if ignored_method?(block_method_name)
|
43
|
+
return if block_args.children.size == 1 &&
|
44
|
+
block_args.source.include?(',')
|
43
45
|
|
44
46
|
offense(node, method, block_method_name)
|
45
47
|
end
|
@@ -34,9 +34,12 @@ module RuboCop
|
|
34
34
|
include RangeHelp
|
35
35
|
|
36
36
|
MSG = 'Use double pipes `||` instead.'.freeze
|
37
|
+
UNNEEDED_CONDITION = 'This condition is not needed.'.freeze
|
37
38
|
|
38
39
|
def on_if(node)
|
40
|
+
return if node.elsif_conditional?
|
39
41
|
return unless offense?(node)
|
42
|
+
|
40
43
|
add_offense(node, location: range_of_offense(node))
|
41
44
|
end
|
42
45
|
|
@@ -44,6 +47,8 @@ module RuboCop
|
|
44
47
|
lambda do |corrector|
|
45
48
|
if node.ternary?
|
46
49
|
corrector.replace(range_of_offense(node), '||')
|
50
|
+
elsif node.modifier_form?
|
51
|
+
corrector.replace(node.source_range, node.if_branch.source)
|
47
52
|
else
|
48
53
|
corrected = [node.if_branch.source,
|
49
54
|
else_source(node.else_branch)].join(' || ')
|
@@ -55,16 +60,24 @@ module RuboCop
|
|
55
60
|
|
56
61
|
private
|
57
62
|
|
63
|
+
def message(node)
|
64
|
+
if node.modifier_form?
|
65
|
+
UNNEEDED_CONDITION
|
66
|
+
else
|
67
|
+
MSG
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
58
71
|
def range_of_offense(node)
|
59
72
|
return :expression unless node.ternary?
|
60
73
|
range_between(node.loc.question.begin_pos, node.loc.colon.end_pos)
|
61
74
|
end
|
62
75
|
|
63
76
|
def offense?(node)
|
64
|
-
return false if node.elsif_conditional?
|
65
|
-
|
66
77
|
condition, if_branch, else_branch = *node
|
67
78
|
|
79
|
+
return false if use_if_branch?(else_branch)
|
80
|
+
|
68
81
|
condition == if_branch && !node.elsif? && (
|
69
82
|
node.ternary? ||
|
70
83
|
!else_branch.instance_of?(AST::Node) ||
|
@@ -72,6 +85,10 @@ module RuboCop
|
|
72
85
|
)
|
73
86
|
end
|
74
87
|
|
88
|
+
def use_if_branch?(else_branch)
|
89
|
+
else_branch && else_branch.if_type?
|
90
|
+
end
|
91
|
+
|
75
92
|
def else_source(else_branch)
|
76
93
|
wrap_else = MODIFIER_NODES.include?(else_branch.type) &&
|
77
94
|
else_branch.modifier_form?
|
@@ -18,8 +18,6 @@ module RuboCop
|
|
18
18
|
@config_to_allow_offenses = {}
|
19
19
|
@detected_styles = {}
|
20
20
|
|
21
|
-
COPS = Cop::Cop.registry.to_h
|
22
|
-
|
23
21
|
class << self
|
24
22
|
attr_accessor :config_to_allow_offenses, :detected_styles
|
25
23
|
end
|
@@ -101,7 +99,9 @@ module RuboCop
|
|
101
99
|
if @show_offense_counts
|
102
100
|
output_buffer.puts "# Offense count: #{offense_count}"
|
103
101
|
end
|
104
|
-
|
102
|
+
|
103
|
+
cop_class = Cop::Cop.registry.find_by_cop_name(cop_name)
|
104
|
+
if cop_class && cop_class.new.support_autocorrect?
|
105
105
|
output_buffer.puts '# Cop supports --auto-correct.'
|
106
106
|
end
|
107
107
|
|
data/lib/rubocop/options.rb
CHANGED
@@ -5,6 +5,7 @@ require 'shellwords'
|
|
5
5
|
|
6
6
|
module RuboCop
|
7
7
|
class IncorrectCopNameError < StandardError; end
|
8
|
+
class OptionArgumentError < StandardError; end
|
8
9
|
|
9
10
|
# This class handles command line options.
|
10
11
|
class Options
|
@@ -26,7 +27,7 @@ module RuboCop
|
|
26
27
|
# The parser has put the file name given after --stdin into
|
27
28
|
# @options[:stdin]. The args array should be empty.
|
28
29
|
if args.any?
|
29
|
-
raise
|
30
|
+
raise OptionArgumentError, '-s/--stdin requires exactly one path.'
|
30
31
|
end
|
31
32
|
# We want the STDIN contents in @options[:stdin] and the file name in
|
32
33
|
# args to simplify the rest of the processing.
|
@@ -241,21 +242,21 @@ module RuboCop
|
|
241
242
|
|
242
243
|
def validate_compatibility # rubocop:disable Metrics/MethodLength
|
243
244
|
if only_includes_unneeded_disable?
|
244
|
-
raise
|
245
|
-
|
245
|
+
raise OptionArgumentError, 'Lint/UnneededCopDisableDirective can not ' \
|
246
|
+
'be used with --only.'
|
246
247
|
end
|
247
248
|
if except_syntax?
|
248
|
-
raise
|
249
|
+
raise OptionArgumentError, 'Syntax checking can not be turned off.'
|
249
250
|
end
|
250
251
|
unless boolean_or_empty_cache?
|
251
|
-
raise
|
252
|
+
raise OptionArgumentError, '-C/--cache argument must be true or false'
|
252
253
|
end
|
253
254
|
validate_auto_gen_config
|
254
255
|
validate_parallel
|
255
256
|
|
256
257
|
return if incompatible_options.size <= 1
|
257
|
-
raise
|
258
|
-
|
258
|
+
raise OptionArgumentError, 'Incompatible cli options: ' \
|
259
|
+
"#{incompatible_options.inspect}"
|
259
260
|
end
|
260
261
|
|
261
262
|
def validate_auto_gen_config
|
@@ -265,7 +266,8 @@ module RuboCop
|
|
265
266
|
|
266
267
|
%i[exclude_limit no_offense_counts no_auto_gen_timestamp].each do |option|
|
267
268
|
if @options.key?(option)
|
268
|
-
raise
|
269
|
+
raise OptionArgumentError,
|
270
|
+
format(message, flag: option.to_s.tr('_', '-'))
|
269
271
|
end
|
270
272
|
end
|
271
273
|
end
|
@@ -274,11 +276,15 @@ module RuboCop
|
|
274
276
|
return unless @options.key?(:parallel)
|
275
277
|
|
276
278
|
if @options[:cache] == 'false'
|
277
|
-
raise
|
278
|
-
|
279
|
-
|
279
|
+
raise OptionArgumentError, '-P/--parallel uses caching to speed up ' \
|
280
|
+
'execution, so combining with --cache ' \
|
281
|
+
'false is not allowed.'
|
280
282
|
end
|
281
283
|
|
284
|
+
validate_parallel_with_combo_option
|
285
|
+
end
|
286
|
+
|
287
|
+
def validate_parallel_with_combo_option
|
282
288
|
combos = {
|
283
289
|
auto_gen_config: '-P/--parallel uses caching to speed up execution, ' \
|
284
290
|
'while --auto-gen-config needs a non-cached run, ' \
|
@@ -287,7 +293,9 @@ module RuboCop
|
|
287
293
|
auto_correct: '-P/--parallel can not be combined with --auto-correct.'
|
288
294
|
}
|
289
295
|
|
290
|
-
combos.each
|
296
|
+
combos.each do |key, msg|
|
297
|
+
raise OptionArgumentError, msg if @options.key?(key)
|
298
|
+
end
|
291
299
|
end
|
292
300
|
|
293
301
|
def only_includes_unneeded_disable?
|
@@ -163,12 +163,9 @@ module RuboCop
|
|
163
163
|
[ast, comments, tokens]
|
164
164
|
end
|
165
165
|
|
166
|
-
# rubocop:disable Metrics/
|
166
|
+
# rubocop:disable Metrics/MethodLength
|
167
167
|
def parser_class(ruby_version)
|
168
168
|
case ruby_version
|
169
|
-
when 2.1
|
170
|
-
require 'parser/ruby21'
|
171
|
-
Parser::Ruby21
|
172
169
|
when 2.2
|
173
170
|
require 'parser/ruby22'
|
174
171
|
Parser::Ruby22
|
@@ -188,7 +185,7 @@ module RuboCop
|
|
188
185
|
raise ArgumentError, "Unknown Ruby version: #{ruby_version.inspect}"
|
189
186
|
end
|
190
187
|
end
|
191
|
-
# rubocop:enable Metrics/
|
188
|
+
# rubocop:enable Metrics/MethodLength
|
192
189
|
|
193
190
|
def create_parser(ruby_version)
|
194
191
|
builder = RuboCop::AST::Builder.new
|
@@ -14,10 +14,6 @@ module CopHelper
|
|
14
14
|
Tempfile.open('tmp') { |f| inspect_source(source, f) }
|
15
15
|
end
|
16
16
|
|
17
|
-
def inspect_gemfile(source)
|
18
|
-
inspect_source(source, 'Gemfile')
|
19
|
-
end
|
20
|
-
|
21
17
|
def inspect_source(source, file = nil)
|
22
18
|
if source.is_a?(Array) && source.size == 1
|
23
19
|
raise "Don't use an array for a single line of code: #{source}"
|
@@ -9,29 +9,6 @@ shared_examples_for 'accepts' do
|
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
|
-
shared_examples_for 'mimics MRI 2.1' do |grep_mri_warning|
|
13
|
-
if RUBY_ENGINE == 'ruby' && RUBY_VERSION.start_with?('2.1')
|
14
|
-
it "mimics MRI #{RUBY_VERSION} built-in syntax checking" do
|
15
|
-
inspect_source(source)
|
16
|
-
offenses_by_mri = MRISyntaxChecker.offenses_for_source(
|
17
|
-
source, cop.name, grep_mri_warning
|
18
|
-
)
|
19
|
-
|
20
|
-
# Compare objects before comparing counts for clear failure output.
|
21
|
-
cop.offenses.each_with_index do |offense_by_cop, index|
|
22
|
-
offense_by_mri = offenses_by_mri[index]
|
23
|
-
# Exclude column attribute since MRI does not
|
24
|
-
# output column number.
|
25
|
-
%i[severity line cop_name].each do |a|
|
26
|
-
expect(offense_by_cop.send(a)).to eq(offense_by_mri.send(a))
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
expect(cop.offenses.count).to eq(offenses_by_mri.count)
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
12
|
shared_examples_for 'misaligned' do |annotated_source, used_style|
|
36
13
|
config_to_allow_offenses = if used_style
|
37
14
|
{ 'EnforcedStyleAlignWith' => used_style.to_s }
|