rubocop 1.23.0 → 1.24.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/README.md +1 -2
- data/config/default.yml +46 -1
- 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/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/comment_indentation.rb +31 -2
- data/lib/rubocop/cop/layout/dot_position.rb +4 -0
- data/lib/rubocop/cop/layout/hash_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/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 +4 -0
- 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 +56 -0
- data/lib/rubocop/cop/naming/block_forwarding.rb +107 -0
- 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 +22 -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/map_to_hash.rb +68 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -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_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/single_line_block_params.rb +2 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/team.rb +1 -1
- data/lib/rubocop/cop/util.rb +9 -1
- 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 +13 -5
@@ -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,10 @@ 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
|
+
#
|
22
26
|
# @example EnforcedStyle: ruby19 (default)
|
23
27
|
# # bad
|
24
28
|
# {:a => 2}
|
@@ -54,8 +58,26 @@ module RuboCop
|
|
54
58
|
# # good
|
55
59
|
# {a: 1, b: 2}
|
56
60
|
# {:c => 3, 'd' => 4}
|
61
|
+
#
|
62
|
+
# @example EnforcedShorthandSyntax: always (default)
|
63
|
+
#
|
64
|
+
# # bad
|
65
|
+
# {foo: foo, bar: bar}
|
66
|
+
#
|
67
|
+
# # good
|
68
|
+
# {foo:, bar:}
|
69
|
+
#
|
70
|
+
# @example EnforcedShorthandSyntax: never
|
71
|
+
#
|
72
|
+
# # bad
|
73
|
+
# {foo:, bar:}
|
74
|
+
#
|
75
|
+
# # good
|
76
|
+
# {foo: foo, bar: bar}
|
77
|
+
#
|
57
78
|
class HashSyntax < Base
|
58
79
|
include ConfigurableEnforcedStyle
|
80
|
+
include HashShorthandSyntax
|
59
81
|
include RangeHelp
|
60
82
|
extend AutoCorrector
|
61
83
|
|
@@ -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
|
@@ -0,0 +1,68 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# This cop looks for uses of `map.to_h` or `collect.to_h` that could be
|
7
|
+
# written with just `to_h` in Ruby >= 2.6.
|
8
|
+
#
|
9
|
+
# NOTE: `Style/HashTransformKeys` and `Style/HashTransformValues` will
|
10
|
+
# also change this pattern if only hash keys or hash values are being
|
11
|
+
# transformed.
|
12
|
+
#
|
13
|
+
# @safety
|
14
|
+
# This cop is unsafe, as it can produce false positives if the receiver
|
15
|
+
# is not an `Enumerable`.
|
16
|
+
#
|
17
|
+
# @example
|
18
|
+
# # bad
|
19
|
+
# something.map { |v| [v, v * 2] }.to_h
|
20
|
+
#
|
21
|
+
# # good
|
22
|
+
# something.to_h { |v| [v, v * 2] }
|
23
|
+
#
|
24
|
+
# # bad
|
25
|
+
# {foo: bar}.collect { |k, v| [k.to_s, v.do_something] }.to_h
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# {foo: bar}.to_h { |k, v| [k.to_s, v.do_something] }
|
29
|
+
#
|
30
|
+
class MapToHash < Base
|
31
|
+
extend AutoCorrector
|
32
|
+
extend TargetRubyVersion
|
33
|
+
include RangeHelp
|
34
|
+
|
35
|
+
minimum_target_ruby_version 2.6
|
36
|
+
|
37
|
+
MSG = 'Pass a block to `to_h` instead of calling `%<method>s.to_h`.'
|
38
|
+
RESTRICT_ON_SEND = %i[to_h].freeze
|
39
|
+
|
40
|
+
# @!method map_to_h?(node)
|
41
|
+
def_node_matcher :map_to_h?, <<~PATTERN
|
42
|
+
$(send (block $(send _ {:map :collect}) ...) :to_h)
|
43
|
+
PATTERN
|
44
|
+
|
45
|
+
def on_send(node)
|
46
|
+
return unless (to_h_node, map_node = map_to_h?(node))
|
47
|
+
|
48
|
+
message = format(MSG, method: map_node.loc.selector.source)
|
49
|
+
add_offense(map_node.loc.selector, message: message) do |corrector|
|
50
|
+
# If the `to_h` call already has a block, do not auto-correct.
|
51
|
+
next if to_h_node.block_node
|
52
|
+
|
53
|
+
autocorrect(corrector, to_h_node, map_node)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
private
|
58
|
+
|
59
|
+
def autocorrect(corrector, to_h, map)
|
60
|
+
removal_range = range_between(to_h.loc.dot.begin_pos, to_h.loc.selector.end_pos)
|
61
|
+
|
62
|
+
corrector.remove(range_with_surrounding_space(range: removal_range, side: :left))
|
63
|
+
corrector.replace(map.loc.selector, 'to_h')
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -13,9 +13,11 @@ module RuboCop
|
|
13
13
|
|
14
14
|
private
|
15
15
|
|
16
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
16
17
|
def omit_parentheses(node)
|
17
18
|
return unless node.parenthesized?
|
18
19
|
return if inside_endless_method_def?(node)
|
20
|
+
return if require_parentheses_for_hash_value_omission?(node)
|
19
21
|
return if syntax_like_method_call?(node)
|
20
22
|
return if super_call_without_arguments?(node)
|
21
23
|
return if allowed_camel_case_method_call?(node)
|
@@ -26,6 +28,7 @@ module RuboCop
|
|
26
28
|
auto_correct(corrector, node)
|
27
29
|
end
|
28
30
|
end
|
31
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
29
32
|
|
30
33
|
def auto_correct(corrector, node)
|
31
34
|
if parentheses_at_the_end_of_multiline_call?(node)
|
@@ -45,6 +48,16 @@ module RuboCop
|
|
45
48
|
node.each_ancestor(:def, :defs).any?(&:endless?) && node.arguments.any?
|
46
49
|
end
|
47
50
|
|
51
|
+
# Require hash value omission be enclosed in parentheses to prevent the following issue:
|
52
|
+
# https://bugs.ruby-lang.org/issues/18396.
|
53
|
+
def require_parentheses_for_hash_value_omission?(node)
|
54
|
+
return unless (last_argument = node.last_argument)
|
55
|
+
|
56
|
+
return false unless (right_sibling = node.right_sibling)
|
57
|
+
|
58
|
+
last_argument.hash_type? && last_argument.pairs.last&.value_omission? && right_sibling
|
59
|
+
end
|
60
|
+
|
48
61
|
def syntax_like_method_call?(node)
|
49
62
|
node.implicit_call? || node.operator_method?
|
50
63
|
end
|
@@ -43,9 +43,11 @@ module RuboCop
|
|
43
43
|
# NOTE: Parentheses are still allowed in cases where omitting them
|
44
44
|
# results in ambiguous or syntactically incorrect code. For example,
|
45
45
|
# parentheses are required around a method with arguments when inside an
|
46
|
-
# endless method definition introduced in Ruby 3.0.
|
46
|
+
# endless method definition introduced in Ruby 3.0. Parentheses are also
|
47
47
|
# allowed when forwarding arguments with the triple-dot syntax introduced
|
48
48
|
# in Ruby 2.7 as omitting them starts an endless range.
|
49
|
+
# And Ruby 3.1's hash omission syntax has a case that requires parentheses
|
50
|
+
# because the issue https://bugs.ruby-lang.org/issues/18396.
|
49
51
|
#
|
50
52
|
# @example EnforcedStyle: require_parentheses (default)
|
51
53
|
#
|
@@ -6,8 +6,13 @@ module RuboCop
|
|
6
6
|
# This cop checks for parentheses around the arguments in method
|
7
7
|
# definitions. Both instance and class/singleton methods are checked.
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
9
|
+
# Regardless of style, parentheses are necessary for:
|
10
|
+
#
|
11
|
+
# 1. Endless methods
|
12
|
+
# 2. Argument lists containing a `forward-arg` (`...`)
|
13
|
+
# 3. Argument lists containing an anonymous block forwarding (`&`)
|
14
|
+
#
|
15
|
+
# Removing the parens would be a syntax error here.
|
11
16
|
#
|
12
17
|
# @example EnforcedStyle: require_parentheses (default)
|
13
18
|
# # The `require_parentheses` style requires method definitions
|
@@ -121,21 +126,13 @@ module RuboCop
|
|
121
126
|
corrector.remove(arg_node.loc.end)
|
122
127
|
end
|
123
128
|
|
124
|
-
def correct_definition(def_node, corrector)
|
125
|
-
arguments_range = def_node.arguments.source_range
|
126
|
-
args_with_space = range_with_surrounding_space(range: arguments_range, side: :left)
|
127
|
-
leading_space = range_between(args_with_space.begin_pos, arguments_range.begin_pos)
|
128
|
-
corrector.replace(leading_space, '(')
|
129
|
-
corrector.insert_after(arguments_range, ')')
|
130
|
-
end
|
131
|
-
|
132
129
|
def forced_parentheses?(node)
|
133
130
|
# Regardless of style, parentheses are necessary for:
|
134
131
|
# 1. Endless methods
|
135
132
|
# 2. Argument lists containing a `forward-arg` (`...`)
|
133
|
+
# 3. Argument lists containing an anonymous block forwarding (`&`)
|
136
134
|
# Removing the parens would be a syntax error here.
|
137
|
-
|
138
|
-
node.endless? || node.arguments.any?(&:forward_arg_type?)
|
135
|
+
node.endless? || node.arguments.any?(&:forward_arg_type?) || anonymous_block_arg?(node)
|
139
136
|
end
|
140
137
|
|
141
138
|
def require_parentheses?(args)
|
@@ -151,7 +148,8 @@ module RuboCop
|
|
151
148
|
location = node.arguments.source_range
|
152
149
|
|
153
150
|
add_offense(location, message: MSG_MISSING) do |corrector|
|
154
|
-
|
151
|
+
add_parentheses(node.arguments, corrector)
|
152
|
+
|
155
153
|
unexpected_style_detected 'require_no_parentheses'
|
156
154
|
end
|
157
155
|
end
|
@@ -163,6 +161,12 @@ module RuboCop
|
|
163
161
|
unexpected_style_detected 'require_parentheses'
|
164
162
|
end
|
165
163
|
end
|
164
|
+
|
165
|
+
def anonymous_block_arg?(node)
|
166
|
+
return false unless (last_argument = node.arguments.last)
|
167
|
+
|
168
|
+
last_argument.blockarg_type? && last_argument.name.nil?
|
169
|
+
end
|
166
170
|
end
|
167
171
|
end
|
168
172
|
end
|
@@ -27,6 +27,11 @@ module RuboCop
|
|
27
27
|
# # bad
|
28
28
|
# 10_000_00 # typical representation of $10,000 in cents
|
29
29
|
#
|
30
|
+
# @example AllowedNumbers: [3000]
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# 3000 # You can specify allowed numbers. (e.g. port number)
|
34
|
+
#
|
30
35
|
class NumericLiterals < Base
|
31
36
|
include IntegerNode
|
32
37
|
extend AutoCorrector
|
@@ -51,9 +56,9 @@ module RuboCop
|
|
51
56
|
|
52
57
|
def check(node)
|
53
58
|
int = integer_part(node)
|
54
|
-
|
55
59
|
# TODO: handle non-decimal literals as well
|
56
60
|
return if int.start_with?('0')
|
61
|
+
return if allowed_numbers.include?(int)
|
57
62
|
return unless int.size >= min_digits
|
58
63
|
|
59
64
|
case int
|
@@ -99,6 +104,10 @@ module RuboCop
|
|
99
104
|
def min_digits
|
100
105
|
cop_config['MinDigits']
|
101
106
|
end
|
107
|
+
|
108
|
+
def allowed_numbers
|
109
|
+
cop_config.fetch('AllowedNumbers', []).map(&:to_s)
|
110
|
+
end
|
102
111
|
end
|
103
112
|
end
|
104
113
|
end
|
@@ -45,7 +45,7 @@ module RuboCop
|
|
45
45
|
|
46
46
|
message = message(node)
|
47
47
|
add_offense(node, message: message) do |corrector|
|
48
|
-
corrector
|
48
|
+
autocorrect(corrector, node)
|
49
49
|
end
|
50
50
|
end
|
51
51
|
|
@@ -55,55 +55,30 @@ module RuboCop
|
|
55
55
|
format(MSG, keyword: node.keyword)
|
56
56
|
end
|
57
57
|
|
58
|
-
def
|
58
|
+
def autocorrect(corrector, node)
|
59
59
|
if always_multiline? || cannot_replace_to_ternary?(node)
|
60
|
-
|
60
|
+
IfThenCorrector.new(node, indentation: indentation_width).call(corrector)
|
61
61
|
else
|
62
|
-
|
63
|
-
return replaced_node unless node.parent
|
64
|
-
return "(#{replaced_node})" if %i[and or].include?(node.parent.type)
|
65
|
-
return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
|
66
|
-
|
67
|
-
replaced_node
|
62
|
+
corrector.replace(node, ternary_correction(node))
|
68
63
|
end
|
69
64
|
end
|
70
65
|
|
71
|
-
def
|
72
|
-
|
73
|
-
end
|
66
|
+
def ternary_correction(node)
|
67
|
+
replaced_node = ternary_replacement(node)
|
74
68
|
|
75
|
-
|
76
|
-
node.
|
77
|
-
|
69
|
+
return replaced_node unless node.parent
|
70
|
+
return "(#{replaced_node})" if %i[and or].include?(node.parent.type)
|
71
|
+
return "(#{replaced_node})" if node.parent.send_type? && node.parent.operator_method?
|
78
72
|
|
79
|
-
|
80
|
-
indentation = ' ' * node.source_range.column if indentation.nil?
|
81
|
-
if_branch_source = node.if_branch&.source || 'nil'
|
82
|
-
elsif_indentation = indentation if node.respond_to?(:elsif?) && node.elsif?
|
83
|
-
if_branch = <<~RUBY
|
84
|
-
#{elsif_indentation}#{node.keyword} #{node.condition.source}
|
85
|
-
#{indentation}#{branch_body_indentation}#{if_branch_source}
|
86
|
-
RUBY
|
87
|
-
else_branch = else_branch_to_multiline(node.else_branch, indentation)
|
88
|
-
if_branch + else_branch
|
73
|
+
replaced_node
|
89
74
|
end
|
90
75
|
|
91
|
-
def
|
92
|
-
|
93
|
-
'end'
|
94
|
-
elsif else_branch.if_type? && else_branch.elsif?
|
95
|
-
multiline_replacement(else_branch, indentation)
|
96
|
-
else
|
97
|
-
<<~RUBY.chomp
|
98
|
-
#{indentation}else
|
99
|
-
#{indentation}#{branch_body_indentation}#{else_branch.source}
|
100
|
-
#{indentation}end
|
101
|
-
RUBY
|
102
|
-
end
|
76
|
+
def always_multiline?
|
77
|
+
@config.for_cop('Style/OneLineConditional')['AlwaysCorrectToMultiline']
|
103
78
|
end
|
104
79
|
|
105
|
-
def
|
106
|
-
|
80
|
+
def cannot_replace_to_ternary?(node)
|
81
|
+
node.elsif_conditional?
|
107
82
|
end
|
108
83
|
|
109
84
|
def ternary_replacement(node)
|
@@ -141,6 +116,10 @@ module RuboCop
|
|
141
116
|
|
142
117
|
node.respond_to?(:arguments?) && node.arguments? && !node.parenthesized_call?
|
143
118
|
end
|
119
|
+
|
120
|
+
def indentation_width
|
121
|
+
@config.for_cop('Layout/IndentationWidth')['Width']
|
122
|
+
end
|
144
123
|
end
|
145
124
|
end
|
146
125
|
end
|
@@ -82,10 +82,20 @@ module RuboCop
|
|
82
82
|
end
|
83
83
|
|
84
84
|
def autocorrect_single_variable_interpolation(corrector, embedded_node, node)
|
85
|
-
|
86
|
-
replacement = "#{variable_loc.expression.source}.to_s"
|
85
|
+
embedded_var = embedded_node.children.first
|
87
86
|
|
88
|
-
|
87
|
+
source = if require_parentheses?(embedded_var)
|
88
|
+
receiver = range_between(
|
89
|
+
embedded_var.loc.expression.begin_pos, embedded_var.loc.selector.end_pos
|
90
|
+
)
|
91
|
+
arguments = embedded_var.arguments.map(&:source).join(', ')
|
92
|
+
|
93
|
+
"#{receiver.source}(#{arguments})"
|
94
|
+
else
|
95
|
+
embedded_var.source
|
96
|
+
end
|
97
|
+
|
98
|
+
corrector.replace(node, "#{source}.to_s")
|
89
99
|
end
|
90
100
|
|
91
101
|
def autocorrect_other(corrector, embedded_node, node)
|
@@ -97,6 +107,10 @@ module RuboCop
|
|
97
107
|
corrector.replace(embedded_loc.begin, '(')
|
98
108
|
corrector.replace(embedded_loc.end, ').to_s')
|
99
109
|
end
|
110
|
+
|
111
|
+
def require_parentheses?(node)
|
112
|
+
node.send_type? && !node.arguments.count.zero? && !node.parenthesized_call?
|
113
|
+
end
|
100
114
|
end
|
101
115
|
end
|
102
116
|
end
|
@@ -80,7 +80,11 @@ module RuboCop
|
|
80
80
|
end
|
81
81
|
|
82
82
|
def without_character_class(loc)
|
83
|
-
loc.source[1..-2]
|
83
|
+
without_character_class = loc.source[1..-2]
|
84
|
+
|
85
|
+
# Adds `\` to prevent auto-correction that changes to an interpolated string when `[#]`.
|
86
|
+
# e.g. From `/[#]{0}/` to `/#{0}/`
|
87
|
+
loc.source == '[#]' ? "\\#{without_character_class}" : without_character_class
|
84
88
|
end
|
85
89
|
|
86
90
|
def whitespace_in_free_space_mode?(node, elem)
|
@@ -218,11 +218,7 @@ module RuboCop
|
|
218
218
|
def find_matching_receiver_invocation(method_chain, checked_variable)
|
219
219
|
return nil unless method_chain
|
220
220
|
|
221
|
-
receiver =
|
222
|
-
method_chain.send_node.receiver
|
223
|
-
else
|
224
|
-
method_chain.receiver
|
225
|
-
end
|
221
|
+
receiver = method_chain.receiver
|
226
222
|
|
227
223
|
return receiver if receiver == checked_variable
|
228
224
|
|