rubocop 1.56.2 → 1.57.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 -1
- data/config/default.yml +12 -6
- data/lib/rubocop/cli.rb +1 -1
- data/lib/rubocop/cop/autocorrect_logic.rb +3 -1
- data/lib/rubocop/cop/bundler/duplicated_group.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +42 -22
- data/lib/rubocop/cop/internal_affairs/redundant_method_dispatch_node.rb +11 -2
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +2 -0
- data/lib/rubocop/cop/layout/dot_position.rb +1 -5
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +41 -8
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +3 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
- data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
- data/lib/rubocop/cop/layout/space_after_not.rb +1 -1
- data/lib/rubocop/cop/layout/space_inside_parens.rb +1 -1
- data/lib/rubocop/cop/lint/debugger.rb +10 -1
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +1 -1
- data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +0 -1
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +4 -0
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +20 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +11 -4
- data/lib/rubocop/cop/lint/useless_assignment.rb +37 -10
- data/lib/rubocop/cop/metrics/block_length.rb +1 -1
- data/lib/rubocop/cop/metrics/class_length.rb +2 -2
- data/lib/rubocop/cop/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -2
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +5 -7
- data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -6
- data/lib/rubocop/cop/style/array_intersect.rb +13 -5
- data/lib/rubocop/cop/style/class_equality_comparison.rb +5 -0
- data/lib/rubocop/cop/style/collection_methods.rb +2 -0
- data/lib/rubocop/cop/style/combinable_loops.rb +3 -2
- data/lib/rubocop/cop/style/empty_case_condition.rb +6 -1
- data/lib/rubocop/cop/style/for.rb +1 -1
- data/lib/rubocop/cop/style/format_string.rb +24 -3
- data/lib/rubocop/cop/style/guard_clause.rb +26 -0
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +17 -3
- data/lib/rubocop/cop/style/multiline_block_chain.rb +1 -1
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -1
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -11
- data/lib/rubocop/cop/style/operator_method_call.rb +6 -0
- data/lib/rubocop/cop/style/redundant_begin.rb +9 -1
- data/lib/rubocop/cop/style/redundant_conditional.rb +1 -9
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +86 -5
- data/lib/rubocop/cop/style/redundant_exception.rb +32 -12
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +18 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +21 -5
- data/lib/rubocop/cop/style/return_nil.rb +6 -2
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +65 -0
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +3 -1
- data/lib/rubocop/cop/style/symbol_array.rb +10 -11
- data/lib/rubocop/cop/style/yoda_expression.rb +8 -7
- data/lib/rubocop/file_finder.rb +4 -7
- data/lib/rubocop/magic_comment.rb +12 -10
- data/lib/rubocop/rspec/shared_contexts.rb +2 -3
- data/lib/rubocop/server/cache.rb +1 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +1 -0
- metadata +10 -9
@@ -7,12 +7,18 @@ module RuboCop
|
|
7
7
|
# scope.
|
8
8
|
# The basic idea for this cop was from the warning of `ruby -cw`:
|
9
9
|
#
|
10
|
-
#
|
10
|
+
# [source,console]
|
11
|
+
# ----
|
12
|
+
# assigned but unused variable - foo
|
13
|
+
# ----
|
11
14
|
#
|
12
15
|
# Currently this cop has advanced logic that detects unreferenced
|
13
16
|
# reassignments and properly handles varied cases such as branch, loop,
|
14
17
|
# rescue, ensure, etc.
|
15
18
|
#
|
19
|
+
# NOTE: Given the assignment `foo = 1, bar = 2`, removing unused variables
|
20
|
+
# can lead to a syntax error, so this case is not autocorrected.
|
21
|
+
#
|
16
22
|
# @safety
|
17
23
|
# This cop's autocorrection is unsafe because removing assignment from
|
18
24
|
# operator assignment can cause NameError if this assignment has been used to declare
|
@@ -51,25 +57,24 @@ module RuboCop
|
|
51
57
|
scope.variables.each_value { |variable| check_for_unused_assignments(variable) }
|
52
58
|
end
|
53
59
|
|
60
|
+
# rubocop:disable Metrics/AbcSize
|
54
61
|
def check_for_unused_assignments(variable)
|
55
62
|
return if variable.should_be_unused?
|
56
63
|
|
57
64
|
variable.assignments.each do |assignment|
|
58
|
-
next if assignment.used?
|
65
|
+
next if assignment.used? || part_of_ignored_node?(assignment.node)
|
59
66
|
|
60
67
|
message = message_for_useless_assignment(assignment)
|
68
|
+
range = offense_range(assignment)
|
61
69
|
|
62
|
-
|
63
|
-
|
64
|
-
else
|
65
|
-
assignment.node.loc.name
|
66
|
-
end
|
67
|
-
|
68
|
-
add_offense(location, message: message) do |corrector|
|
69
|
-
autocorrect(corrector, assignment)
|
70
|
+
add_offense(range, message: message) do |corrector|
|
71
|
+
autocorrect(corrector, assignment) unless sequential_assignment?(assignment.node)
|
70
72
|
end
|
73
|
+
|
74
|
+
ignore_node(assignment.node) if chained_assignment?(assignment.node)
|
71
75
|
end
|
72
76
|
end
|
77
|
+
# rubocop:enable Metrics/AbcSize
|
73
78
|
|
74
79
|
def message_for_useless_assignment(assignment)
|
75
80
|
variable = assignment.variable
|
@@ -77,6 +82,28 @@ module RuboCop
|
|
77
82
|
format(MSG, variable: variable.name) + message_specification(assignment, variable).to_s
|
78
83
|
end
|
79
84
|
|
85
|
+
def offense_range(assignment)
|
86
|
+
if assignment.regexp_named_capture?
|
87
|
+
assignment.node.children.first.source_range
|
88
|
+
else
|
89
|
+
assignment.node.loc.name
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def sequential_assignment?(node)
|
94
|
+
if node.lvasgn_type? && node.expression&.array_type? &&
|
95
|
+
node.each_descendant.any?(&:assignment?)
|
96
|
+
return true
|
97
|
+
end
|
98
|
+
return false unless node.parent
|
99
|
+
|
100
|
+
sequential_assignment?(node.parent)
|
101
|
+
end
|
102
|
+
|
103
|
+
def chained_assignment?(node)
|
104
|
+
node.respond_to?(:expression) && node.expression&.lvasgn_type?
|
105
|
+
end
|
106
|
+
|
80
107
|
def message_specification(assignment, variable)
|
81
108
|
if assignment.multiple_assignment?
|
82
109
|
multiple_assignment_message(variable.name)
|
@@ -12,6 +12,7 @@ module RuboCop
|
|
12
12
|
# Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
|
13
13
|
# will be counted as one line regardless of its actual size.
|
14
14
|
#
|
15
|
+
# NOTE: This cop does not apply for `Struct` definitions.
|
15
16
|
#
|
16
17
|
# NOTE: The `ExcludedMethods` configuration is deprecated and only kept
|
17
18
|
# for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
|
@@ -40,7 +41,6 @@ module RuboCop
|
|
40
41
|
# )
|
41
42
|
# end # 6 points
|
42
43
|
#
|
43
|
-
# NOTE: This cop does not apply for `Struct` definitions.
|
44
44
|
class BlockLength < Base
|
45
45
|
include CodeLength
|
46
46
|
include AllowedMethods
|
@@ -11,6 +11,8 @@ module RuboCop
|
|
11
11
|
# Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
|
12
12
|
# will be counted as one line regardless of its actual size.
|
13
13
|
#
|
14
|
+
# NOTE: This cop also applies for `Struct` definitions.
|
15
|
+
#
|
14
16
|
# @example CountAsOne: ['array', 'heredoc', 'method_call']
|
15
17
|
#
|
16
18
|
# class Foo
|
@@ -34,8 +36,6 @@ module RuboCop
|
|
34
36
|
# )
|
35
37
|
# end # 6 points
|
36
38
|
#
|
37
|
-
#
|
38
|
-
# NOTE: This cop also applies for `Struct` definitions.
|
39
39
|
class ClassLength < Base
|
40
40
|
include CodeLength
|
41
41
|
|
@@ -10,7 +10,7 @@ module RuboCop
|
|
10
10
|
include Util
|
11
11
|
|
12
12
|
FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
|
13
|
-
CLASSLIKE_TYPES = %i[class module
|
13
|
+
CLASSLIKE_TYPES = %i[class module].freeze
|
14
14
|
private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
|
15
15
|
|
16
16
|
def initialize(node, processed_source, count_comments: false, foldable_types: [])
|
@@ -145,7 +145,7 @@ module RuboCop
|
|
145
145
|
|
146
146
|
def extract_body(node)
|
147
147
|
case node.type
|
148
|
-
when :class, :module, :block, :numblock, :def, :defs
|
148
|
+
when :class, :module, :sclass, :block, :numblock, :def, :defs
|
149
149
|
node.body
|
150
150
|
when :casgn
|
151
151
|
_scope, _name, value = *node
|
@@ -20,16 +20,17 @@ module RuboCop
|
|
20
20
|
range = offending_range(node, lhs, rhs, style)
|
21
21
|
check(range, node, lhs, rhs)
|
22
22
|
end
|
23
|
+
alias on_csend on_send
|
23
24
|
|
24
25
|
private
|
25
26
|
|
26
|
-
# In a chain of method calls, we regard the top
|
27
|
+
# In a chain of method calls, we regard the top call node as the base
|
27
28
|
# for indentation of all lines following the first. For example:
|
28
29
|
# a.
|
29
30
|
# b c { block }. <-- b is indented relative to a
|
30
31
|
# d <-- d is indented relative to a
|
31
32
|
def left_hand_side(lhs)
|
32
|
-
while lhs.parent&.
|
33
|
+
while lhs.parent&.call_type? && lhs.parent.loc.dot && !lhs.parent.assignment_method?
|
33
34
|
lhs = lhs.parent
|
34
35
|
end
|
35
36
|
lhs
|
@@ -75,7 +75,7 @@ module RuboCop
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def aligned_token?(range, line)
|
78
|
-
aligned_words?(range, line) ||
|
78
|
+
aligned_words?(range, line) || aligned_assignment?(range, line)
|
79
79
|
end
|
80
80
|
|
81
81
|
def aligned_operator?(range, line)
|
@@ -83,13 +83,11 @@ module RuboCop
|
|
83
83
|
end
|
84
84
|
|
85
85
|
def aligned_words?(range, line)
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
def aligned_dot?(range, line)
|
90
|
-
char = line[range.column]
|
86
|
+
left_edge = range.column
|
87
|
+
return true if /\s\S/.match?(line[left_edge - 1, 2])
|
91
88
|
|
92
|
-
|
89
|
+
token = range.source
|
90
|
+
token == line[left_edge, token.length]
|
93
91
|
end
|
94
92
|
|
95
93
|
def aligned_assignment?(range, line)
|
@@ -116,11 +116,11 @@ module RuboCop
|
|
116
116
|
end
|
117
117
|
|
118
118
|
def only_forwards_all?(send_classifications)
|
119
|
-
send_classifications.
|
119
|
+
send_classifications.all? { |_, c, _, _| c == :all }
|
120
120
|
end
|
121
121
|
|
122
122
|
def add_forward_all_offenses(node, send_classifications, forwardable_args)
|
123
|
-
send_classifications.each do |send_node,
|
123
|
+
send_classifications.each do |send_node, _c, forward_rest, _forward_kwrest|
|
124
124
|
register_forward_all_offense(send_node, send_node, forward_rest)
|
125
125
|
end
|
126
126
|
|
@@ -133,7 +133,7 @@ module RuboCop
|
|
133
133
|
|
134
134
|
rest_arg, kwrest_arg, _block_arg = *forwardable_args
|
135
135
|
|
136
|
-
send_classifications.each do |send_node,
|
136
|
+
send_classifications.each do |send_node, _c, forward_rest, forward_kwrest|
|
137
137
|
if forward_rest
|
138
138
|
register_forward_args_offense(def_node.arguments, rest_arg)
|
139
139
|
register_forward_args_offense(send_node, forward_rest)
|
@@ -157,7 +157,7 @@ module RuboCop
|
|
157
157
|
end
|
158
158
|
|
159
159
|
def classify_send_nodes(def_node, send_nodes, referenced_lvars, forwardable_args)
|
160
|
-
send_nodes.
|
160
|
+
send_nodes.filter_map do |send_node|
|
161
161
|
classification_and_forwards = classification_and_forwards(
|
162
162
|
def_node,
|
163
163
|
send_node,
|
@@ -165,8 +165,10 @@ module RuboCop
|
|
165
165
|
forwardable_args
|
166
166
|
)
|
167
167
|
|
168
|
-
|
169
|
-
|
168
|
+
next unless classification_and_forwards
|
169
|
+
|
170
|
+
[send_node, *classification_and_forwards]
|
171
|
+
end
|
170
172
|
end
|
171
173
|
|
172
174
|
def classification_and_forwards(def_node, send_node, referenced_lvars, forwardable_args)
|
@@ -11,6 +11,15 @@ module RuboCop
|
|
11
11
|
# The `array1.intersect?(array2)` method is faster than
|
12
12
|
# `(array1 & array2).any?` and is more readable.
|
13
13
|
#
|
14
|
+
# In cases like the following, compatibility is not ensured,
|
15
|
+
# so it will not be detected when using block argument.
|
16
|
+
#
|
17
|
+
# [source,ruby]
|
18
|
+
# ----
|
19
|
+
# ([1] & [1,2]).any? { |x| false } # => false
|
20
|
+
# [1].intersect?([1,2]) { |x| false } # => true
|
21
|
+
# ----
|
22
|
+
#
|
14
23
|
# @safety
|
15
24
|
# This cop cannot guarantee that `array1` and `array2` are
|
16
25
|
# actually arrays while method `intersect?` is for arrays only.
|
@@ -68,16 +77,15 @@ module RuboCop
|
|
68
77
|
RESTRICT_ON_SEND = (STRAIGHT_METHODS + NEGATED_METHODS).freeze
|
69
78
|
|
70
79
|
def on_send(node)
|
80
|
+
return if (parent = node.parent) && (parent.block_type? || parent.numblock_type?)
|
71
81
|
return unless (receiver, argument, method_name = bad_intersection_check?(node))
|
72
82
|
|
73
83
|
message = message(receiver.source, argument.source, method_name)
|
74
84
|
|
75
85
|
add_offense(node, message: message) do |corrector|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
corrector.replace(node, "!#{receiver.source}.intersect?(#{argument.source})")
|
80
|
-
end
|
86
|
+
bang = straight?(method_name) ? '' : '!'
|
87
|
+
|
88
|
+
corrector.replace(node, "#{bang}#{receiver.source}.intersect?(#{argument.source})")
|
81
89
|
end
|
82
90
|
end
|
83
91
|
|
@@ -8,6 +8,11 @@ module RuboCop
|
|
8
8
|
# `==`, `equal?`, and `eql?` custom method definitions are allowed by default.
|
9
9
|
# These are customizable with `AllowedMethods` option.
|
10
10
|
#
|
11
|
+
# @safety
|
12
|
+
# This cop's autocorrection is unsafe because there is no guarantee that
|
13
|
+
# the constant `Foo` exists when autocorrecting `var.class.name == 'Foo'` to
|
14
|
+
# `var.instance_of?(Foo)`.
|
15
|
+
#
|
11
16
|
# @example
|
12
17
|
# # bad
|
13
18
|
# var.class == Date
|
@@ -25,6 +25,7 @@ module RuboCop
|
|
25
25
|
# # bad
|
26
26
|
# items.collect
|
27
27
|
# items.collect!
|
28
|
+
# items.collect_concat
|
28
29
|
# items.inject
|
29
30
|
# items.detect
|
30
31
|
# items.find_all
|
@@ -33,6 +34,7 @@ module RuboCop
|
|
33
34
|
# # good
|
34
35
|
# items.map
|
35
36
|
# items.map!
|
37
|
+
# items.flat_map
|
36
38
|
# items.reduce
|
37
39
|
# items.find
|
38
40
|
# items.select
|
@@ -93,8 +93,9 @@ module RuboCop
|
|
93
93
|
end
|
94
94
|
|
95
95
|
def same_collection_looping_block?(node, sibling)
|
96
|
-
(sibling
|
97
|
-
|
96
|
+
return false if sibling.nil? || (!sibling.block_type? && !sibling.numblock_type?)
|
97
|
+
|
98
|
+
sibling.method?(node.method_name) &&
|
98
99
|
sibling.receiver == node.receiver &&
|
99
100
|
sibling.send_node.arguments == node.send_node.arguments
|
100
101
|
end
|
@@ -40,9 +40,13 @@ module RuboCop
|
|
40
40
|
extend AutoCorrector
|
41
41
|
|
42
42
|
MSG = 'Do not use empty `case` condition, instead use an `if` expression.'
|
43
|
+
NOT_SUPPORTED_PARENT_TYPES = %i[return break next send csend].freeze
|
43
44
|
|
45
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
44
46
|
def on_case(case_node)
|
45
|
-
|
47
|
+
if case_node.condition || NOT_SUPPORTED_PARENT_TYPES.include?(case_node.parent&.type)
|
48
|
+
return
|
49
|
+
end
|
46
50
|
|
47
51
|
branch_bodies = [*case_node.when_branches.map(&:body), case_node.else_branch].compact
|
48
52
|
|
@@ -52,6 +56,7 @@ module RuboCop
|
|
52
56
|
|
53
57
|
add_offense(case_node.loc.keyword) { |corrector| autocorrect(corrector, case_node) }
|
54
58
|
end
|
59
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
55
60
|
|
56
61
|
private
|
57
62
|
|
@@ -4,13 +4,25 @@ module RuboCop
|
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
6
|
# Enforces the use of a single string formatting utility.
|
7
|
-
# Valid options include Kernel#format
|
7
|
+
# Valid options include `Kernel#format`, `Kernel#sprintf`, and `String#%`.
|
8
8
|
#
|
9
|
-
# The detection of String
|
9
|
+
# The detection of `String#%` cannot be implemented in a reliable
|
10
10
|
# manner for all cases, so only two scenarios are considered -
|
11
11
|
# if the first argument is a string literal and if the second
|
12
12
|
# argument is an array literal.
|
13
13
|
#
|
14
|
+
# Autocorrection will be applied when using argument is a literal or known built-in conversion
|
15
|
+
# methods such as `to_d`, `to_f`, `to_h`, `to_i`, `to_r`, `to_s`, and `to_sym` on variables,
|
16
|
+
# provided that their return value is not an array. For example, when using `to_s`,
|
17
|
+
# `'%s' % [1, 2, 3].to_s` can be autocorrected without any incompatibility:
|
18
|
+
#
|
19
|
+
# [source,ruby]
|
20
|
+
# ----
|
21
|
+
# '%s' % [1, 2, 3] #=> '1'
|
22
|
+
# format('%s', [1, 2, 3]) #=> '[1, 2, 3]'
|
23
|
+
# '%s' % [1, 2, 3].to_s #=> '[1, 2, 3]'
|
24
|
+
# ----
|
25
|
+
#
|
14
26
|
# @example EnforcedStyle: format (default)
|
15
27
|
# # bad
|
16
28
|
# puts sprintf('%10s', 'hoge')
|
@@ -42,6 +54,9 @@ module RuboCop
|
|
42
54
|
MSG = 'Favor `%<prefer>s` over `%<current>s`.'
|
43
55
|
RESTRICT_ON_SEND = %i[format sprintf %].freeze
|
44
56
|
|
57
|
+
# Known conversion methods whose return value is not an array.
|
58
|
+
AUTOCORRECTABLE_METHODS = %i[to_d to_f to_h to_i to_r to_s to_sym].freeze
|
59
|
+
|
45
60
|
# @!method formatter(node)
|
46
61
|
def_node_matcher :formatter, <<~PATTERN
|
47
62
|
{
|
@@ -53,7 +68,7 @@ module RuboCop
|
|
53
68
|
|
54
69
|
# @!method variable_argument?(node)
|
55
70
|
def_node_matcher :variable_argument?, <<~PATTERN
|
56
|
-
(send {str dstr} :%
|
71
|
+
(send {str dstr} :% #autocorrectable?)
|
57
72
|
PATTERN
|
58
73
|
|
59
74
|
def on_send(node)
|
@@ -70,6 +85,12 @@ module RuboCop
|
|
70
85
|
|
71
86
|
private
|
72
87
|
|
88
|
+
def autocorrectable?(node)
|
89
|
+
return true if node.lvar_type?
|
90
|
+
|
91
|
+
node.send_type? && !AUTOCORRECTABLE_METHODS.include?(node.method_name)
|
92
|
+
end
|
93
|
+
|
73
94
|
def message(detected_style)
|
74
95
|
format(MSG, prefer: method_name(style), current: method_name(detected_style))
|
75
96
|
end
|
@@ -55,6 +55,25 @@ module RuboCop
|
|
55
55
|
# foo || raise('exception') if something
|
56
56
|
# ok
|
57
57
|
#
|
58
|
+
# # bad
|
59
|
+
# define_method(:test) do
|
60
|
+
# if something
|
61
|
+
# work
|
62
|
+
# end
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# # good
|
66
|
+
# define_method(:test) do
|
67
|
+
# return unless something
|
68
|
+
#
|
69
|
+
# work
|
70
|
+
# end
|
71
|
+
#
|
72
|
+
# # also good
|
73
|
+
# define_method(:test) do
|
74
|
+
# work if something
|
75
|
+
# end
|
76
|
+
#
|
58
77
|
# @example AllowConsecutiveConditionals: false (default)
|
59
78
|
# # bad
|
60
79
|
# def test
|
@@ -110,6 +129,13 @@ module RuboCop
|
|
110
129
|
end
|
111
130
|
alias on_defs on_def
|
112
131
|
|
132
|
+
def on_block(node)
|
133
|
+
return unless node.method?(:define_method) || node.method?(:define_singleton_method)
|
134
|
+
|
135
|
+
on_def(node)
|
136
|
+
end
|
137
|
+
alias on_numblock on_block
|
138
|
+
|
113
139
|
def on_if(node)
|
114
140
|
return if accepted_form?(node)
|
115
141
|
|
@@ -136,7 +136,7 @@ module RuboCop
|
|
136
136
|
|
137
137
|
private
|
138
138
|
|
139
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
139
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
140
140
|
def check_branches(node, branches)
|
141
141
|
# return if any branch is empty. An empty branch can be an `if`
|
142
142
|
# without an `else` or a branch that contains only comments.
|
@@ -149,9 +149,15 @@ module RuboCop
|
|
149
149
|
branches.any? { |branch| single_child_branch?(branch) }
|
150
150
|
|
151
151
|
heads = branches.map { |branch| head(branch) }
|
152
|
-
|
152
|
+
|
153
|
+
return unless duplicated_expressions?(node, heads)
|
154
|
+
|
155
|
+
condition_variable = assignable_condition_value(node)
|
156
|
+
return if heads.first.assignment? && condition_variable == heads.first.name.to_s
|
157
|
+
|
158
|
+
check_expressions(node, heads, :before_condition)
|
153
159
|
end
|
154
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
160
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
155
161
|
|
156
162
|
def duplicated_expressions?(node, expressions)
|
157
163
|
unique_expressions = expressions.uniq
|
@@ -164,6 +170,14 @@ module RuboCop
|
|
164
170
|
node.condition.child_nodes.none? { |n| n.source == lhs.source if n.variable? }
|
165
171
|
end
|
166
172
|
|
173
|
+
def assignable_condition_value(node)
|
174
|
+
if node.condition.call_type?
|
175
|
+
(receiver = node.condition.receiver) ? receiver.source : node.condition.source
|
176
|
+
elsif node.condition.variable?
|
177
|
+
node.condition.source
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
167
181
|
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
168
182
|
def check_expressions(node, expressions, insert_position)
|
169
183
|
return if expressions.any?(&:nil?)
|
@@ -28,7 +28,7 @@ module RuboCop
|
|
28
28
|
MSG = 'Avoid multi-line chains of blocks.'
|
29
29
|
|
30
30
|
def on_block(node)
|
31
|
-
node.send_node.each_node(:send) do |send_node|
|
31
|
+
node.send_node.each_node(:send, :csend) do |send_node|
|
32
32
|
receiver = send_node.receiver
|
33
33
|
|
34
34
|
next unless (receiver&.block_type? || receiver&.numblock_type?) && receiver&.multiline?
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
|
40
40
|
MSG_IF = 'Avoid multi-line ternary operators, use `if` or `unless` instead.'
|
41
41
|
MSG_SINGLE_LINE = 'Avoid multi-line ternary operators, use single-line instead.'
|
42
|
-
SINGLE_LINE_TYPES = %i[return break next send].freeze
|
42
|
+
SINGLE_LINE_TYPES = %i[return break next send csend].freeze
|
43
43
|
|
44
44
|
def on_if(node)
|
45
45
|
return unless offense?(node)
|
@@ -27,24 +27,16 @@ module RuboCop
|
|
27
27
|
|
28
28
|
node.each_descendant(:if).select(&:ternary?).each do |nested_ternary|
|
29
29
|
add_offense(nested_ternary) do |corrector|
|
30
|
-
|
31
|
-
next if part_of_ignored_node?(if_node)
|
30
|
+
next if part_of_ignored_node?(node)
|
32
31
|
|
33
|
-
autocorrect(corrector,
|
34
|
-
ignore_node(
|
32
|
+
autocorrect(corrector, node)
|
33
|
+
ignore_node(node)
|
35
34
|
end
|
36
35
|
end
|
37
36
|
end
|
38
37
|
|
39
38
|
private
|
40
39
|
|
41
|
-
def if_node(node)
|
42
|
-
node = node.parent
|
43
|
-
return node if node.if_type?
|
44
|
-
|
45
|
-
if_node(node)
|
46
|
-
end
|
47
|
-
|
48
40
|
def autocorrect(corrector, if_node)
|
49
41
|
replace_loc_and_whitespace(corrector, if_node.loc.question, "\n")
|
50
42
|
replace_loc_and_whitespace(corrector, if_node.loc.colon, "\nelse\n")
|
@@ -23,6 +23,7 @@ module RuboCop
|
|
23
23
|
MSG = 'Redundant dot detected.'
|
24
24
|
RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
|
25
25
|
|
26
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
26
27
|
def on_send(node)
|
27
28
|
return unless (dot = node.loc.dot)
|
28
29
|
return if node.receiver.const_type? || !node.arguments.one?
|
@@ -33,8 +34,12 @@ module RuboCop
|
|
33
34
|
add_offense(dot) do |corrector|
|
34
35
|
wrap_in_parentheses_if_chained(corrector, node)
|
35
36
|
corrector.replace(dot, ' ')
|
37
|
+
|
38
|
+
selector = node.loc.selector
|
39
|
+
corrector.insert_after(selector, ' ') if selector.end_pos == rhs.source_range.begin_pos
|
36
40
|
end
|
37
41
|
end
|
42
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
38
43
|
|
39
44
|
private
|
40
45
|
|
@@ -54,6 +59,7 @@ module RuboCop
|
|
54
59
|
|
55
60
|
def wrap_in_parentheses_if_chained(corrector, node)
|
56
61
|
return unless node.parent&.call_type?
|
62
|
+
return if node.parent.first_argument == node
|
57
63
|
|
58
64
|
operator = node.loc.selector
|
59
65
|
|
@@ -114,7 +114,7 @@ module RuboCop
|
|
114
114
|
if node.parent&.assignment?
|
115
115
|
replace_begin_with_statement(corrector, offense_range, node)
|
116
116
|
else
|
117
|
-
corrector
|
117
|
+
remove_begin(corrector, offense_range, node)
|
118
118
|
end
|
119
119
|
|
120
120
|
if use_modifier_form_after_multiline_begin_block?(node)
|
@@ -136,6 +136,14 @@ module RuboCop
|
|
136
136
|
restore_removed_comments(corrector, offense_range, node, first_child)
|
137
137
|
end
|
138
138
|
|
139
|
+
def remove_begin(corrector, offense_range, node)
|
140
|
+
if node.parent.respond_to?(:endless?) && node.parent.endless?
|
141
|
+
offense_range = range_with_surrounding_space(offense_range, newlines: true)
|
142
|
+
end
|
143
|
+
|
144
|
+
corrector.remove(offense_range)
|
145
|
+
end
|
146
|
+
|
139
147
|
# Restore comments that occur between "begin" and "first_child".
|
140
148
|
# These comments will be moved to above the assignment line.
|
141
149
|
def restore_removed_comments(corrector, offense_range, node, first_child)
|
@@ -70,19 +70,11 @@ module RuboCop
|
|
70
70
|
|
71
71
|
def replacement_condition(node)
|
72
72
|
condition = node.condition.source
|
73
|
-
expression =
|
73
|
+
expression = redundant_condition_inverted?(node) ? "!(#{condition})" : condition
|
74
74
|
|
75
75
|
node.elsif? ? indented_else_node(expression, node) : expression
|
76
76
|
end
|
77
77
|
|
78
|
-
def invert_expression?(node)
|
79
|
-
(
|
80
|
-
(node.if? || node.elsif? || node.ternary?) && redundant_condition_inverted?(node)
|
81
|
-
) || (
|
82
|
-
node.unless? && redundant_condition?(node)
|
83
|
-
)
|
84
|
-
end
|
85
|
-
|
86
78
|
def indented_else_node(expression, node)
|
87
79
|
"else\n#{indentation(node)}#{expression}"
|
88
80
|
end
|