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
@@ -41,7 +41,11 @@ module RuboCop
|
|
41
41
|
end
|
42
42
|
|
43
43
|
node.operator_method? || node.setter_method? || chained_calls?(node) ||
|
44
|
-
|
44
|
+
valid_first_argument?(node.first_argument)
|
45
|
+
end
|
46
|
+
|
47
|
+
def valid_first_argument?(first_arg)
|
48
|
+
first_arg.operator_keyword? || first_arg.hash_type? || ternary_expression?(first_arg)
|
45
49
|
end
|
46
50
|
|
47
51
|
def first_argument_starts_with_left_parenthesis?(node)
|
@@ -53,9 +57,8 @@ module RuboCop
|
|
53
57
|
first_argument.send_type? && (node.children.last&.children&.count || 0) > 1
|
54
58
|
end
|
55
59
|
|
56
|
-
def
|
57
|
-
|
58
|
-
first_argument.operator_keyword?
|
60
|
+
def ternary_expression?(node)
|
61
|
+
node.if_type? && node.ternary?
|
59
62
|
end
|
60
63
|
|
61
64
|
def spaces_before_left_parenthesis(node)
|
@@ -46,15 +46,6 @@ module RuboCop
|
|
46
46
|
1
|
47
47
|
end
|
48
48
|
|
49
|
-
def block_method(node)
|
50
|
-
case node.type
|
51
|
-
when :block
|
52
|
-
node.method_name
|
53
|
-
when :block_pass
|
54
|
-
node.parent.method_name
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
49
|
def count_block?(block)
|
59
50
|
KNOWN_ITERATING_METHODS.include? block.method_name
|
60
51
|
end
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
44
44
|
|
45
45
|
# @!method module_definition?(node)
|
46
46
|
def_node_matcher :module_definition?, <<~PATTERN
|
47
|
-
(casgn nil? _ (block (send (const {nil? cbase} :Module) :new) ...))
|
47
|
+
(casgn nil? _ ({block numblock} (send (const {nil? cbase} :Module) :new) ...))
|
48
48
|
PATTERN
|
49
49
|
|
50
50
|
def message(length, max_length)
|
@@ -13,6 +13,11 @@ module RuboCop
|
|
13
13
|
# @api private
|
14
14
|
module EnforceSuperclass
|
15
15
|
def self.included(base)
|
16
|
+
warn Rainbow(
|
17
|
+
'`RuboCop::Cop::EnforceSuperclass` is deprecated and will be removed in RuboCop 2.0. ' \
|
18
|
+
'Please upgrade to RuboCop Rails 2.9 or newer to continue.'
|
19
|
+
).yellow
|
20
|
+
|
16
21
|
# @!method class_definition(node)
|
17
22
|
base.def_node_matcher :class_definition, <<~PATTERN
|
18
23
|
(class (const _ !:#{base::SUPERCLASS}) #{base::BASE_PATTERN} ...)
|
@@ -43,7 +43,7 @@ module RuboCop
|
|
43
43
|
end
|
44
44
|
|
45
45
|
def value_delta(pair)
|
46
|
-
return 0 if pair.value_on_new_line?
|
46
|
+
return 0 if pair.value_on_new_line? || pair.value_omission?
|
47
47
|
|
48
48
|
correct_value_column = pair.loc.operator.end.column + 1
|
49
49
|
actual_value_column = pair.value.loc.column
|
@@ -111,7 +111,8 @@ module RuboCop
|
|
111
111
|
correct_value_column = first_pair.key.loc.column +
|
112
112
|
current_pair.delimiter(true).length +
|
113
113
|
max_key_width
|
114
|
-
|
114
|
+
|
115
|
+
current_pair.value_omission? ? 0 : correct_value_column - current_pair.value.loc.column
|
115
116
|
end
|
116
117
|
end
|
117
118
|
|
@@ -134,7 +135,7 @@ module RuboCop
|
|
134
135
|
end
|
135
136
|
|
136
137
|
def value_delta(first_pair, current_pair)
|
137
|
-
first_pair.value_delta(current_pair)
|
138
|
+
current_pair.value_omission? ? 0 : first_pair.value_delta(current_pair)
|
138
139
|
end
|
139
140
|
end
|
140
141
|
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# This module checks for Ruby 3.1's hash value omission syntax.
|
6
|
+
module HashShorthandSyntax
|
7
|
+
OMIT_HASH_VALUE_MSG = 'Omit the hash value.'
|
8
|
+
EXPLICIT_HASH_VALUE_MSG = 'Explicit the hash value.'
|
9
|
+
|
10
|
+
def on_pair(node)
|
11
|
+
return if target_ruby_version <= 3.0
|
12
|
+
|
13
|
+
hash_key_source = node.key.source
|
14
|
+
|
15
|
+
if enforced_shorthand_syntax == 'always'
|
16
|
+
return if node.value_omission? || require_hash_value?(hash_key_source, node)
|
17
|
+
|
18
|
+
message = OMIT_HASH_VALUE_MSG
|
19
|
+
replacement = "#{hash_key_source}:"
|
20
|
+
else
|
21
|
+
return unless node.value_omission?
|
22
|
+
|
23
|
+
message = EXPLICIT_HASH_VALUE_MSG
|
24
|
+
replacement = "#{hash_key_source}: #{hash_key_source}"
|
25
|
+
end
|
26
|
+
|
27
|
+
add_offense(node.value, message: message) do |corrector|
|
28
|
+
corrector.replace(node, replacement)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def enforced_shorthand_syntax
|
35
|
+
cop_config.fetch('EnforcedShorthandSyntax', 'always')
|
36
|
+
end
|
37
|
+
|
38
|
+
def require_hash_value?(hash_key_source, node)
|
39
|
+
return true if without_parentheses_call_expr_follows?(node)
|
40
|
+
|
41
|
+
hash_value = node.value
|
42
|
+
return true unless hash_value.send_type? || hash_value.lvar_type?
|
43
|
+
|
44
|
+
hash_key_source != hash_value.source || hash_key_source.end_with?('!', '?')
|
45
|
+
end
|
46
|
+
|
47
|
+
def without_parentheses_call_expr_follows?(node)
|
48
|
+
return false unless (ancestor = node.parent.parent)
|
49
|
+
return false unless (right_sibling = ancestor.right_sibling)
|
50
|
+
|
51
|
+
ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized? &&
|
52
|
+
right_sibling.respond_to?(:parenthesized?) && !right_sibling.parenthesized?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Naming
|
6
|
+
# In Ruby 3.1, anonymous block forwarding has been added.
|
7
|
+
#
|
8
|
+
# This cop identifies places where `do_something(&block)` can be replaced
|
9
|
+
# by `do_something(&)`.
|
10
|
+
#
|
11
|
+
# It also supports the opposite style by alternative `explicit` option.
|
12
|
+
#
|
13
|
+
# @example EnforcedStyle: anonymous (default)
|
14
|
+
#
|
15
|
+
# # bad
|
16
|
+
# def foo(&block)
|
17
|
+
# bar(&block)
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# def foo(&)
|
22
|
+
# bar(&)
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# @example EnforcedStyle: explicit
|
26
|
+
#
|
27
|
+
# # bad
|
28
|
+
# def foo(&)
|
29
|
+
# bar(&)
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# def foo(&block)
|
34
|
+
# bar(&block)
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
class BlockForwarding < Base
|
38
|
+
include ConfigurableEnforcedStyle
|
39
|
+
include RangeHelp
|
40
|
+
extend AutoCorrector
|
41
|
+
extend TargetRubyVersion
|
42
|
+
|
43
|
+
minimum_target_ruby_version 3.1
|
44
|
+
|
45
|
+
MSG = 'Use %<style>s block forwarding.'
|
46
|
+
|
47
|
+
def on_def(node)
|
48
|
+
return if node.arguments.empty?
|
49
|
+
|
50
|
+
last_argument = node.arguments.last
|
51
|
+
return if expected_block_forwarding_style?(node, last_argument)
|
52
|
+
|
53
|
+
register_offense(last_argument)
|
54
|
+
|
55
|
+
node.each_descendant(:block_pass) do |block_pass_node|
|
56
|
+
next if block_pass_node.children.first&.sym_type?
|
57
|
+
|
58
|
+
register_offense(block_pass_node)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
alias on_defs on_def
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def expected_block_forwarding_style?(node, last_argument)
|
66
|
+
if style == :anonymous
|
67
|
+
!explicit_block_argument?(last_argument) ||
|
68
|
+
use_kwarg_in_method_definition?(node) ||
|
69
|
+
use_block_argument_as_local_variable?(node, last_argument)
|
70
|
+
else
|
71
|
+
!anonymous_block_argument?(last_argument)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def use_kwarg_in_method_definition?(node)
|
76
|
+
node.arguments.each_descendant(:kwarg, :kwoptarg).any?
|
77
|
+
end
|
78
|
+
|
79
|
+
def anonymous_block_argument?(node)
|
80
|
+
node.blockarg_type? && node.name.nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
def explicit_block_argument?(node)
|
84
|
+
node.blockarg_type? && !node.name.nil?
|
85
|
+
end
|
86
|
+
|
87
|
+
def use_block_argument_as_local_variable?(node, last_argument)
|
88
|
+
return if node.body.nil?
|
89
|
+
|
90
|
+
node.body.each_descendant(:lvar).any? do |lvar|
|
91
|
+
!lvar.parent.block_pass_type? && lvar.source == last_argument.source[1..-1]
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def register_offense(block_argument)
|
96
|
+
add_offense(block_argument, message: format(MSG, style: style)) do |corrector|
|
97
|
+
corrector.replace(block_argument, '&')
|
98
|
+
|
99
|
+
arguments = block_argument.parent
|
100
|
+
|
101
|
+
add_parentheses(arguments, corrector) unless arguments.parenthesized_call?
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
@@ -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 _ {: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
|
+
_ {: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
|
+
_ {: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
|