rubocop 0.57.2 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 }
|