rubocop 1.23.0 → 1.25.1
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/LICENSE.txt +1 -1
- data/README.md +2 -3
- data/config/default.yml +60 -12
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +1 -1
- data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
- data/lib/rubocop/cli/command/show_docs_url.rb +48 -0
- data/lib/rubocop/cli/command/suggest_extensions.rb +1 -1
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config_loader_resolver.rb +1 -1
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +1 -1
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/if_then_corrector.rb +55 -0
- data/lib/rubocop/cop/documentation.rb +19 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +8 -10
- data/lib/rubocop/cop/gemspec/required_ruby_version.rb +10 -3
- data/lib/rubocop/cop/generator.rb +4 -3
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +47 -0
- data/lib/rubocop/cop/internal_affairs/undefined_config.rb +3 -1
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/argument_alignment.rb +36 -9
- data/lib/rubocop/cop/layout/comment_indentation.rb +31 -2
- data/lib/rubocop/cop/layout/dot_position.rb +4 -0
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +5 -1
- data/lib/rubocop/cop/layout/hash_alignment.rb +7 -2
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_colon.rb +1 -1
- data/lib/rubocop/cop/layout/space_before_first_arg.rb +4 -0
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +5 -1
- data/lib/rubocop/cop/lint/constant_definition_in_block.rb +1 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +16 -4
- data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +6 -0
- data/lib/rubocop/cop/lint/each_with_object_argument.rb +1 -1
- data/lib/rubocop/cop/lint/incompatible_io_select_with_fiber_scheduler.rb +10 -5
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +7 -4
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -9
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/metrics/module_length.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/repeated_attribute_discount.rb +1 -1
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +5 -0
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +4 -3
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +82 -0
- data/lib/rubocop/cop/naming/block_forwarding.rb +121 -0
- data/lib/rubocop/cop/naming/method_parameter_name.rb +1 -1
- data/lib/rubocop/cop/security/open.rb +11 -1
- data/lib/rubocop/cop/style/character_literal.rb +8 -1
- data/lib/rubocop/cop/style/collection_compact.rb +31 -13
- data/lib/rubocop/cop/style/combinable_loops.rb +2 -2
- data/lib/rubocop/cop/style/empty_case_condition.rb +10 -0
- data/lib/rubocop/cop/style/file_read.rb +112 -0
- data/lib/rubocop/cop/style/file_write.rb +124 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +2 -1
- data/lib/rubocop/cop/style/hash_syntax.rb +36 -0
- data/lib/rubocop/cop/style/hash_transform_keys.rb +6 -6
- data/lib/rubocop/cop/style/hash_transform_values.rb +6 -6
- data/lib/rubocop/cop/style/if_inside_else.rb +15 -0
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +68 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +3 -1
- data/lib/rubocop/cop/style/method_def_parentheses.rb +17 -13
- data/lib/rubocop/cop/style/numeric_literals.rb +10 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +18 -39
- data/lib/rubocop/cop/style/redundant_begin.rb +2 -6
- data/lib/rubocop/cop/style/redundant_interpolation.rb +17 -3
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +5 -1
- data/lib/rubocop/cop/style/redundant_self.rb +1 -1
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -5
- data/lib/rubocop/cop/style/sample.rb +5 -3
- data/lib/rubocop/cop/style/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/style/swap_values.rb +2 -0
- data/lib/rubocop/cop/style/ternary_parentheses.rb +16 -2
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +9 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +16 -2
- data/lib/rubocop/options.rb +6 -1
- data/lib/rubocop/remote_config.rb +1 -3
- data/lib/rubocop/result_cache.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +7 -0
- metadata +15 -7
@@ -3,7 +3,8 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Security
|
6
|
-
# This cop checks for the use of `Kernel#open` and `URI.open
|
6
|
+
# This cop checks for the use of `Kernel#open` and `URI.open` with dynamic
|
7
|
+
# data.
|
7
8
|
#
|
8
9
|
# `Kernel#open` and `URI.open` enable not only file access but also process
|
9
10
|
# invocation by prefixing a pipe symbol (e.g., `open("| ls")`).
|
@@ -11,6 +12,9 @@ module RuboCop
|
|
11
12
|
# the argument of `Kernel#open` and `URI.open`. It would be better to use
|
12
13
|
# `File.open`, `IO.popen` or `URI.parse#open` explicitly.
|
13
14
|
#
|
15
|
+
# NOTE: `open` and `URI.open` with literal strings are not flagged by this
|
16
|
+
# cop.
|
17
|
+
#
|
14
18
|
# @safety
|
15
19
|
# This cop could register false positives if `open` is redefined
|
16
20
|
# in a class and then used without a receiver in that class.
|
@@ -18,12 +22,18 @@ module RuboCop
|
|
18
22
|
# @example
|
19
23
|
# # bad
|
20
24
|
# open(something)
|
25
|
+
# open("| #{something}")
|
21
26
|
# URI.open(something)
|
22
27
|
#
|
23
28
|
# # good
|
24
29
|
# File.open(something)
|
25
30
|
# IO.popen(something)
|
26
31
|
# URI.parse(something).open
|
32
|
+
#
|
33
|
+
# # good (literal strings)
|
34
|
+
# open("foo.text")
|
35
|
+
# open("| foo")
|
36
|
+
# URI.open("http://example.com")
|
27
37
|
class Open < Base
|
28
38
|
MSG = 'The use of `%<receiver>sopen` is a serious security risk.'
|
29
39
|
RESTRICT_ON_SEND = %i[open].freeze
|
@@ -4,6 +4,12 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
6
|
# Checks for uses of the character literal ?x.
|
7
|
+
# Starting with Ruby 1.9 character literals are
|
8
|
+
# essentially one-character strings, so this syntax
|
9
|
+
# is mostly redundant at this point.
|
10
|
+
#
|
11
|
+
# ? character literal can be used to express meta and control character.
|
12
|
+
# That's a good use case of ? literal so it doesn't count it as an offense.
|
7
13
|
#
|
8
14
|
# @example
|
9
15
|
# # bad
|
@@ -12,8 +18,9 @@ module RuboCop
|
|
12
18
|
# # good
|
13
19
|
# 'x'
|
14
20
|
#
|
15
|
-
# # good
|
21
|
+
# # good - control & meta escapes
|
16
22
|
# ?\C-\M-d
|
23
|
+
# "\C-\M-d" # same as above
|
17
24
|
class CharacterLiteral < Base
|
18
25
|
include StringHelp
|
19
26
|
extend AutoCorrector
|
@@ -16,6 +16,7 @@ module RuboCop
|
|
16
16
|
#
|
17
17
|
# @example
|
18
18
|
# # bad
|
19
|
+
# array.reject(&:nil?)
|
19
20
|
# array.reject { |e| e.nil? }
|
20
21
|
# array.select { |e| !e.nil? }
|
21
22
|
#
|
@@ -23,6 +24,7 @@ module RuboCop
|
|
23
24
|
# array.compact
|
24
25
|
#
|
25
26
|
# # bad
|
27
|
+
# hash.reject!(&:nil?)
|
26
28
|
# hash.reject! { |k, v| v.nil? }
|
27
29
|
# hash.select! { |k, v| !v.nil? }
|
28
30
|
#
|
@@ -37,11 +39,18 @@ module RuboCop
|
|
37
39
|
|
38
40
|
RESTRICT_ON_SEND = %i[reject reject! select select!].freeze
|
39
41
|
|
42
|
+
# @!method reject_method_with_block_pass?(node)
|
43
|
+
def_node_matcher :reject_method_with_block_pass?, <<~PATTERN
|
44
|
+
(send !nil? {:reject :reject!}
|
45
|
+
(block_pass
|
46
|
+
(sym :nil?)))
|
47
|
+
PATTERN
|
48
|
+
|
40
49
|
# @!method reject_method?(node)
|
41
50
|
def_node_matcher :reject_method?, <<~PATTERN
|
42
51
|
(block
|
43
52
|
(send
|
44
|
-
|
53
|
+
!nil? {:reject :reject!})
|
45
54
|
$(args ...)
|
46
55
|
(send
|
47
56
|
$(lvar _) :nil?))
|
@@ -51,7 +60,7 @@ module RuboCop
|
|
51
60
|
def_node_matcher :select_method?, <<~PATTERN
|
52
61
|
(block
|
53
62
|
(send
|
54
|
-
|
63
|
+
!nil? {:select :select!})
|
55
64
|
$(args ...)
|
56
65
|
(send
|
57
66
|
(send
|
@@ -59,16 +68,9 @@ module RuboCop
|
|
59
68
|
PATTERN
|
60
69
|
|
61
70
|
def on_send(node)
|
62
|
-
|
63
|
-
return unless block_node&.block_type?
|
64
|
-
|
65
|
-
return unless (method_name, args, receiver =
|
66
|
-
reject_method?(block_node) || select_method?(block_node))
|
71
|
+
return unless (range = offense_range(node))
|
67
72
|
|
68
|
-
|
69
|
-
|
70
|
-
range = offense_range(node, block_node)
|
71
|
-
good = good_method_name(method_name)
|
73
|
+
good = good_method_name(node.method_name)
|
72
74
|
message = format(MSG, good: good, bad: range.source)
|
73
75
|
|
74
76
|
add_offense(range, message: message) { |corrector| corrector.replace(range, good) }
|
@@ -76,6 +78,22 @@ module RuboCop
|
|
76
78
|
|
77
79
|
private
|
78
80
|
|
81
|
+
def offense_range(node)
|
82
|
+
if reject_method_with_block_pass?(node)
|
83
|
+
range(node, node)
|
84
|
+
else
|
85
|
+
block_node = node.parent
|
86
|
+
|
87
|
+
return unless block_node&.block_type?
|
88
|
+
unless (args, receiver = reject_method?(block_node) || select_method?(block_node))
|
89
|
+
return
|
90
|
+
end
|
91
|
+
return unless args.last.source == receiver.source
|
92
|
+
|
93
|
+
range(node, block_node)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
79
97
|
def good_method_name(method_name)
|
80
98
|
if method_name.to_s.end_with?('!')
|
81
99
|
'compact!'
|
@@ -84,8 +102,8 @@ module RuboCop
|
|
84
102
|
end
|
85
103
|
end
|
86
104
|
|
87
|
-
def
|
88
|
-
range_between(
|
105
|
+
def range(begin_pos_node, end_pos_node)
|
106
|
+
range_between(begin_pos_node.loc.selector.begin_pos, end_pos_node.loc.end.end_pos)
|
89
107
|
end
|
90
108
|
end
|
91
109
|
end
|
@@ -77,14 +77,14 @@ module RuboCop
|
|
77
77
|
|
78
78
|
def collection_looping_method?(node)
|
79
79
|
# TODO: Remove `Symbol#to_s` after supporting only Ruby >= 2.7.
|
80
|
-
method_name = node.
|
80
|
+
method_name = node.method_name.to_s
|
81
81
|
method_name.start_with?('each') || method_name.end_with?('_each')
|
82
82
|
end
|
83
83
|
|
84
84
|
def same_collection_looping?(node, sibling)
|
85
85
|
sibling&.block_type? &&
|
86
86
|
sibling.send_node.method?(node.method_name) &&
|
87
|
-
sibling.
|
87
|
+
sibling.receiver == node.receiver &&
|
88
88
|
sibling.send_node.arguments == node.send_node.arguments
|
89
89
|
end
|
90
90
|
end
|
@@ -79,6 +79,8 @@ module RuboCop
|
|
79
79
|
when_nodes.each do |when_node|
|
80
80
|
conditions = when_node.conditions
|
81
81
|
|
82
|
+
replace_then_with_line_break(corrector, conditions, when_node)
|
83
|
+
|
82
84
|
next unless conditions.size > 1
|
83
85
|
|
84
86
|
range = range_between(conditions.first.source_range.begin_pos,
|
@@ -97,6 +99,14 @@ module RuboCop
|
|
97
99
|
line_beginning = case_range.adjust(begin_pos: -case_range.column)
|
98
100
|
corrector.insert_before(line_beginning, comments)
|
99
101
|
end
|
102
|
+
|
103
|
+
def replace_then_with_line_break(corrector, conditions, when_node)
|
104
|
+
return unless when_node.parent.parent && when_node.then?
|
105
|
+
|
106
|
+
range = range_between(conditions.last.source_range.end_pos, when_node.loc.begin.end_pos)
|
107
|
+
|
108
|
+
corrector.replace(range, "\n")
|
109
|
+
end
|
100
110
|
end
|
101
111
|
end
|
102
112
|
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Favor `File.(bin)read` convenience methods.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# ## text mode
|
10
|
+
# # bad
|
11
|
+
# File.open(filename).read
|
12
|
+
# File.open(filename, &:read)
|
13
|
+
# File.open(filename) { |f| f.read }
|
14
|
+
# File.open(filename) do |f|
|
15
|
+
# f.read
|
16
|
+
# end
|
17
|
+
# File.open(filename, 'r').read
|
18
|
+
# File.open(filename, 'r', &:read)
|
19
|
+
# File.open(filename, 'r') do |f|
|
20
|
+
# f.read
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# File.read(filename)
|
25
|
+
#
|
26
|
+
# @example
|
27
|
+
# ## binary mode
|
28
|
+
# # bad
|
29
|
+
# File.open(filename, 'rb').read
|
30
|
+
# File.open(filename, 'rb', &:read)
|
31
|
+
# File.open(filename, 'rb') do |f|
|
32
|
+
# f.read
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# # good
|
36
|
+
# File.binread(filename)
|
37
|
+
#
|
38
|
+
class FileRead < Base
|
39
|
+
extend AutoCorrector
|
40
|
+
include RangeHelp
|
41
|
+
|
42
|
+
MSG = 'Use `File.%<read_method>s`.'
|
43
|
+
|
44
|
+
RESTRICT_ON_SEND = %i[open].freeze
|
45
|
+
|
46
|
+
READ_FILE_START_TO_FINISH_MODES = %w[r rt rb r+ r+t r+b].to_set.freeze
|
47
|
+
|
48
|
+
# @!method file_open?(node)
|
49
|
+
def_node_matcher :file_open?, <<~PATTERN
|
50
|
+
(send
|
51
|
+
(const {nil? cbase} :File)
|
52
|
+
:open
|
53
|
+
$_
|
54
|
+
(str $%READ_FILE_START_TO_FINISH_MODES)?
|
55
|
+
$(block-pass (sym :read))?
|
56
|
+
)
|
57
|
+
PATTERN
|
58
|
+
|
59
|
+
# @!method send_read?(node)
|
60
|
+
def_node_matcher :send_read?, <<~PATTERN
|
61
|
+
(send _ :read)
|
62
|
+
PATTERN
|
63
|
+
|
64
|
+
# @!method block_read?(node)
|
65
|
+
def_node_matcher :block_read?, <<~PATTERN
|
66
|
+
(block _ (args (arg $_)) (send (lvar $_) :read))
|
67
|
+
PATTERN
|
68
|
+
|
69
|
+
def on_send(node)
|
70
|
+
evidence(node) do |filename, mode, read_node|
|
71
|
+
message = format(MSG, read_method: read_method(mode))
|
72
|
+
|
73
|
+
add_offense(read_node, message: message) do |corrector|
|
74
|
+
range = range_between(node.loc.selector.begin_pos, read_node.loc.expression.end_pos)
|
75
|
+
replacement = "#{read_method(mode)}(#{filename.source})"
|
76
|
+
|
77
|
+
corrector.replace(range, replacement)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def evidence(node)
|
85
|
+
file_open?(node) do |filename, mode_array, block_pass|
|
86
|
+
read_node?(node, block_pass) do |read_node|
|
87
|
+
yield(filename, mode_array.first || 'r', read_node)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def read_node?(node, block_pass)
|
93
|
+
if block_pass.any?
|
94
|
+
yield(node)
|
95
|
+
elsif file_open_read?(node.parent)
|
96
|
+
yield(node.parent)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def file_open_read?(node)
|
101
|
+
return true if send_read?(node)
|
102
|
+
|
103
|
+
block_read?(node) { |block_arg, read_lvar| block_arg == read_lvar }
|
104
|
+
end
|
105
|
+
|
106
|
+
def read_method(mode)
|
107
|
+
mode.end_with?('b') ? :binread : :read
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Favor `File.(bin)write` convenience methods.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# ## text mode
|
10
|
+
# # bad
|
11
|
+
# File.open(filename, 'w').write(content)
|
12
|
+
# File.open(filename, 'w') do |f|
|
13
|
+
# f.write(content)
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# # good
|
17
|
+
# File.write(filename, content)
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# ## binary mode
|
21
|
+
# # bad
|
22
|
+
# File.open(filename, 'wb').write(content)
|
23
|
+
# File.open(filename, 'wb') do |f|
|
24
|
+
# f.write(content)
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# File.binwrite(filename, content)
|
29
|
+
#
|
30
|
+
class FileWrite < Base
|
31
|
+
extend AutoCorrector
|
32
|
+
include RangeHelp
|
33
|
+
|
34
|
+
MSG = 'Use `File.%<write_method>s`.'
|
35
|
+
|
36
|
+
RESTRICT_ON_SEND = %i[open].to_set.freeze
|
37
|
+
|
38
|
+
TRUNCATING_WRITE_MODES = %w[w wt wb w+ w+t w+b].to_set.freeze
|
39
|
+
|
40
|
+
# @!method file_open?(node)
|
41
|
+
def_node_matcher :file_open?, <<~PATTERN
|
42
|
+
(send
|
43
|
+
(const {nil? cbase} :File)
|
44
|
+
:open
|
45
|
+
$_
|
46
|
+
(str $%TRUNCATING_WRITE_MODES)
|
47
|
+
(block-pass (sym :write))?
|
48
|
+
)
|
49
|
+
PATTERN
|
50
|
+
|
51
|
+
# @!method send_write?(node)
|
52
|
+
def_node_matcher :send_write?, <<~PATTERN
|
53
|
+
(send _ :write $_)
|
54
|
+
PATTERN
|
55
|
+
|
56
|
+
# @!method block_write?(node)
|
57
|
+
def_node_matcher :block_write?, <<~PATTERN
|
58
|
+
(block _ (args (arg $_)) (send (lvar $_) :write $_))
|
59
|
+
PATTERN
|
60
|
+
|
61
|
+
def on_send(node)
|
62
|
+
evidence(node) do |filename, mode, content, write_node|
|
63
|
+
message = format(MSG, write_method: write_method(mode))
|
64
|
+
|
65
|
+
add_offense(write_node, message: message) do |corrector|
|
66
|
+
range = range_between(node.loc.selector.begin_pos, write_node.loc.expression.end_pos)
|
67
|
+
replacement = replacement(mode, filename, content, write_node)
|
68
|
+
|
69
|
+
corrector.replace(range, replacement)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def evidence(node)
|
75
|
+
file_open?(node) do |filename, mode|
|
76
|
+
file_open_write?(node.parent) do |content|
|
77
|
+
yield(filename, mode, content, node.parent)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
def file_open_write?(node)
|
85
|
+
content = send_write?(node) || block_write?(node) do |block_arg, lvar, write_arg|
|
86
|
+
write_arg if block_arg == lvar
|
87
|
+
end
|
88
|
+
|
89
|
+
yield(content) if content
|
90
|
+
end
|
91
|
+
|
92
|
+
def write_method(mode)
|
93
|
+
mode.end_with?('b') ? :binwrite : :write
|
94
|
+
end
|
95
|
+
|
96
|
+
def replacement(mode, filename, content, write_node)
|
97
|
+
replacement = "#{write_method(mode)}(#{filename.source}, #{content.source})"
|
98
|
+
|
99
|
+
if heredoc?(write_node)
|
100
|
+
first_argument = write_node.body.first_argument
|
101
|
+
|
102
|
+
<<~REPLACEMENT.chomp
|
103
|
+
#{replacement}
|
104
|
+
#{heredoc_range(first_argument).source}
|
105
|
+
REPLACEMENT
|
106
|
+
else
|
107
|
+
replacement
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def heredoc?(write_node)
|
112
|
+
write_node.block_type? && (first_argument = write_node.body.first_argument) &&
|
113
|
+
first_argument.respond_to?(:heredoc?) && first_argument.heredoc?
|
114
|
+
end
|
115
|
+
|
116
|
+
def heredoc_range(first_argument)
|
117
|
+
range_between(
|
118
|
+
first_argument.loc.heredoc_body.begin_pos, first_argument.loc.heredoc_end.end_pos
|
119
|
+
)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -101,7 +101,8 @@ module RuboCop
|
|
101
101
|
end
|
102
102
|
|
103
103
|
def requires_parens?(node)
|
104
|
-
node.call_type? && node.arguments.any? && !node.parenthesized?
|
104
|
+
(node.call_type? && node.arguments.any? && !node.parenthesized?) ||
|
105
|
+
node.or_type? || node.and_type?
|
105
106
|
end
|
106
107
|
|
107
108
|
def multi_argument(node)
|
@@ -19,6 +19,16 @@ module RuboCop
|
|
19
19
|
# * ruby19_no_mixed_keys - forces use of ruby 1.9 syntax and forbids mixed
|
20
20
|
# syntax hashes
|
21
21
|
#
|
22
|
+
# This cop has `EnforcedShorthandSyntax` option.
|
23
|
+
# It can enforce either the use of the explicit hash value syntax or
|
24
|
+
# the use of Ruby 3.1's hash value shorthand syntax.
|
25
|
+
#
|
26
|
+
# The supported styles are:
|
27
|
+
#
|
28
|
+
# * always - forces use of the 3.1 syntax (e.g. {foo:})
|
29
|
+
# * never - forces use of explicit hash literal value
|
30
|
+
# * either - accepts both shorthand and explicit use of hash literal value
|
31
|
+
#
|
22
32
|
# @example EnforcedStyle: ruby19 (default)
|
23
33
|
# # bad
|
24
34
|
# {:a => 2}
|
@@ -54,8 +64,34 @@ module RuboCop
|
|
54
64
|
# # good
|
55
65
|
# {a: 1, b: 2}
|
56
66
|
# {:c => 3, 'd' => 4}
|
67
|
+
#
|
68
|
+
# @example EnforcedShorthandSyntax: always (default)
|
69
|
+
#
|
70
|
+
# # bad
|
71
|
+
# {foo: foo, bar: bar}
|
72
|
+
#
|
73
|
+
# # good
|
74
|
+
# {foo:, bar:}
|
75
|
+
#
|
76
|
+
# @example EnforcedShorthandSyntax: never
|
77
|
+
#
|
78
|
+
# # bad
|
79
|
+
# {foo:, bar:}
|
80
|
+
#
|
81
|
+
# # good
|
82
|
+
# {foo: foo, bar: bar}
|
83
|
+
#
|
84
|
+
# @example EnforcedShorthandSyntax: either
|
85
|
+
#
|
86
|
+
# # good
|
87
|
+
# {foo: foo, bar: bar}
|
88
|
+
#
|
89
|
+
# # good
|
90
|
+
# {foo:, bar:}
|
91
|
+
#
|
57
92
|
class HashSyntax < Base
|
58
93
|
include ConfigurableEnforcedStyle
|
94
|
+
include HashShorthandSyntax
|
59
95
|
include RangeHelp
|
60
96
|
extend AutoCorrector
|
61
97
|
|
@@ -30,13 +30,13 @@ module RuboCop
|
|
30
30
|
# @!method on_bad_each_with_object(node)
|
31
31
|
def_node_matcher :on_bad_each_with_object, <<~PATTERN
|
32
32
|
(block
|
33
|
-
(
|
33
|
+
(call !#array_receiver? :each_with_object (hash))
|
34
34
|
(args
|
35
35
|
(mlhs
|
36
36
|
(arg $_)
|
37
37
|
(arg _val))
|
38
38
|
(arg _memo))
|
39
|
-
(
|
39
|
+
(call (lvar _memo) :[]= $!`_memo $(lvar _val)))
|
40
40
|
PATTERN
|
41
41
|
|
42
42
|
# @!method on_bad_hash_brackets_map(node)
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
(const _ :Hash)
|
46
46
|
:[]
|
47
47
|
(block
|
48
|
-
(
|
48
|
+
(call !#array_receiver? {:map :collect})
|
49
49
|
(args
|
50
50
|
(arg $_)
|
51
51
|
(arg _val))
|
@@ -54,9 +54,9 @@ module RuboCop
|
|
54
54
|
|
55
55
|
# @!method on_bad_map_to_h(node)
|
56
56
|
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
57
|
-
(
|
57
|
+
(call
|
58
58
|
(block
|
59
|
-
(
|
59
|
+
(call !#array_receiver? {:map :collect})
|
60
60
|
(args
|
61
61
|
(arg $_)
|
62
62
|
(arg _val))
|
@@ -67,7 +67,7 @@ module RuboCop
|
|
67
67
|
# @!method on_bad_to_h(node)
|
68
68
|
def_node_matcher :on_bad_to_h, <<~PATTERN
|
69
69
|
(block
|
70
|
-
(
|
70
|
+
(call !#array_receiver? :to_h)
|
71
71
|
(args
|
72
72
|
(arg $_)
|
73
73
|
(arg _val))
|
@@ -30,13 +30,13 @@ module RuboCop
|
|
30
30
|
# @!method on_bad_each_with_object(node)
|
31
31
|
def_node_matcher :on_bad_each_with_object, <<~PATTERN
|
32
32
|
(block
|
33
|
-
(
|
33
|
+
(call !#array_receiver? :each_with_object (hash))
|
34
34
|
(args
|
35
35
|
(mlhs
|
36
36
|
(arg _key)
|
37
37
|
(arg $_))
|
38
38
|
(arg _memo))
|
39
|
-
(
|
39
|
+
(call (lvar _memo) :[]= $(lvar _key) $!`_memo))
|
40
40
|
PATTERN
|
41
41
|
|
42
42
|
# @!method on_bad_hash_brackets_map(node)
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
(const _ :Hash)
|
46
46
|
:[]
|
47
47
|
(block
|
48
|
-
(
|
48
|
+
(call !#array_receiver? {:map :collect})
|
49
49
|
(args
|
50
50
|
(arg _key)
|
51
51
|
(arg $_))
|
@@ -54,9 +54,9 @@ module RuboCop
|
|
54
54
|
|
55
55
|
# @!method on_bad_map_to_h(node)
|
56
56
|
def_node_matcher :on_bad_map_to_h, <<~PATTERN
|
57
|
-
(
|
57
|
+
(call
|
58
58
|
(block
|
59
|
-
(
|
59
|
+
(call !#array_receiver? {:map :collect})
|
60
60
|
(args
|
61
61
|
(arg _key)
|
62
62
|
(arg $_))
|
@@ -67,7 +67,7 @@ module RuboCop
|
|
67
67
|
# @!method on_bad_to_h(node)
|
68
68
|
def_node_matcher :on_bad_to_h, <<~PATTERN
|
69
69
|
(block
|
70
|
-
(
|
70
|
+
(call !#array_receiver? :to_h)
|
71
71
|
(args
|
72
72
|
(arg _key)
|
73
73
|
(arg $_))
|
@@ -80,11 +80,19 @@ module RuboCop
|
|
80
80
|
private
|
81
81
|
|
82
82
|
def autocorrect(corrector, node)
|
83
|
+
if then?(node)
|
84
|
+
# If the nested `if` is a then node, correct it first,
|
85
|
+
# then the next pass will use `correct_to_elsif_from_if_inside_else_form`
|
86
|
+
IfThenCorrector.new(node, indentation: 0).call(corrector)
|
87
|
+
return
|
88
|
+
end
|
89
|
+
|
83
90
|
if node.modifier_form?
|
84
91
|
correct_to_elsif_from_modifier_form(corrector, node)
|
85
92
|
else
|
86
93
|
correct_to_elsif_from_if_inside_else_form(corrector, node, node.condition)
|
87
94
|
end
|
95
|
+
|
88
96
|
corrector.remove(range_by_whole_lines(find_end_range(node), include_final_newline: true))
|
89
97
|
return unless (if_branch = node.if_branch)
|
90
98
|
|
@@ -102,15 +110,22 @@ module RuboCop
|
|
102
110
|
|
103
111
|
def correct_to_elsif_from_if_inside_else_form(corrector, node, condition)
|
104
112
|
corrector.replace(node.parent.loc.else, "elsif #{condition.source}")
|
113
|
+
|
105
114
|
if_condition_range = if_condition_range(node, condition)
|
115
|
+
|
106
116
|
if (if_branch = node.if_branch)
|
107
117
|
corrector.replace(if_condition_range, if_branch.source)
|
108
118
|
else
|
109
119
|
corrector.remove(range_by_whole_lines(if_condition_range, include_final_newline: true))
|
110
120
|
end
|
121
|
+
|
111
122
|
corrector.remove(condition)
|
112
123
|
end
|
113
124
|
|
125
|
+
def then?(node)
|
126
|
+
node.loc.begin&.source == 'then'
|
127
|
+
end
|
128
|
+
|
114
129
|
def find_end_range(node)
|
115
130
|
end_range = node.loc.end
|
116
131
|
return end_range if end_range
|