rubocop 1.31.2 → 1.34.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/config/default.yml +74 -16
- data/config/obsoletion.yml +23 -1
- data/lib/rubocop/cache_config.rb +29 -0
- data/lib/rubocop/cli/command/auto_genenerate_config.rb +2 -2
- data/lib/rubocop/cli/command/init_dotfile.rb +1 -1
- data/lib/rubocop/cli/command/suggest_extensions.rb +53 -15
- data/lib/rubocop/cli.rb +1 -0
- data/lib/rubocop/config.rb +1 -1
- data/lib/rubocop/config_finder.rb +68 -0
- data/lib/rubocop/config_loader.rb +12 -40
- data/lib/rubocop/config_loader_resolver.rb +1 -5
- data/lib/rubocop/config_obsoletion/changed_parameter.rb +5 -0
- data/lib/rubocop/config_obsoletion/parameter_rule.rb +4 -0
- data/lib/rubocop/config_obsoletion.rb +7 -2
- data/lib/rubocop/cop/cop.rb +1 -1
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +28 -0
- data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +61 -0
- data/lib/rubocop/cop/internal_affairs/useless_restrict_on_send.rb +7 -1
- data/lib/rubocop/cop/internal_affairs.rb +1 -0
- data/lib/rubocop/cop/layout/block_end_newline.rb +33 -5
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +6 -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_assignment_layout.rb +1 -1
- 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/legacy/corrections_proxy.rb +1 -1
- data/lib/rubocop/cop/legacy/corrector.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +26 -6
- data/lib/rubocop/cop/lint/debugger.rb +26 -16
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +10 -4
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +65 -1
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +55 -24
- data/lib/rubocop/cop/lint/number_conversion.rb +28 -6
- 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/lint/shadowed_exception.rb +15 -0
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +9 -1
- data/lib/rubocop/cop/metrics/abc_size.rb +3 -1
- data/lib/rubocop/cop/metrics/block_length.rb +6 -6
- data/lib/rubocop/cop/metrics/method_length.rb +8 -7
- data/lib/rubocop/cop/mixin/allowed_methods.rb +15 -1
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +9 -1
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +4 -0
- data/lib/rubocop/cop/mixin/comments_help.rb +5 -1
- data/lib/rubocop/cop/mixin/enforce_superclass.rb +2 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +4 -9
- data/lib/rubocop/cop/mixin/percent_array.rb +60 -1
- data/lib/rubocop/cop/mixin/range_help.rb +2 -2
- data/lib/rubocop/cop/naming/predicate_name.rb +30 -1
- data/lib/rubocop/cop/style/block_delimiters.rb +26 -7
- data/lib/rubocop/cop/style/class_and_module_children.rb +4 -4
- data/lib/rubocop/cop/style/class_equality_comparison.rb +50 -3
- data/lib/rubocop/cop/style/double_negation.rb +2 -0
- data/lib/rubocop/cop/style/empty_else.rb +37 -0
- data/lib/rubocop/cop/style/empty_heredoc.rb +73 -0
- data/lib/rubocop/cop/style/fetch_env_var.rb +10 -177
- data/lib/rubocop/cop/style/format_string_token.rb +25 -6
- data/lib/rubocop/cop/style/hash_except.rb +0 -4
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -1
- data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +2 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/require_parentheses.rb +5 -1
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +7 -7
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +19 -2
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +9 -0
- data/lib/rubocop/cop/style/numeric_literals.rb +16 -1
- data/lib/rubocop/cop/style/numeric_predicate.rb +43 -9
- data/lib/rubocop/cop/style/redundant_condition.rb +19 -4
- data/lib/rubocop/cop/style/redundant_parentheses.rb +15 -22
- data/lib/rubocop/cop/style/redundant_sort.rb +21 -6
- data/lib/rubocop/cop/style/semicolon.rb +27 -3
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +14 -3
- data/lib/rubocop/cop/style/symbol_array.rb +2 -3
- data/lib/rubocop/cop/style/symbol_proc.rb +40 -7
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -13
- 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/ext/range.rb +15 -0
- data/lib/rubocop/feature_loader.rb +88 -0
- data/lib/rubocop/formatter/clang_style_formatter.rb +1 -1
- data/lib/rubocop/formatter/disabled_config_formatter.rb +1 -1
- data/lib/rubocop/formatter/html_formatter.rb +1 -1
- data/lib/rubocop/formatter/markdown_formatter.rb +1 -1
- data/lib/rubocop/formatter/tap_formatter.rb +1 -1
- data/lib/rubocop/options.rb +3 -6
- data/lib/rubocop/result_cache.rb +22 -20
- 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/cache.rb +33 -1
- data/lib/rubocop/server/cli.rb +19 -2
- data/lib/rubocop/server/client_command/base.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +5 -1
- metadata +16 -9
- data/lib/rubocop/cop/mixin/ignored_methods.rb +0 -52
@@ -4,6 +4,14 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Lint
|
6
6
|
# Checks for the presence of `if`, `elsif` and `unless` branches without a body.
|
7
|
+
#
|
8
|
+
# NOTE: empty `else` branches are handled by `Style/EmptyElse`.
|
9
|
+
#
|
10
|
+
# @safety
|
11
|
+
# Autocorrection for this cop is not safe. The conditions for empty branches that
|
12
|
+
# the autocorrection removes may have side effects, or the logic in subsequent
|
13
|
+
# branches may change due to the removal of a previous condition.
|
14
|
+
#
|
7
15
|
# @example
|
8
16
|
# # bad
|
9
17
|
# if condition
|
@@ -53,7 +61,9 @@ module RuboCop
|
|
53
61
|
# end
|
54
62
|
#
|
55
63
|
class EmptyConditionalBody < Base
|
64
|
+
extend AutoCorrector
|
56
65
|
include CommentsHelp
|
66
|
+
include RangeHelp
|
57
67
|
|
58
68
|
MSG = 'Avoid `%<keyword>s` branches without a body.'
|
59
69
|
|
@@ -61,7 +71,61 @@ module RuboCop
|
|
61
71
|
return if node.body
|
62
72
|
return if cop_config['AllowComments'] && contains_comments?(node)
|
63
73
|
|
64
|
-
add_offense(node, message: format(MSG, keyword: node.keyword))
|
74
|
+
add_offense(node, message: format(MSG, keyword: node.keyword)) do |corrector|
|
75
|
+
autocorrect(corrector, node)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def autocorrect(corrector, node)
|
82
|
+
remove_comments(corrector, node)
|
83
|
+
remove_empty_branch(corrector, node)
|
84
|
+
correct_other_branches(corrector, node)
|
85
|
+
end
|
86
|
+
|
87
|
+
def remove_comments(corrector, node)
|
88
|
+
comments_in_range(node).each do |comment|
|
89
|
+
range = range_by_whole_lines(comment.loc.expression, include_final_newline: true)
|
90
|
+
corrector.remove(range)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def remove_empty_branch(corrector, node)
|
95
|
+
corrector.remove(deletion_range(branch_range(node)))
|
96
|
+
end
|
97
|
+
|
98
|
+
def correct_other_branches(corrector, node)
|
99
|
+
return unless (node.if? || node.unless?) && node.else_branch
|
100
|
+
|
101
|
+
if node.else_branch.if_type?
|
102
|
+
# Replace an orphaned `elsif` with `if`
|
103
|
+
corrector.replace(node.else_branch.loc.keyword, 'if')
|
104
|
+
else
|
105
|
+
# Flip orphaned `else`
|
106
|
+
corrector.replace(node.loc.else, "#{node.inverse_keyword} #{node.condition.source}")
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def branch_range(node)
|
111
|
+
if node.loc.else
|
112
|
+
node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
|
113
|
+
else
|
114
|
+
node.source_range
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def deletion_range(range)
|
119
|
+
# Collect a range between the start of the `if` node and the next relevant node,
|
120
|
+
# including final new line.
|
121
|
+
# Based on `RangeHelp#range_by_whole_lines` but allows the `if` to not start
|
122
|
+
# on the first column.
|
123
|
+
buffer = @processed_source.buffer
|
124
|
+
|
125
|
+
last_line = buffer.source_line(range.last_line)
|
126
|
+
end_offset = last_line.length - range.last_column + 1
|
127
|
+
|
128
|
+
range.adjust(end_pos: end_offset).intersect(buffer.source_range)
|
65
129
|
end
|
66
130
|
end
|
67
131
|
end
|
@@ -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
|
19
|
+
# with `Integer()` and can be allowed with `AllowedMethods`. By default,
|
20
|
+
# there are no methods to allowed.
|
20
21
|
#
|
21
22
|
# @safety
|
22
23
|
# Autocorrection is unsafe because it is not guaranteed that the
|
@@ -45,7 +46,22 @@ module RuboCop
|
|
45
46
|
# foo.try { |i| Float(i) }
|
46
47
|
# bar.send { |i| Complex(i) }
|
47
48
|
#
|
48
|
-
# @example
|
49
|
+
# @example AllowedMethods: [] (default)
|
50
|
+
#
|
51
|
+
# # bad
|
52
|
+
# 10.minutes.to_i
|
53
|
+
#
|
54
|
+
# @example AllowedMethods: [minutes]
|
55
|
+
#
|
56
|
+
# # good
|
57
|
+
# 10.minutes.to_i
|
58
|
+
#
|
59
|
+
# @example AllowedPatterns: [] (default)
|
60
|
+
#
|
61
|
+
# # bad
|
62
|
+
# 10.minutes.to_i
|
63
|
+
#
|
64
|
+
# @example AllowedPatterns: [/min*/]
|
49
65
|
#
|
50
66
|
# # good
|
51
67
|
# 10.minutes.to_i
|
@@ -56,7 +72,8 @@ module RuboCop
|
|
56
72
|
# Time.now.to_datetime.to_i
|
57
73
|
class NumberConversion < Base
|
58
74
|
extend AutoCorrector
|
59
|
-
include
|
75
|
+
include AllowedMethods
|
76
|
+
include AllowedPattern
|
60
77
|
|
61
78
|
CONVERSION_METHOD_CLASS_MAPPING = {
|
62
79
|
to_i: "#{Integer.name}(%<number_object>s, 10)",
|
@@ -91,7 +108,7 @@ module RuboCop
|
|
91
108
|
|
92
109
|
def handle_conversion_method(node)
|
93
110
|
to_method(node) do |receiver, to_method|
|
94
|
-
next if receiver.nil? ||
|
111
|
+
next if receiver.nil? || allow_receiver?(receiver)
|
95
112
|
|
96
113
|
message = format(
|
97
114
|
MSG,
|
@@ -135,9 +152,10 @@ module RuboCop
|
|
135
152
|
corrector.remove(node.loc.end)
|
136
153
|
end
|
137
154
|
|
138
|
-
def
|
155
|
+
def allow_receiver?(receiver)
|
139
156
|
if receiver.numeric_type? || (receiver.send_type? &&
|
140
|
-
(conversion_method?(receiver.method_name) ||
|
157
|
+
(conversion_method?(receiver.method_name) ||
|
158
|
+
allowed_method_name?(receiver.method_name)))
|
141
159
|
true
|
142
160
|
elsif (receiver = top_receiver(receiver))
|
143
161
|
receiver.const_type? && ignored_class?(receiver.const_name)
|
@@ -146,6 +164,10 @@ module RuboCop
|
|
146
164
|
end
|
147
165
|
end
|
148
166
|
|
167
|
+
def allowed_method_name?(name)
|
168
|
+
allowed_method?(name) || matches_allowed_pattern?(name)
|
169
|
+
end
|
170
|
+
|
149
171
|
def top_receiver(node)
|
150
172
|
receiver = node
|
151
173
|
receiver = receiver.receiver until receiver.receiver.nil?
|
@@ -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?
|
@@ -7,6 +7,14 @@ module RuboCop
|
|
7
7
|
# less specific exception being rescued before a more specific
|
8
8
|
# exception is rescued.
|
9
9
|
#
|
10
|
+
# An exception is considered shadowed if it is rescued after its
|
11
|
+
# ancestor is, or if it and its ancestor are both rescued in the
|
12
|
+
# same `rescue` statement. In both cases, the more specific rescue is
|
13
|
+
# unnecessary because it is covered by rescuing the less specific
|
14
|
+
# exception. (ie. `rescue Exception, StandardError` has the same behavior
|
15
|
+
# whether `StandardError` is included or not, because all `StandardError`s
|
16
|
+
# are rescued by `rescue Exception`).
|
17
|
+
#
|
10
18
|
# @example
|
11
19
|
#
|
12
20
|
# # bad
|
@@ -19,6 +27,13 @@ module RuboCop
|
|
19
27
|
# handle_standard_error
|
20
28
|
# end
|
21
29
|
#
|
30
|
+
# # bad
|
31
|
+
# begin
|
32
|
+
# something
|
33
|
+
# rescue Exception, StandardError
|
34
|
+
# handle_error
|
35
|
+
# end
|
36
|
+
#
|
22
37
|
# # good
|
23
38
|
#
|
24
39
|
# begin
|
@@ -67,10 +67,18 @@ module RuboCop
|
|
67
67
|
variable_node = variable.scope.node.parent
|
68
68
|
return false unless variable_node.conditional?
|
69
69
|
|
70
|
-
outer_local_variable_node =
|
70
|
+
outer_local_variable_node =
|
71
|
+
find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
|
71
72
|
|
72
73
|
outer_local_variable_node.conditional? && variable_node == outer_local_variable_node
|
73
74
|
end
|
75
|
+
|
76
|
+
def find_conditional_node_from_ascendant(node)
|
77
|
+
return unless (parent = node.parent)
|
78
|
+
return parent if parent.conditional?
|
79
|
+
|
80
|
+
find_conditional_node_from_ascendant(parent)
|
81
|
+
end
|
74
82
|
end
|
75
83
|
end
|
76
84
|
end
|
@@ -33,7 +33,9 @@ module RuboCop
|
|
33
33
|
# render 'pages/search/page'
|
34
34
|
# end
|
35
35
|
#
|
36
|
-
# This cop also takes into account `
|
36
|
+
# This cop also takes into account `AllowedMethods` (defaults to `[]`)
|
37
|
+
# And `AllowedPatterns` (defaults to `[]`)
|
38
|
+
#
|
37
39
|
class AbcSize < Base
|
38
40
|
include MethodComplexity
|
39
41
|
|
@@ -14,7 +14,8 @@ module RuboCop
|
|
14
14
|
#
|
15
15
|
#
|
16
16
|
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
17
|
-
# for backwards compatibility. Please use `
|
17
|
+
# for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
|
18
|
+
# instead. By default, there are no methods to allowed.
|
18
19
|
#
|
19
20
|
# @example CountAsOne: ['array', 'heredoc']
|
20
21
|
#
|
@@ -37,14 +38,13 @@ module RuboCop
|
|
37
38
|
# NOTE: This cop does not apply for `Struct` definitions.
|
38
39
|
class BlockLength < Base
|
39
40
|
include CodeLength
|
40
|
-
include
|
41
|
-
|
42
|
-
ignored_methods deprecated_key: 'ExcludedMethods'
|
41
|
+
include AllowedMethods
|
42
|
+
include AllowedPattern
|
43
43
|
|
44
44
|
LABEL = 'Block'
|
45
45
|
|
46
46
|
def on_block(node)
|
47
|
-
return if
|
47
|
+
return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
|
48
48
|
return if method_receiver_excluded?(node)
|
49
49
|
return if node.class_constructor? || node.struct_constructor?
|
50
50
|
|
@@ -58,7 +58,7 @@ module RuboCop
|
|
58
58
|
node_receiver = node.receiver&.source&.gsub(/\s+/, '')
|
59
59
|
node_method = String(node.method_name)
|
60
60
|
|
61
|
-
|
61
|
+
allowed_methods.any? do |config|
|
62
62
|
next unless config.is_a?(String)
|
63
63
|
|
64
64
|
receiver, method = config.split('.')
|
@@ -4,15 +4,17 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Metrics
|
6
6
|
# Checks if the length of a method exceeds some maximum value.
|
7
|
-
# Comment lines can optionally be
|
7
|
+
# Comment lines can optionally be allowed.
|
8
8
|
# The maximum allowed length is configurable.
|
9
9
|
#
|
10
10
|
# You can set literals you want to fold with `CountAsOne`.
|
11
11
|
# Available are: 'array', 'hash', and 'heredoc'. Each literal
|
12
12
|
# will be counted as one line regardless of its actual size.
|
13
13
|
#
|
14
|
-
# NOTE: The `ExcludedMethods` configuration is
|
15
|
-
#
|
14
|
+
# NOTE: The `ExcludedMethods` and `IgnoredMethods` configuration is
|
15
|
+
# deprecated and only kept for backwards compatibility.
|
16
|
+
# Please use `AllowedMethods` and `AllowedPatterns` instead.
|
17
|
+
# By default, there are no methods to allowed.
|
16
18
|
#
|
17
19
|
# @example CountAsOne: ['array', 'heredoc']
|
18
20
|
#
|
@@ -34,14 +36,13 @@ module RuboCop
|
|
34
36
|
#
|
35
37
|
class MethodLength < Base
|
36
38
|
include CodeLength
|
37
|
-
include
|
38
|
-
|
39
|
-
ignored_methods deprecated_key: 'ExcludedMethods'
|
39
|
+
include AllowedMethods
|
40
|
+
include AllowedPattern
|
40
41
|
|
41
42
|
LABEL = 'Method'
|
42
43
|
|
43
44
|
def on_def(node)
|
44
|
-
return if
|
45
|
+
return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
|
45
46
|
|
46
47
|
check_code_length(node)
|
47
48
|
end
|
@@ -12,10 +12,24 @@ module RuboCop
|
|
12
12
|
allowed_methods.include?(name.to_s)
|
13
13
|
end
|
14
14
|
|
15
|
+
# @deprecated Use allowed_method? instead
|
16
|
+
alias ignored_method? allowed_method?
|
17
|
+
|
15
18
|
# @api public
|
16
19
|
def allowed_methods
|
17
|
-
|
20
|
+
deprecated_values = cop_config_deprecated_values
|
21
|
+
if deprecated_values.any?(Regexp)
|
22
|
+
cop_config.fetch('AllowedMethods', [])
|
23
|
+
else
|
24
|
+
Array(cop_config['AllowedMethods']).concat(deprecated_values)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def cop_config_deprecated_values
|
29
|
+
Array(cop_config['IgnoredMethods']).concat(Array(cop_config['ExcludedMethods']))
|
18
30
|
end
|
19
31
|
end
|
32
|
+
# @deprecated IgnoredMethods class has been replaced with AllowedMethods.
|
33
|
+
IgnoredMethods = AllowedMethods
|
20
34
|
end
|
21
35
|
end
|
@@ -30,7 +30,15 @@ module RuboCop
|
|
30
30
|
def allowed_patterns
|
31
31
|
# Since there could be a pattern specified in the default config, merge the two
|
32
32
|
# arrays together.
|
33
|
-
Array(cop_config['AllowedPatterns']).concat(Array(cop_config['IgnoredPatterns']))
|
33
|
+
patterns = Array(cop_config['AllowedPatterns']).concat(Array(cop_config['IgnoredPatterns']))
|
34
|
+
deprecated_values = cop_config_deprecated_methods_values
|
35
|
+
return patterns unless deprecated_values.any?(Regexp)
|
36
|
+
|
37
|
+
Array(patterns.concat(deprecated_values))
|
38
|
+
end
|
39
|
+
|
40
|
+
def cop_config_deprecated_methods_values
|
41
|
+
Array(cop_config['IgnoredMethods']).concat(Array(cop_config['ExcludedMethods']))
|
34
42
|
end
|
35
43
|
end
|
36
44
|
|
@@ -46,6 +46,8 @@ module RuboCop
|
|
46
46
|
if node.send_type?
|
47
47
|
args = process_args(node.arguments)
|
48
48
|
return extract_breakable_node_from_elements(node, args, max)
|
49
|
+
elsif node.def_type?
|
50
|
+
return extract_breakable_node_from_elements(node, node.arguments, max)
|
49
51
|
elsif node.array_type? || node.hash_type?
|
50
52
|
return extract_breakable_node_from_elements(node, node.children, max)
|
51
53
|
end
|
@@ -216,6 +218,8 @@ module RuboCop
|
|
216
218
|
|
217
219
|
# @api private
|
218
220
|
def already_on_multiple_lines?(node)
|
221
|
+
return node.first_line != node.arguments.last.last_line if node.def_type?
|
222
|
+
|
219
223
|
node.first_line != node.last_line
|
220
224
|
end
|
221
225
|
end
|
@@ -12,10 +12,14 @@ module RuboCop
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def contains_comments?(node)
|
15
|
+
comments_in_range(node).any?
|
16
|
+
end
|
17
|
+
|
18
|
+
def comments_in_range(node)
|
15
19
|
start_line = node.source_range.line
|
16
20
|
end_line = find_end_line(node)
|
17
21
|
|
18
|
-
processed_source.each_comment_in_lines(start_line...end_line)
|
22
|
+
processed_source.each_comment_in_lines(start_line...end_line)
|
19
23
|
end
|
20
24
|
|
21
25
|
private
|
@@ -6,7 +6,8 @@ module RuboCop
|
|
6
6
|
#
|
7
7
|
# IMPORTANT: RuboCop core depended on this module when it supported Rails department.
|
8
8
|
# Rails department has been extracted to RuboCop Rails gem.
|
9
|
-
#
|
9
|
+
#
|
10
|
+
# @deprecated This module is deprecated and will be removed by RuboCop 2.0.
|
10
11
|
# It will not be updated to `RuboCop::Cop::Base` v1 API to maintain compatibility
|
11
12
|
# with existing RuboCop Rails 2.8 or lower.
|
12
13
|
#
|