rubocop 1.66.0 → 1.67.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 +15 -6
- data/config/internal_affairs.yml +11 -0
- data/lib/rubocop/cli/command/auto_generate_config.rb +6 -7
- data/lib/rubocop/cli/command/lsp.rb +2 -2
- data/lib/rubocop/comment_config.rb +4 -8
- data/lib/rubocop/config.rb +4 -16
- data/lib/rubocop/config_loader.rb +14 -8
- data/lib/rubocop/config_loader_resolver.rb +3 -3
- data/lib/rubocop/config_validator.rb +7 -10
- data/lib/rubocop/cop/base.rb +6 -2
- data/lib/rubocop/cop/bundler/gem_version.rb +1 -0
- data/lib/rubocop/cop/cop.rb +8 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/cop_description.rb +0 -4
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/redundant_message_argument.rb +6 -21
- data/lib/rubocop/cop/internal_affairs/redundant_source_range.rb +8 -1
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +0 -5
- data/lib/rubocop/cop/internal_affairs.rb +16 -0
- data/lib/rubocop/cop/layout/access_modifier_indentation.rb +5 -1
- data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +8 -0
- data/lib/rubocop/cop/layout/indentation_width.rb +4 -5
- data/lib/rubocop/cop/layout/leading_comment_space.rb +28 -1
- data/lib/rubocop/cop/lint/ambiguous_range.rb +4 -1
- data/lib/rubocop/cop/lint/big_decimal_new.rb +4 -7
- data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
- data/lib/rubocop/cop/lint/duplicate_set_element.rb +74 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +0 -3
- data/lib/rubocop/cop/lint/erb_new_arguments.rb +1 -1
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +10 -4
- data/lib/rubocop/cop/lint/it_without_arguments_in_block.rb +5 -14
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +25 -2
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -6
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -1
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +105 -41
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -1
- data/lib/rubocop/cop/lint/uri_regexp.rb +25 -7
- data/lib/rubocop/cop/mixin/percent_array.rb +1 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +3 -2
- data/lib/rubocop/cop/naming/inclusive_language.rb +12 -3
- data/lib/rubocop/cop/naming/predicate_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +2 -2
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +12 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +10 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +46 -6
- data/lib/rubocop/cop/style/block_delimiters.rb +14 -1
- data/lib/rubocop/cop/style/collection_compact.rb +10 -10
- data/lib/rubocop/cop/style/combinable_loops.rb +7 -0
- data/lib/rubocop/cop/style/commented_keyword.rb +7 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/data_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/empty_else.rb +1 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/eval_with_location.rb +1 -1
- data/lib/rubocop/cop/style/guard_clause.rb +1 -1
- data/lib/rubocop/cop/style/hash_each_methods.rb +6 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +2 -2
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -1
- data/lib/rubocop/cop/style/if_with_semicolon.rb +16 -5
- data/lib/rubocop/cop/style/lambda.rb +1 -1
- data/lib/rubocop/cop/style/magic_comment_format.rb +3 -8
- data/lib/rubocop/cop/style/map_into_array.rb +54 -10
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +12 -7
- data/lib/rubocop/cop/style/multiline_memoization.rb +1 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +1 -1
- data/lib/rubocop/cop/style/nested_parenthesized_calls.rb +1 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +4 -0
- data/lib/rubocop/cop/style/operator_method_call.rb +25 -6
- data/lib/rubocop/cop/style/redundant_begin.rb +4 -0
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +3 -3
- data/lib/rubocop/cop/style/redundant_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/rescue_modifier.rb +13 -1
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +54 -12
- data/lib/rubocop/cop/style/safe_navigation.rb +92 -50
- data/lib/rubocop/cop/style/select_by_regexp.rb +9 -6
- data/lib/rubocop/cop/style/semicolon.rb +1 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +1 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +8 -1
- data/lib/rubocop/cop/util.rb +1 -1
- data/lib/rubocop/cops_documentation_generator.rb +73 -34
- data/lib/rubocop/file_finder.rb +9 -4
- data/lib/rubocop/lsp/runtime.rb +2 -0
- data/lib/rubocop/lsp/server.rb +0 -1
- data/lib/rubocop/rspec/expect_offense.rb +1 -0
- data/lib/rubocop/runner.rb +3 -0
- data/lib/rubocop/server/cache.rb +6 -1
- data/lib/rubocop/server/core.rb +1 -0
- data/lib/rubocop/target_ruby.rb +12 -12
- data/lib/rubocop/version.rb +3 -1
- data/lib/rubocop/yaml_duplication_checker.rb +20 -26
- data/lib/rubocop.rb +2 -0
- metadata +12 -10
@@ -13,8 +13,10 @@ module RuboCop
|
|
13
13
|
# return value of `Enumerable#map` is an `Array`. They are not autocorrected
|
14
14
|
# when a return value could be used because these types differ.
|
15
15
|
#
|
16
|
-
# NOTE: It only detects when the mapping destination is
|
17
|
-
# initialized as an empty array and referred to only by the
|
16
|
+
# NOTE: It only detects when the mapping destination is either:
|
17
|
+
# * a local variable initialized as an empty array and referred to only by the
|
18
|
+
# pushing operation;
|
19
|
+
# * or, if it is the single block argument to a `[].tap` block.
|
18
20
|
# This is because, if not, it's challenging to statically guarantee that the
|
19
21
|
# mapping destination variable remains an empty array:
|
20
22
|
#
|
@@ -42,6 +44,14 @@ module RuboCop
|
|
42
44
|
# # good
|
43
45
|
# dest = src.map { |e| e * 2 }
|
44
46
|
#
|
47
|
+
# # bad
|
48
|
+
# [].tap do |dest|
|
49
|
+
# src.each { |e| dest << e * 2 }
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# # good
|
53
|
+
# dest = src.map { |e| e * 2 }
|
54
|
+
#
|
45
55
|
# # good - contains another operation
|
46
56
|
# dest = []
|
47
57
|
# src.each { |e| dest << e * 2; puts e }
|
@@ -56,7 +66,7 @@ module RuboCop
|
|
56
66
|
# @!method each_block_with_push?(node)
|
57
67
|
def_node_matcher :each_block_with_push?, <<-PATTERN
|
58
68
|
[
|
59
|
-
^({begin kwbegin} ...)
|
69
|
+
^({begin kwbegin block} ...)
|
60
70
|
({block numblock} (send !{nil? self} :each) _
|
61
71
|
(send (lvar _) {:<< :push :append} {send lvar begin}))
|
62
72
|
]
|
@@ -74,6 +84,16 @@ module RuboCop
|
|
74
84
|
)
|
75
85
|
PATTERN
|
76
86
|
|
87
|
+
# @!method empty_array_tap(node)
|
88
|
+
def_node_matcher :empty_array_tap, <<~PATTERN
|
89
|
+
^^$(
|
90
|
+
block
|
91
|
+
(send (array) :tap)
|
92
|
+
(args (arg _))
|
93
|
+
...
|
94
|
+
)
|
95
|
+
PATTERN
|
96
|
+
|
77
97
|
# @!method lvar_ref?(node, name)
|
78
98
|
def_node_matcher :lvar_ref?, '(lvar %1)'
|
79
99
|
|
@@ -89,9 +109,14 @@ module RuboCop
|
|
89
109
|
return unless each_block_with_push?(node)
|
90
110
|
|
91
111
|
dest_var = find_dest_var(node)
|
92
|
-
|
93
|
-
|
94
|
-
|
112
|
+
|
113
|
+
if offending_empty_array_tap?(node, dest_var)
|
114
|
+
asgn = dest_var.declaration_node
|
115
|
+
else
|
116
|
+
return unless (asgn = find_closest_assignment(node, dest_var))
|
117
|
+
return unless empty_array_asgn?(asgn)
|
118
|
+
return unless dest_used_only_for_mapping?(node, dest_var, asgn)
|
119
|
+
end
|
95
120
|
|
96
121
|
register_offense(node, dest_var, asgn)
|
97
122
|
end
|
@@ -108,6 +133,15 @@ module RuboCop
|
|
108
133
|
candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
|
109
134
|
end
|
110
135
|
|
136
|
+
def offending_empty_array_tap?(node, dest_var)
|
137
|
+
return false unless (tap_block_node = empty_array_tap(dest_var.declaration_node))
|
138
|
+
|
139
|
+
# A `tap` block only offends if the array push is the only thing in it;
|
140
|
+
# otherwise we cannot guarantee that the block variable is still an empty
|
141
|
+
# array when pushed to.
|
142
|
+
tap_block_node.body == node
|
143
|
+
end
|
144
|
+
|
111
145
|
def find_closest_assignment(block, dest_var)
|
112
146
|
dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
|
113
147
|
node.source_range.end_pos < block.source_range.begin_pos
|
@@ -127,7 +161,13 @@ module RuboCop
|
|
127
161
|
next if return_value_used?(block)
|
128
162
|
|
129
163
|
corrector.replace(block.send_node.selector, new_method_name)
|
130
|
-
|
164
|
+
|
165
|
+
if (tap_block_node = empty_array_tap(dest_var.declaration_node))
|
166
|
+
remove_tap(corrector, block, tap_block_node)
|
167
|
+
else
|
168
|
+
remove_assignment(corrector, asgn)
|
169
|
+
end
|
170
|
+
|
131
171
|
correct_push_node(corrector, block.body)
|
132
172
|
correct_return_value_handling(corrector, block, dest_var)
|
133
173
|
end
|
@@ -147,10 +187,8 @@ module RuboCop
|
|
147
187
|
false
|
148
188
|
when :begin, :kwbegin
|
149
189
|
!node.right_sibling && return_value_used?(parent)
|
150
|
-
when :block, :numblock
|
151
|
-
!parent.void_context?
|
152
190
|
else
|
153
|
-
|
191
|
+
!parent.respond_to?(:void_context?) || !parent.void_context?
|
154
192
|
end
|
155
193
|
end
|
156
194
|
|
@@ -161,6 +199,12 @@ module RuboCop
|
|
161
199
|
corrector.remove(range)
|
162
200
|
end
|
163
201
|
|
202
|
+
def remove_tap(corrector, node, block_node)
|
203
|
+
range = range_between(block_node.source_range.begin_pos, node.source_range.begin_pos)
|
204
|
+
corrector.remove(range)
|
205
|
+
corrector.remove(range_with_surrounding_space(block_node.loc.end, side: :left))
|
206
|
+
end
|
207
|
+
|
164
208
|
def correct_push_node(corrector, push_node)
|
165
209
|
range = push_node.source_range
|
166
210
|
arg_range = push_node.first_argument.source_range
|
@@ -7,6 +7,8 @@ module RuboCop
|
|
7
7
|
# Style omit_parentheses
|
8
8
|
# rubocop:disable Metrics/ModuleLength, Metrics/CyclomaticComplexity
|
9
9
|
module OmitParentheses
|
10
|
+
include RangeHelp
|
11
|
+
|
10
12
|
TRAILING_WHITESPACE_REGEX = /\s+\Z/.freeze
|
11
13
|
OMIT_MSG = 'Omit parentheses for method calls with arguments.'
|
12
14
|
private_constant :OMIT_MSG
|
@@ -30,10 +32,13 @@ module RuboCop
|
|
30
32
|
end
|
31
33
|
|
32
34
|
def autocorrect(corrector, node)
|
35
|
+
range = args_begin(node)
|
33
36
|
if parentheses_at_the_end_of_multiline_call?(node)
|
34
|
-
|
37
|
+
# Whitespace after line continuation (`\ `) is a syntax error
|
38
|
+
with_whitespace = range_with_surrounding_space(range, side: :right, newlines: false)
|
39
|
+
corrector.replace(with_whitespace, ' \\')
|
35
40
|
else
|
36
|
-
corrector.replace(
|
41
|
+
corrector.replace(range, ' ')
|
37
42
|
end
|
38
43
|
corrector.remove(node.loc.end)
|
39
44
|
end
|
@@ -47,11 +52,11 @@ module RuboCop
|
|
47
52
|
node.each_ancestor(:def, :defs).any?(&:endless?) && node.arguments.any?
|
48
53
|
end
|
49
54
|
|
50
|
-
def require_parentheses_for_hash_value_omission?(node)
|
55
|
+
def require_parentheses_for_hash_value_omission?(node) # rubocop:disable Metrics/PerceivedComplexity
|
51
56
|
return false unless (last_argument = node.last_argument)
|
52
57
|
return false if !last_argument.hash_type? || !last_argument.pairs.last&.value_omission?
|
53
58
|
|
54
|
-
node.parent&.conditional? || !last_expression?(node)
|
59
|
+
node.parent&.conditional? || node.parent&.single_line? || !last_expression?(node)
|
55
60
|
end
|
56
61
|
|
57
62
|
# Require hash value omission be enclosed in parentheses to prevent the following issue:
|
@@ -127,7 +132,7 @@ module RuboCop
|
|
127
132
|
end
|
128
133
|
|
129
134
|
def call_in_single_line_inheritance?(node)
|
130
|
-
node.parent&.class_type? && node.parent
|
135
|
+
node.parent&.class_type? && node.parent.single_line?
|
131
136
|
end
|
132
137
|
|
133
138
|
def call_with_ambiguous_arguments?(node) # rubocop:disable Metrics/PerceivedComplexity
|
@@ -147,7 +152,7 @@ module RuboCop
|
|
147
152
|
end
|
148
153
|
|
149
154
|
def call_in_argument_with_block?(node)
|
150
|
-
parent = node.parent&.block_type? && node.parent
|
155
|
+
parent = node.parent&.block_type? && node.parent.parent
|
151
156
|
return false unless parent
|
152
157
|
|
153
158
|
parent.call_type? || parent.super_type? || parent.yield_type?
|
@@ -211,7 +216,7 @@ module RuboCop
|
|
211
216
|
|
212
217
|
def unary_literal?(node)
|
213
218
|
(node.numeric_type? && node.sign?) ||
|
214
|
-
(node.parent&.send_type? && node.parent
|
219
|
+
(node.parent&.send_type? && node.parent.unary_operation?)
|
215
220
|
end
|
216
221
|
|
217
222
|
def assigned_before?(node, target)
|
@@ -39,7 +39,7 @@ module RuboCop
|
|
39
39
|
next if allowed_omission?(nested)
|
40
40
|
|
41
41
|
message = format(MSG, source: nested.source)
|
42
|
-
add_offense(nested
|
42
|
+
add_offense(nested, message: message) do |corrector|
|
43
43
|
autocorrect(corrector, nested)
|
44
44
|
end
|
45
45
|
end
|
@@ -22,6 +22,9 @@ module RuboCop
|
|
22
22
|
|
23
23
|
MSG = 'Redundant dot detected.'
|
24
24
|
RESTRICT_ON_SEND = %i[| ^ & <=> == === =~ > >= < <= << >> + - * / % ** ~ ! != !~].freeze
|
25
|
+
INVALID_SYNTAX_ARG_TYPES = %i[
|
26
|
+
splat kwsplat forwarded_args forwarded_restarg forwarded_kwrestarg block_pass
|
27
|
+
].freeze
|
25
28
|
|
26
29
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
27
30
|
def on_send(node)
|
@@ -29,14 +32,16 @@ module RuboCop
|
|
29
32
|
return if node.receiver.const_type? || !node.arguments.one?
|
30
33
|
|
31
34
|
_lhs, _op, rhs = *node
|
32
|
-
|
35
|
+
if !rhs || method_call_with_parenthesized_arg?(rhs) || invalid_syntax_argument?(rhs)
|
36
|
+
return
|
37
|
+
end
|
33
38
|
|
34
39
|
add_offense(dot) do |corrector|
|
35
40
|
wrap_in_parentheses_if_chained(corrector, node)
|
36
41
|
corrector.replace(dot, ' ')
|
37
42
|
|
38
43
|
selector = node.loc.selector
|
39
|
-
corrector.insert_after(selector, ' ') if
|
44
|
+
corrector.insert_after(selector, ' ') if insert_space_after?(node)
|
40
45
|
end
|
41
46
|
end
|
42
47
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity
|
@@ -50,11 +55,10 @@ module RuboCop
|
|
50
55
|
argument.children.first && argument.parent.parenthesized?
|
51
56
|
end
|
52
57
|
|
53
|
-
def
|
54
|
-
|
55
|
-
return true if argument.hash_type? && argument.children.first&.forwarded_kwrestarg_type?
|
58
|
+
def invalid_syntax_argument?(argument)
|
59
|
+
type = argument.hash_type? ? argument.children.first&.type : argument.type
|
56
60
|
|
57
|
-
|
61
|
+
INVALID_SYNTAX_ARG_TYPES.include?(type)
|
58
62
|
end
|
59
63
|
|
60
64
|
def wrap_in_parentheses_if_chained(corrector, node)
|
@@ -67,6 +71,21 @@ module RuboCop
|
|
67
71
|
corrector.insert_after(operator, ' ')
|
68
72
|
corrector.wrap(node, '(', ')')
|
69
73
|
end
|
74
|
+
|
75
|
+
def insert_space_after?(node)
|
76
|
+
_lhs, op, rhs = *node
|
77
|
+
selector = node.loc.selector
|
78
|
+
|
79
|
+
return true if selector.end_pos == rhs.source_range.begin_pos
|
80
|
+
return false if node.parent&.call_type? # if chained, a space is already added
|
81
|
+
|
82
|
+
# For `/` operations, if the RHS starts with a `(` without space,
|
83
|
+
# add one to avoid a syntax error.
|
84
|
+
range = selector.end.join(rhs.source_range.begin)
|
85
|
+
return true if op == :/ && range.source == '('
|
86
|
+
|
87
|
+
false
|
88
|
+
end
|
70
89
|
end
|
71
90
|
end
|
72
91
|
end
|
@@ -68,6 +68,10 @@ module RuboCop
|
|
68
68
|
|
69
69
|
MSG = 'Redundant `begin` block detected.'
|
70
70
|
|
71
|
+
def self.autocorrect_incompatible_with
|
72
|
+
[Style::BlockDelimiters]
|
73
|
+
end
|
74
|
+
|
71
75
|
# @!method offensive_kwbegins(node)
|
72
76
|
def_node_search :offensive_kwbegins, <<~PATTERN
|
73
77
|
[(kwbegin ...) !#allowable_kwbegin?]
|
@@ -122,7 +122,7 @@ module RuboCop
|
|
122
122
|
end
|
123
123
|
|
124
124
|
def redundant_line_continuation?(range)
|
125
|
-
return true unless (node = find_node_for_line(range.
|
125
|
+
return true unless (node = find_node_for_line(range.last_line))
|
126
126
|
return false if argument_newline?(node)
|
127
127
|
|
128
128
|
source = node.parent ? node.parent.source : node.source
|
@@ -160,9 +160,9 @@ module RuboCop
|
|
160
160
|
end
|
161
161
|
# rubocop:enable Metrics/AbcSize
|
162
162
|
|
163
|
-
def find_node_for_line(
|
163
|
+
def find_node_for_line(last_line)
|
164
164
|
processed_source.ast.each_node do |node|
|
165
|
-
return node if
|
165
|
+
return node if node.respond_to?(:expression) && node.expression&.last_line == last_line
|
166
166
|
end
|
167
167
|
end
|
168
168
|
|
@@ -160,7 +160,7 @@ module RuboCop
|
|
160
160
|
return if node.semantic_operator? && begin_node.parent
|
161
161
|
return if node.multiline? && allow_in_multiline_conditions?
|
162
162
|
return if ALLOWED_NODE_TYPES.include?(begin_node.parent&.type)
|
163
|
-
return if begin_node.parent&.if_type? && begin_node.parent
|
163
|
+
return if begin_node.parent&.if_type? && begin_node.parent.ternary?
|
164
164
|
|
165
165
|
'a logical expression'
|
166
166
|
elsif node.respond_to?(:comparison_method?) && node.comparison_method?
|
@@ -103,7 +103,7 @@ module RuboCop
|
|
103
103
|
next unless sibling.is_a?(AST::Node)
|
104
104
|
|
105
105
|
sibling = sibling_node(sibling)
|
106
|
-
break unless sibling&.send_type? && sibling
|
106
|
+
break unless sibling&.send_type? && sibling.method?(node.method_name)
|
107
107
|
break unless sibling.arguments? && !sibling.receiver
|
108
108
|
break unless in_same_section?(sibling, node)
|
109
109
|
break unless node.first_argument.str_type? && sibling.first_argument.str_type?
|
@@ -75,7 +75,7 @@ module RuboCop
|
|
75
75
|
|
76
76
|
corrector.remove(range_between(operation.source_range.end_pos, node.source_range.end_pos))
|
77
77
|
corrector.insert_before(operation, "begin\n#{node_indentation}")
|
78
|
-
corrector.insert_after(operation, <<~RESCUE_CLAUSE.chop)
|
78
|
+
corrector.insert_after(heredoc_end(operation) || operation, <<~RESCUE_CLAUSE.chop)
|
79
79
|
|
80
80
|
#{node_offset}rescue
|
81
81
|
#{node_indentation}#{rescue_args.source}
|
@@ -92,6 +92,18 @@ module RuboCop
|
|
92
92
|
end
|
93
93
|
[node_indentation, node_offset]
|
94
94
|
end
|
95
|
+
|
96
|
+
def heredoc_end(node)
|
97
|
+
return unless node.call_type?
|
98
|
+
|
99
|
+
heredoc = node.arguments.reverse.find do |argument|
|
100
|
+
argument.respond_to?(:heredoc?) && argument.heredoc?
|
101
|
+
end
|
102
|
+
|
103
|
+
return unless heredoc
|
104
|
+
|
105
|
+
heredoc.loc.heredoc_end
|
106
|
+
end
|
95
107
|
end
|
96
108
|
end
|
97
109
|
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
# Checks
|
6
|
+
# Checks for predicate method definitions that return `nil`.
|
7
|
+
# A predicate method should only return a boolean value.
|
7
8
|
#
|
8
9
|
# @safety
|
9
10
|
# Autocorrection is marked as unsafe because the change of the return value
|
@@ -31,6 +32,24 @@ module RuboCop
|
|
31
32
|
# do_something?
|
32
33
|
# end
|
33
34
|
#
|
35
|
+
# # bad
|
36
|
+
# def foo?
|
37
|
+
# if condition
|
38
|
+
# nil
|
39
|
+
# else
|
40
|
+
# true
|
41
|
+
# end
|
42
|
+
# end
|
43
|
+
#
|
44
|
+
# # good
|
45
|
+
# def foo?
|
46
|
+
# if condition
|
47
|
+
# false
|
48
|
+
# else
|
49
|
+
# true
|
50
|
+
# end
|
51
|
+
# end
|
52
|
+
#
|
34
53
|
# @example AllowedMethods: ['foo?']
|
35
54
|
# # good
|
36
55
|
# def foo?
|
@@ -64,24 +83,25 @@ module RuboCop
|
|
64
83
|
return if allowed_method?(node.method_name) || matches_allowed_pattern?(node.method_name)
|
65
84
|
return unless (body = node.body)
|
66
85
|
|
67
|
-
body.each_descendant(:return)
|
68
|
-
register_offense(return_node, 'return false') if return_nil?(return_node)
|
69
|
-
end
|
70
|
-
|
71
|
-
return unless (nil_node = nil_node_at_the_end_of_method_body(body))
|
86
|
+
body.each_descendant(:return) { |return_node| handle_return(return_node) }
|
72
87
|
|
73
|
-
|
88
|
+
handle_implicit_return_values(body)
|
74
89
|
end
|
75
90
|
alias on_defs on_def
|
76
91
|
|
77
92
|
private
|
78
93
|
|
79
|
-
def
|
80
|
-
return
|
81
|
-
return
|
82
|
-
return unless
|
94
|
+
def last_node_of_type(node, type)
|
95
|
+
return unless node
|
96
|
+
return node if node_type?(node, type)
|
97
|
+
return unless node.begin_type?
|
98
|
+
return unless (last_child = node.children.last)
|
99
|
+
|
100
|
+
last_child if last_child.is_a?(AST::Node) && node_type?(last_child, type)
|
101
|
+
end
|
83
102
|
|
84
|
-
|
103
|
+
def node_type?(node, type)
|
104
|
+
node.type == type.to_sym
|
85
105
|
end
|
86
106
|
|
87
107
|
def register_offense(offense_node, replacement)
|
@@ -89,6 +109,28 @@ module RuboCop
|
|
89
109
|
corrector.replace(offense_node, replacement)
|
90
110
|
end
|
91
111
|
end
|
112
|
+
|
113
|
+
def handle_implicit_return_values(node)
|
114
|
+
handle_if(last_node_of_type(node, :if))
|
115
|
+
handle_nil(last_node_of_type(node, :nil))
|
116
|
+
end
|
117
|
+
|
118
|
+
def handle_return(return_node)
|
119
|
+
register_offense(return_node, 'return false') if return_nil?(return_node)
|
120
|
+
end
|
121
|
+
|
122
|
+
def handle_nil(nil_node)
|
123
|
+
return unless nil_node
|
124
|
+
|
125
|
+
register_offense(nil_node, 'false')
|
126
|
+
end
|
127
|
+
|
128
|
+
def handle_if(if_node)
|
129
|
+
return unless if_node
|
130
|
+
|
131
|
+
handle_implicit_return_values(if_node.if_branch)
|
132
|
+
handle_implicit_return_values(if_node.else_branch)
|
133
|
+
end
|
92
134
|
end
|
93
135
|
end
|
94
136
|
end
|