rubocop 1.31.2 → 1.32.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 +1 -1
- data/config/default.yml +23 -0
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +7 -1
- data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +57 -13
- data/lib/rubocop/cop/layout/line_length.rb +2 -0
- data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +4 -1
- data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +45 -0
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +7 -0
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -4
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +55 -24
- data/lib/rubocop/cop/lint/number_conversion.rb +7 -1
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +7 -0
- data/lib/rubocop/cop/lint/require_range_parentheses.rb +57 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +35 -1
- data/lib/rubocop/cop/metrics/block_length.rb +1 -0
- data/lib/rubocop/cop/metrics/method_length.rb +1 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +4 -0
- data/lib/rubocop/cop/mixin/percent_array.rb +60 -1
- data/lib/rubocop/cop/naming/predicate_name.rb +8 -0
- data/lib/rubocop/cop/style/class_equality_comparison.rb +22 -0
- data/lib/rubocop/cop/style/empty_else.rb +37 -0
- data/lib/rubocop/cop/style/empty_heredoc.rb +59 -0
- data/lib/rubocop/cop/style/fetch_env_var.rb +10 -177
- data/lib/rubocop/cop/style/format_string_token.rb +6 -0
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -0
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +12 -0
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +9 -0
- data/lib/rubocop/cop/style/numeric_predicate.rb +20 -6
- data/lib/rubocop/cop/style/semicolon.rb +27 -3
- data/lib/rubocop/cop/style/symbol_array.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +9 -1
- data/lib/rubocop/cop/style/top_level_method_definition.rb +2 -0
- data/lib/rubocop/cop/style/trivial_accessors.rb +3 -0
- data/lib/rubocop/cop/style/word_array.rb +2 -3
- data/lib/rubocop/options.rb +3 -6
- data/lib/rubocop/rspec/shared_contexts.rb +14 -14
- data/lib/rubocop/rspec/support.rb +14 -0
- data/lib/rubocop/runner.rb +4 -0
- data/lib/rubocop/server/client_command/base.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +3 -0
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 270faf06487f8a915ef6293944db136da6fb51ec09b5613be4b848a471c858bb
|
4
|
+
data.tar.gz: 1c61746959019453862c2e90b8d3250ce3f9815b1eae286f1fd5b73fb267a1cb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 86966e7ff42d0f514a447c8313cc9bdc13020ec1d375e584dba8ebae5bb535a1238380a57d55dfd697c735b3f9259f59939563f4b504f08570304e4ec1c2c7af
|
7
|
+
data.tar.gz: ce35c98b8b51b8b59c6c2aeac880a0618067a68f7080a2e44408a7e283471b2b57f84e57c127144632b61eaa8edf6e4369d4702c2baa222cb3b37fad38606bd5
|
data/README.md
CHANGED
@@ -53,7 +53,7 @@ To prevent an unwanted RuboCop update you might want to use a conservative versi
|
|
53
53
|
in your `Gemfile`:
|
54
54
|
|
55
55
|
```rb
|
56
|
-
gem 'rubocop', '~> 1.
|
56
|
+
gem 'rubocop', '~> 1.32', require: false
|
57
57
|
```
|
58
58
|
|
59
59
|
See [our versioning policy](https://docs.rubocop.org/rubocop/versioning.html) for further details.
|
data/config/default.yml
CHANGED
@@ -977,6 +977,11 @@ Layout/LineContinuationLeadingSpace:
|
|
977
977
|
AutoCorrect: false
|
978
978
|
SafeAutoCorrect: false
|
979
979
|
VersionAdded: '1.31'
|
980
|
+
VersionChanged: '1.32'
|
981
|
+
EnforcedStyle: trailing
|
982
|
+
SupportedStyles:
|
983
|
+
- leading
|
984
|
+
- trailing
|
980
985
|
|
981
986
|
Layout/LineContinuationSpacing:
|
982
987
|
Description: 'Checks the spacing in front of backslash in line continuations.'
|
@@ -1153,6 +1158,13 @@ Layout/MultilineMethodDefinitionBraceLayout:
|
|
1153
1158
|
- new_line
|
1154
1159
|
- same_line
|
1155
1160
|
|
1161
|
+
Layout/MultilineMethodParameterLineBreaks:
|
1162
|
+
Description: >-
|
1163
|
+
Checks that each parameter in a multi-line method definition
|
1164
|
+
starts on a separate line.
|
1165
|
+
Enabled: false
|
1166
|
+
VersionAdded: '1.32'
|
1167
|
+
|
1156
1168
|
Layout/MultilineOperationIndentation:
|
1157
1169
|
Description: >-
|
1158
1170
|
Checks indentation of binary operations that span more than
|
@@ -2128,6 +2140,11 @@ Lint/RequireParentheses:
|
|
2128
2140
|
Enabled: true
|
2129
2141
|
VersionAdded: '0.18'
|
2130
2142
|
|
2143
|
+
Lint/RequireRangeParentheses:
|
2144
|
+
Description: 'Checks that a range literal is enclosed in parentheses when the end of the range is at a line break.'
|
2145
|
+
Enabled: pending
|
2146
|
+
VersionAdded: '1.32'
|
2147
|
+
|
2131
2148
|
Lint/RequireRelativeSelfPath:
|
2132
2149
|
Description: 'Checks for uses a file requiring itself with `require_relative`.'
|
2133
2150
|
Enabled: pending
|
@@ -3495,6 +3512,12 @@ Style/EmptyElse:
|
|
3495
3512
|
- empty
|
3496
3513
|
- nil
|
3497
3514
|
- both
|
3515
|
+
AllowComments: false
|
3516
|
+
|
3517
|
+
Style/EmptyHeredoc:
|
3518
|
+
Description: 'Checks for using empty heredoc to reduce redundancy.'
|
3519
|
+
Enabled: pending
|
3520
|
+
VersionAdded: '1.32'
|
3498
3521
|
|
3499
3522
|
Style/EmptyLambdaParameter:
|
3500
3523
|
Description: 'Omit parens for empty lambda parameters.'
|
data/lib/rubocop/cli.rb
CHANGED
@@ -12,6 +12,7 @@ module RuboCop
|
|
12
12
|
color debug display_style_guide display_time display_only_fail_level_offenses
|
13
13
|
display_only_failed except extra_details fail_level fix_layout format
|
14
14
|
ignore_disable_comments lint only only_guide_cops require safe
|
15
|
+
autocorrect safe_autocorrect autocorrect_all
|
15
16
|
].freeze
|
16
17
|
|
17
18
|
class Finished < StandardError; end
|
data/lib/rubocop/config.rb
CHANGED
@@ -25,7 +25,7 @@ module RuboCop
|
|
25
25
|
@loaded_path = loaded_path
|
26
26
|
@for_cop = Hash.new do |h, cop|
|
27
27
|
qualified_cop_name = Cop::Registry.qualified_cop_name(cop, loaded_path)
|
28
|
-
cop_options = self[qualified_cop_name] || {}
|
28
|
+
cop_options = self[qualified_cop_name].dup || {}
|
29
29
|
cop_options['Enabled'] = enable_cop?(qualified_cop_name, cop_options)
|
30
30
|
h[cop] = cop_options
|
31
31
|
end
|
@@ -33,7 +33,13 @@ module RuboCop
|
|
33
33
|
MSG = 'Useless `RESTRICT_ON_SEND` is defined.'
|
34
34
|
|
35
35
|
# @!method defined_send_callback?(node)
|
36
|
-
def_node_search :defined_send_callback?,
|
36
|
+
def_node_search :defined_send_callback?, <<~PATTERN
|
37
|
+
{
|
38
|
+
(def {:on_send :after_send} ...)
|
39
|
+
(alias (sym {:on_send :after_send}) _source ...)
|
40
|
+
(send nil? :alias_method {(sym {:on_send :after_send}) (str {"on_send" "after_send"})} _source ...)
|
41
|
+
}
|
42
|
+
PATTERN
|
37
43
|
|
38
44
|
def on_casgn(node)
|
39
45
|
return if !restrict_on_send?(node) || defined_send_callback?(node.parent)
|
@@ -4,9 +4,10 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Layout
|
6
6
|
# Checks that strings broken over multiple lines (by a backslash) contain
|
7
|
-
# trailing spaces instead of leading spaces
|
7
|
+
# trailing spaces instead of leading spaces (default) or leading spaces
|
8
|
+
# instead of trailing spaces.
|
8
9
|
#
|
9
|
-
# @example
|
10
|
+
# @example EnforcedStyle: trailing (default)
|
10
11
|
# # bad
|
11
12
|
# 'this text contains a lot of' \
|
12
13
|
# ' spaces'
|
@@ -23,18 +24,38 @@ module RuboCop
|
|
23
24
|
# 'this text is too ' \
|
24
25
|
# 'long'
|
25
26
|
#
|
27
|
+
# @example EnforcedStyle: leading
|
28
|
+
# # bad
|
29
|
+
# 'this text contains a lot of ' \
|
30
|
+
# 'spaces'
|
31
|
+
#
|
32
|
+
# # good
|
33
|
+
# 'this text contains a lot of' \
|
34
|
+
# ' spaces'
|
35
|
+
#
|
36
|
+
# # bad
|
37
|
+
# 'this text is too ' \
|
38
|
+
# 'long'
|
39
|
+
#
|
40
|
+
# # good
|
41
|
+
# 'this text is too' \
|
42
|
+
# ' long'
|
26
43
|
class LineContinuationLeadingSpace < Base
|
27
44
|
include RangeHelp
|
28
45
|
|
29
|
-
MSG = 'Move leading spaces to the end of previous line.'
|
30
|
-
|
31
46
|
def on_dstr(node)
|
32
|
-
|
47
|
+
end_of_first_line = node.loc.expression.begin_pos - node.loc.expression.column
|
33
48
|
|
34
49
|
raw_lines(node).each_cons(2) do |raw_line_one, raw_line_two|
|
35
|
-
|
50
|
+
end_of_first_line += raw_line_one.length
|
36
51
|
|
37
|
-
|
52
|
+
next unless continuation?(raw_line_one)
|
53
|
+
|
54
|
+
if enforced_style_leading?
|
55
|
+
investigate_leading_style(raw_line_one, end_of_first_line)
|
56
|
+
else
|
57
|
+
investigate_trailing_style(raw_line_two, end_of_first_line)
|
58
|
+
end
|
38
59
|
end
|
39
60
|
end
|
40
61
|
|
@@ -44,24 +65,47 @@ module RuboCop
|
|
44
65
|
processed_source.raw_source.lines[node.first_line - 1, line_range(node).size]
|
45
66
|
end
|
46
67
|
|
47
|
-
def
|
48
|
-
|
68
|
+
def investigate_leading_style(first_line, end_of_first_line)
|
69
|
+
matches = first_line.match(/(?<trailing_spaces>\s+)(?<ending>['"]\s*\\\n)/)
|
70
|
+
return if matches.nil?
|
49
71
|
|
50
|
-
|
72
|
+
add_offense(leading_offense_range(end_of_first_line, matches))
|
73
|
+
end
|
74
|
+
|
75
|
+
def investigate_trailing_style(second_line, end_of_first_line)
|
76
|
+
matches = second_line.match(/\A(?<beginning>\s*['"])(?<leading_spaces>\s+)/)
|
51
77
|
return if matches.nil?
|
52
78
|
|
53
|
-
add_offense(
|
79
|
+
add_offense(trailing_offense_range(end_of_first_line, matches))
|
54
80
|
end
|
55
81
|
|
56
82
|
def continuation?(line)
|
57
83
|
line.end_with?("\\\n")
|
58
84
|
end
|
59
85
|
|
60
|
-
def
|
61
|
-
|
86
|
+
def leading_offense_range(end_of_first_line, matches)
|
87
|
+
end_pos = end_of_first_line - matches[:ending].length
|
88
|
+
begin_pos = end_pos - matches[:trailing_spaces].length
|
89
|
+
range_between(begin_pos, end_pos)
|
90
|
+
end
|
91
|
+
|
92
|
+
def trailing_offense_range(end_of_first_line, matches)
|
93
|
+
begin_pos = end_of_first_line + matches[:beginning].length
|
62
94
|
end_pos = begin_pos + matches[:leading_spaces].length
|
63
95
|
range_between(begin_pos, end_pos)
|
64
96
|
end
|
97
|
+
|
98
|
+
def message(_range)
|
99
|
+
if enforced_style_leading?
|
100
|
+
'Move trailing spaces to the start of next line.'
|
101
|
+
else
|
102
|
+
'Move leading spaces to the end of previous line.'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def enforced_style_leading?
|
107
|
+
cop_config['EnforcedStyle'] == 'leading'
|
108
|
+
end
|
65
109
|
end
|
66
110
|
end
|
67
111
|
end
|
@@ -37,6 +37,7 @@ module RuboCop
|
|
37
37
|
# * MultilineHashBraceLayout
|
38
38
|
# * MultilineHashKeyLineBreaks
|
39
39
|
# * MultilineMethodArgumentLineBreaks
|
40
|
+
# * MultilineMethodParameterLineBreaks
|
40
41
|
# * ParameterAlignment
|
41
42
|
#
|
42
43
|
# Together, these cops will pretty print hashes, arrays,
|
@@ -79,6 +80,7 @@ module RuboCop
|
|
79
80
|
alias on_array on_potential_breakable_node
|
80
81
|
alias on_hash on_potential_breakable_node
|
81
82
|
alias on_send on_potential_breakable_node
|
83
|
+
alias on_def on_potential_breakable_node
|
82
84
|
|
83
85
|
def on_new_investigation
|
84
86
|
check_for_breakable_semicolons(processed_source)
|
@@ -6,7 +6,7 @@ module RuboCop
|
|
6
6
|
# Ensures that each argument in a multi-line method call
|
7
7
|
# starts on a separate line.
|
8
8
|
#
|
9
|
-
# NOTE:
|
9
|
+
# NOTE: This cop does not move the first argument, if you want that to
|
10
10
|
# be on a separate line, see `Layout/FirstMethodArgumentLineBreak`.
|
11
11
|
#
|
12
12
|
# @example
|
@@ -22,6 +22,9 @@ module RuboCop
|
|
22
22
|
# b,
|
23
23
|
# c
|
24
24
|
# )
|
25
|
+
#
|
26
|
+
# # good
|
27
|
+
# foo(a, b, c)
|
25
28
|
class MultilineMethodArgumentLineBreaks < Base
|
26
29
|
include MultilineElementLineBreaks
|
27
30
|
extend AutoCorrector
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Layout
|
6
|
+
# Ensures that each parameter in a multi-line method definition
|
7
|
+
# starts on a separate line.
|
8
|
+
#
|
9
|
+
# NOTE: This cop does not move the first argument, if you want that to
|
10
|
+
# be on a separate line, see `Layout/FirstMethodParameterLineBreak`.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
#
|
14
|
+
# # bad
|
15
|
+
# def foo(a, b,
|
16
|
+
# c
|
17
|
+
# )
|
18
|
+
# end
|
19
|
+
#
|
20
|
+
# # good
|
21
|
+
# def foo(
|
22
|
+
# a,
|
23
|
+
# b,
|
24
|
+
# c
|
25
|
+
# )
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# # good
|
29
|
+
# def foo(a, b, c)
|
30
|
+
# end
|
31
|
+
class MultilineMethodParameterLineBreaks < Base
|
32
|
+
include MultilineElementLineBreaks
|
33
|
+
extend AutoCorrector
|
34
|
+
|
35
|
+
MSG = 'Each parameter in a multi-line method definition must start on a separate line.'
|
36
|
+
|
37
|
+
def on_def(node)
|
38
|
+
return if node.arguments.empty?
|
39
|
+
|
40
|
+
check_line_breaks(node, node.arguments)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -7,6 +7,7 @@ module RuboCop
|
|
7
7
|
# when param passed without parentheses.
|
8
8
|
#
|
9
9
|
# This cop can customize ignored methods with `IgnoredMethods`.
|
10
|
+
# By default, there are no methods to ignored.
|
10
11
|
#
|
11
12
|
# @example
|
12
13
|
#
|
@@ -29,10 +30,16 @@ module RuboCop
|
|
29
30
|
# # Lambda arguments require no disambiguation
|
30
31
|
# foo = ->(bar) { bar.baz }
|
31
32
|
#
|
33
|
+
# @example IgnoredMethods: [] (default)
|
34
|
+
#
|
35
|
+
# # bad
|
36
|
+
# expect { do_something }.to change { object.attribute }
|
37
|
+
#
|
32
38
|
# @example IgnoredMethods: [change]
|
33
39
|
#
|
34
40
|
# # good
|
35
41
|
# expect { do_something }.to change { object.attribute }
|
42
|
+
#
|
36
43
|
class AmbiguousBlockAssociation < Base
|
37
44
|
include IgnoredMethods
|
38
45
|
|
@@ -8,22 +8,22 @@ module RuboCop
|
|
8
8
|
# @example
|
9
9
|
#
|
10
10
|
# # bad
|
11
|
-
#
|
12
11
|
# File.exists?(some_path)
|
13
12
|
# Dir.exists?(some_path)
|
14
13
|
# iterator?
|
15
14
|
# ENV.freeze # Calling `Env.freeze` raises `TypeError` since Ruby 2.7.
|
15
|
+
# ENV.clone
|
16
|
+
# ENV.dup # Calling `Env.dup` raises `TypeError` since Ruby 3.1.
|
16
17
|
# Socket.gethostbyname(host)
|
17
18
|
# Socket.gethostbyaddr(host)
|
18
19
|
#
|
19
|
-
# @example
|
20
|
-
#
|
21
20
|
# # good
|
22
|
-
#
|
23
21
|
# File.exist?(some_path)
|
24
22
|
# Dir.exist?(some_path)
|
25
23
|
# block_given?
|
26
24
|
# ENV # `ENV.freeze` cannot prohibit changes to environment variables.
|
25
|
+
# ENV.to_h
|
26
|
+
# ENV.to_h # `ENV.dup` cannot dup `ENV`, use `ENV.to_h` to get a copy of `ENV` as a hash.
|
27
27
|
# Addrinfo.getaddrinfo(nodename, service)
|
28
28
|
# Addrinfo.tcp(host, port).getnameinfo
|
29
29
|
class DeprecatedClassMethods < Base
|
@@ -111,6 +111,12 @@ module RuboCop
|
|
111
111
|
DeprecatedClassMethod.new(:freeze, class_constant: :ENV) =>
|
112
112
|
Replacement.new(nil, class_constant: :ENV),
|
113
113
|
|
114
|
+
DeprecatedClassMethod.new(:clone, class_constant: :ENV) =>
|
115
|
+
Replacement.new(:to_h, class_constant: :ENV),
|
116
|
+
|
117
|
+
DeprecatedClassMethod.new(:dup, class_constant: :ENV) =>
|
118
|
+
Replacement.new(:to_h, class_constant: :ENV),
|
119
|
+
|
114
120
|
DeprecatedClassMethod.new(:gethostbyaddr, class_constant: :Socket, correctable: false) =>
|
115
121
|
Replacement.new(:getnameinfo, class_constant: :Addrinfo, instance_method: true),
|
116
122
|
|
@@ -25,30 +25,37 @@ module RuboCop
|
|
25
25
|
# to be strictly equivalent to that before the replacement.
|
26
26
|
#
|
27
27
|
# @example
|
28
|
-
# # bad
|
29
|
-
# unless
|
30
|
-
# FileUtils.
|
28
|
+
# # bad - race condition with another process may result in an error in `mkdir`
|
29
|
+
# unless Dir.exist?(path)
|
30
|
+
# FileUtils.mkdir(path)
|
31
31
|
# end
|
32
32
|
#
|
33
|
-
#
|
33
|
+
# # good - atomic and idempotent creation
|
34
|
+
# FileUtils.mkdir_p(path)
|
35
|
+
#
|
36
|
+
# # bad - race condition with another process may result in an error in `remove`
|
37
|
+
# if File.exist?(path)
|
34
38
|
# FileUtils.remove(path)
|
35
39
|
# end
|
36
40
|
#
|
37
|
-
# # good
|
38
|
-
# FileUtils.
|
39
|
-
#
|
40
|
-
# FileUtils.rm_rf(path)
|
41
|
+
# # good - atomic and idempotent removal
|
42
|
+
# FileUtils.rm_f(path)
|
41
43
|
#
|
42
44
|
class NonAtomicFileOperation < Base
|
43
45
|
extend AutoCorrector
|
44
46
|
include Alignment
|
45
47
|
include RangeHelp
|
46
48
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
49
|
+
MSG_REMOVE_FILE_EXIST_CHECK = 'Remove unnecessary existence check ' \
|
50
|
+
'`%<receiver>s.%<method_name>s`.'
|
51
|
+
MSG_CHANGE_FORCE_METHOD = 'Use atomic file operation method `FileUtils.%<method_name>s`.'
|
52
|
+
MAKE_FORCE_METHODS = %i[makedirs mkdir_p mkpath].freeze
|
53
|
+
MAKE_METHODS = %i[mkdir].freeze
|
54
|
+
REMOVE_FORCE_METHODS = %i[rm_f rm_rf].freeze
|
55
|
+
REMOVE_METHODS = %i[remove remove_dir remove_entry remove_entry_secure
|
56
|
+
delete unlink remove_file rm rmdir safe_unlink].freeze
|
57
|
+
RESTRICT_ON_SEND = (MAKE_METHODS + MAKE_FORCE_METHODS + REMOVE_METHODS +
|
58
|
+
REMOVE_FORCE_METHODS).freeze
|
52
59
|
|
53
60
|
# @!method send_exist_node(node)
|
54
61
|
def_node_search :send_exist_node, <<-PATTERN
|
@@ -71,10 +78,9 @@ module RuboCop
|
|
71
78
|
PATTERN
|
72
79
|
|
73
80
|
def on_send(node)
|
74
|
-
return unless (
|
75
|
-
return if allowable_use_with_if?(parent)
|
81
|
+
return unless if_node_child?(node)
|
76
82
|
return if explicit_not_force?(node)
|
77
|
-
return unless (exist_node = send_exist_node(parent).first)
|
83
|
+
return unless (exist_node = send_exist_node(node.parent).first)
|
78
84
|
return unless exist_node.first_argument == node.first_argument
|
79
85
|
|
80
86
|
register_offense(node, exist_node)
|
@@ -82,44 +88,69 @@ module RuboCop
|
|
82
88
|
|
83
89
|
private
|
84
90
|
|
91
|
+
def if_node_child?(node)
|
92
|
+
return false unless (parent = node.parent)
|
93
|
+
|
94
|
+
parent.if_type? && !allowable_use_with_if?(parent)
|
95
|
+
end
|
96
|
+
|
85
97
|
def allowable_use_with_if?(if_node)
|
86
98
|
if_node.condition.and_type? || if_node.condition.or_type? || if_node.else_branch
|
87
99
|
end
|
88
100
|
|
89
101
|
def register_offense(node, exist_node)
|
102
|
+
unless force_method?(node)
|
103
|
+
add_offense(node,
|
104
|
+
message: format(MSG_CHANGE_FORCE_METHOD,
|
105
|
+
method_name: replacement_method(node)))
|
106
|
+
end
|
107
|
+
|
90
108
|
range = range_between(node.parent.loc.keyword.begin_pos,
|
91
109
|
exist_node.loc.expression.end_pos)
|
92
|
-
|
93
|
-
add_offense(range, message: message(exist_node)) do |corrector|
|
110
|
+
add_offense(range, message: message_remove_file_exist_check(exist_node)) do |corrector|
|
94
111
|
autocorrect(corrector, node, range)
|
95
112
|
end
|
96
113
|
end
|
97
114
|
|
98
|
-
def
|
115
|
+
def message_remove_file_exist_check(node)
|
99
116
|
receiver, method_name = receiver_and_method_name(node)
|
100
|
-
format(
|
117
|
+
format(MSG_REMOVE_FILE_EXIST_CHECK, receiver: receiver, method_name: method_name)
|
101
118
|
end
|
102
119
|
|
103
120
|
def autocorrect(corrector, node, range)
|
104
121
|
corrector.remove(range)
|
122
|
+
autocorrect_replace_method(corrector, node)
|
123
|
+
corrector.remove(node.parent.loc.end) if node.parent.multiline?
|
124
|
+
end
|
125
|
+
|
126
|
+
def autocorrect_replace_method(corrector, node)
|
127
|
+
return if force_method?(node)
|
128
|
+
|
105
129
|
corrector.replace(node.child_nodes.first.loc.name, 'FileUtils')
|
106
130
|
corrector.replace(node.loc.selector, replacement_method(node))
|
107
|
-
corrector.remove(node.parent.loc.end) if node.parent.multiline?
|
108
131
|
end
|
109
132
|
|
110
133
|
def replacement_method(node)
|
111
|
-
return node.method_name if force_option?(node)
|
112
|
-
|
113
134
|
if MAKE_METHODS.include?(node.method_name)
|
114
135
|
'mkdir_p'
|
115
136
|
elsif REMOVE_METHODS.include?(node.method_name)
|
116
|
-
'
|
137
|
+
'rm_f'
|
138
|
+
else
|
139
|
+
node.method_name
|
117
140
|
end
|
118
141
|
end
|
119
142
|
|
143
|
+
def force_method?(node)
|
144
|
+
force_method_name?(node) || force_option?(node)
|
145
|
+
end
|
146
|
+
|
120
147
|
def force_option?(node)
|
121
148
|
node.arguments.any? { |arg| force?(arg) }
|
122
149
|
end
|
150
|
+
|
151
|
+
def force_method_name?(node)
|
152
|
+
(MAKE_FORCE_METHODS + REMOVE_FORCE_METHODS).include?(node.method_name)
|
153
|
+
end
|
123
154
|
end
|
124
155
|
end
|
125
156
|
end
|
@@ -16,7 +16,8 @@ module RuboCop
|
|
16
16
|
# NOTE: Some values cannot be converted properly using one of the `Kernel`
|
17
17
|
# method (for instance, `Time` and `DateTime` values are allowed by this
|
18
18
|
# cop by default). Similarly, Rails' duration methods do not work well
|
19
|
-
# with `Integer()` and can be ignored with `IgnoredMethods`.
|
19
|
+
# with `Integer()` and can be ignored with `IgnoredMethods`. By default,
|
20
|
+
# there are no methods to ignored.
|
20
21
|
#
|
21
22
|
# @safety
|
22
23
|
# Autocorrection is unsafe because it is not guaranteed that the
|
@@ -45,6 +46,11 @@ module RuboCop
|
|
45
46
|
# foo.try { |i| Float(i) }
|
46
47
|
# bar.send { |i| Complex(i) }
|
47
48
|
#
|
49
|
+
# @example IgnoredMethods: [] (default)
|
50
|
+
#
|
51
|
+
# # bad
|
52
|
+
# 10.minutes.to_i
|
53
|
+
#
|
48
54
|
# @example IgnoredMethods: [minutes]
|
49
55
|
#
|
50
56
|
# # good
|
@@ -35,6 +35,13 @@ module RuboCop
|
|
35
35
|
# # good - without `&.` this will always return `true`
|
36
36
|
# foo&.respond_to?(:to_a)
|
37
37
|
#
|
38
|
+
# @example AllowedMethods: [foo?]
|
39
|
+
# # bad
|
40
|
+
# do_something if attrs&.foo?(:[])
|
41
|
+
#
|
42
|
+
# # good
|
43
|
+
# do_something if attrs&.bar?(:[])
|
44
|
+
#
|
38
45
|
class RedundantSafeNavigation < Base
|
39
46
|
include AllowedMethods
|
40
47
|
include RangeHelp
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Lint
|
6
|
+
# Checks that a range literal is enclosed in parentheses when the end of the range is
|
7
|
+
# at a line break.
|
8
|
+
#
|
9
|
+
# NOTE: The following is maybe intended for `(42..)`. But, compatible is `42..do_something`.
|
10
|
+
# So, this cop does not provide autocorrection because it is left to user.
|
11
|
+
#
|
12
|
+
# [source,ruby]
|
13
|
+
# ----
|
14
|
+
# case condition
|
15
|
+
# when 42..
|
16
|
+
# do_something
|
17
|
+
# end
|
18
|
+
# ----
|
19
|
+
#
|
20
|
+
# @example
|
21
|
+
#
|
22
|
+
# # bad - Represents `(1..42)`, not endless range.
|
23
|
+
# 1..
|
24
|
+
# 42
|
25
|
+
#
|
26
|
+
# # good - It's incompatible, but your intentions when using endless range may be:
|
27
|
+
# (1..)
|
28
|
+
# 42
|
29
|
+
#
|
30
|
+
# # good
|
31
|
+
# 1..42
|
32
|
+
#
|
33
|
+
# # good
|
34
|
+
# (1..42)
|
35
|
+
#
|
36
|
+
# # good
|
37
|
+
# (1..
|
38
|
+
# 42)
|
39
|
+
#
|
40
|
+
class RequireRangeParentheses < Base
|
41
|
+
MSG = 'Wrap the endless range literal `%<range>s` to avoid precedence ambiguity.'
|
42
|
+
|
43
|
+
def on_irange(node)
|
44
|
+
return if node.parent&.begin_type?
|
45
|
+
return unless node.begin && node.end
|
46
|
+
return if same_line?(node.begin, node.end)
|
47
|
+
|
48
|
+
message = format(MSG, range: "#{node.begin.source}#{node.loc.operator.source}")
|
49
|
+
|
50
|
+
add_offense(node, message: message)
|
51
|
+
end
|
52
|
+
|
53
|
+
alias on_erange on_irange
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -25,6 +25,7 @@ module RuboCop
|
|
25
25
|
# x&.foo || bar
|
26
26
|
class SafeNavigationChain < Base
|
27
27
|
include NilMethods
|
28
|
+
extend AutoCorrector
|
28
29
|
extend TargetRubyVersion
|
29
30
|
|
30
31
|
minimum_target_ruby_version 2.3
|
@@ -48,12 +49,45 @@ module RuboCop
|
|
48
49
|
Parser::Source::Range.new(node.source_range.source_buffer,
|
49
50
|
safe_nav.source_range.end_pos,
|
50
51
|
method_chain.source_range.end_pos)
|
51
|
-
add_offense(location)
|
52
|
+
add_offense(location) do |corrector|
|
53
|
+
autocorrect(corrector, offense_range: location, send_node: method_chain)
|
54
|
+
end
|
52
55
|
end
|
53
56
|
end
|
54
57
|
|
55
58
|
private
|
56
59
|
|
60
|
+
# @param [Parser::Source::Range] offense_range
|
61
|
+
# @param [RuboCop::AST::SendNode] send_node
|
62
|
+
# @return [String]
|
63
|
+
def add_safe_navigation_operator(offense_range:, send_node:)
|
64
|
+
source = \
|
65
|
+
if send_node.method?(:[]) || send_node.method?(:[]=)
|
66
|
+
format(
|
67
|
+
'%<method_name>s(%<arguments>s)',
|
68
|
+
arguments: send_node.arguments.map(&:source).join(', '),
|
69
|
+
method_name: send_node.method_name
|
70
|
+
)
|
71
|
+
else
|
72
|
+
offense_range.source.dup
|
73
|
+
end
|
74
|
+
source.prepend('.') unless send_node.dot?
|
75
|
+
source.prepend('&')
|
76
|
+
end
|
77
|
+
|
78
|
+
# @param [RuboCop::Cop::Corrector] corrector
|
79
|
+
# @param [Parser::Source::Range] offense_range
|
80
|
+
# @param [RuboCop::AST::SendNode] send_node
|
81
|
+
def autocorrect(corrector, offense_range:, send_node:)
|
82
|
+
corrector.replace(
|
83
|
+
offense_range,
|
84
|
+
add_safe_navigation_operator(
|
85
|
+
offense_range: offense_range,
|
86
|
+
send_node: send_node
|
87
|
+
)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
|
57
91
|
def method_chain(node)
|
58
92
|
chain = node
|
59
93
|
chain = chain.parent if chain.send_type? && chain.parent&.call_type?
|
@@ -15,6 +15,7 @@ module RuboCop
|
|
15
15
|
#
|
16
16
|
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
17
17
|
# for backwards compatibility. Please use `IgnoredMethods` instead.
|
18
|
+
# By default, there are no methods to ignored.
|
18
19
|
#
|
19
20
|
# @example CountAsOne: ['array', 'heredoc']
|
20
21
|
#
|
@@ -13,6 +13,7 @@ module RuboCop
|
|
13
13
|
#
|
14
14
|
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
15
15
|
# for backwards compatibility. Please use `IgnoredMethods` instead.
|
16
|
+
# By default, there are no methods to ignored.
|
16
17
|
#
|
17
18
|
# @example CountAsOne: ['array', 'heredoc']
|
18
19
|
#
|