rubocop 1.82.1 → 1.84.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/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/config/default.yml +44 -0
- data/lib/rubocop/cli/command/lsp.rb +1 -1
- data/lib/rubocop/cli.rb +2 -1
- data/lib/rubocop/comment_config.rb +1 -0
- data/lib/rubocop/cop/bundler/gem_version.rb +28 -28
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +20 -2
- data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +8 -8
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +9 -9
- data/lib/rubocop/cop/internal_affairs/useless_message_assertion.rb +4 -4
- data/lib/rubocop/cop/layout/case_indentation.rb +3 -1
- data/lib/rubocop/cop/layout/class_structure.rb +12 -5
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +32 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +26 -0
- data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +25 -25
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +34 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +102 -9
- data/lib/rubocop/cop/layout/line_length.rb +5 -2
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +57 -57
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +56 -56
- data/lib/rubocop/cop/layout/space_in_lambda_literal.rb +8 -8
- data/lib/rubocop/cop/lint/duplicate_methods.rb +57 -5
- data/lib/rubocop/cop/lint/float_comparison.rb +1 -1
- data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -1
- data/lib/rubocop/cop/lint/struct_new_override.rb +17 -1
- data/lib/rubocop/cop/lint/to_json.rb +12 -16
- data/lib/rubocop/cop/lint/useless_or.rb +1 -1
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +1 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +4 -4
- data/lib/rubocop/cop/naming/predicate_prefix.rb +11 -11
- data/lib/rubocop/cop/offense.rb +2 -1
- data/lib/rubocop/cop/security/json_load.rb +1 -1
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +1 -2
- data/lib/rubocop/cop/style/documentation.rb +6 -6
- data/lib/rubocop/cop/style/documentation_method.rb +8 -8
- data/lib/rubocop/cop/style/empty_class_definition.rb +144 -0
- data/lib/rubocop/cop/style/guard_clause.rb +7 -4
- data/lib/rubocop/cop/style/hash_lookup_method.rb +94 -0
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +12 -12
- data/lib/rubocop/cop/style/lambda_call.rb +8 -8
- data/lib/rubocop/cop/style/module_member_existence_check.rb +56 -13
- data/lib/rubocop/cop/style/negative_array_index.rb +218 -0
- data/lib/rubocop/cop/style/preferred_hash_methods.rb +12 -12
- data/lib/rubocop/cop/style/redundant_condition.rb +1 -1
- data/lib/rubocop/cop/style/reverse_find.rb +51 -0
- data/lib/rubocop/cop/team.rb +3 -3
- data/lib/rubocop/cop/variable_force/branch.rb +28 -4
- data/lib/rubocop/formatter/clang_style_formatter.rb +5 -2
- data/lib/rubocop/formatter/formatter_set.rb +1 -1
- data/lib/rubocop/formatter/tap_formatter.rb +5 -2
- data/lib/rubocop/remote_config.rb +5 -2
- data/lib/rubocop/rspec/shared_contexts.rb +4 -0
- data/lib/rubocop/rspec/support.rb +1 -0
- data/lib/rubocop/target_ruby.rb +3 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +4 -0
- metadata +9 -5
|
@@ -11,7 +11,7 @@ module RuboCop
|
|
|
11
11
|
#
|
|
12
12
|
# @safety
|
|
13
13
|
# As shown in the examples below, there are generally two possible ways to correct the
|
|
14
|
-
# offense, but this cop
|
|
14
|
+
# offense, but this cop's autocorrection always chooses the option that preserves the
|
|
15
15
|
# current behavior. While this does not change how the code behaves, that option is not
|
|
16
16
|
# necessarily the appropriate fix in every situation. For this reason, the autocorrection
|
|
17
17
|
# provided by this cop is considered unsafe.
|
|
@@ -125,7 +125,7 @@ module RuboCop
|
|
|
125
125
|
return if dispatch_node.assignment_method?
|
|
126
126
|
return if dispatch_node.parenthesized?
|
|
127
127
|
return if dispatch_node.parent && parentheses?(dispatch_node.parent)
|
|
128
|
-
return if last_expression?(dispatch_node) && !
|
|
128
|
+
return if last_expression?(dispatch_node) && !requires_parentheses_context?(dispatch_node)
|
|
129
129
|
|
|
130
130
|
def_node = node.each_ancestor(:call, :super, :yield).first
|
|
131
131
|
|
|
@@ -164,11 +164,11 @@ module RuboCop
|
|
|
164
164
|
!assignment_node.right_sibling
|
|
165
165
|
end
|
|
166
166
|
|
|
167
|
-
def
|
|
168
|
-
parent =
|
|
167
|
+
def requires_parentheses_context?(node)
|
|
168
|
+
parent = node.parent
|
|
169
169
|
return false unless parent
|
|
170
170
|
|
|
171
|
-
parent.type?(:call, :super, :yield)
|
|
171
|
+
parent.type?(:call, :if, :super, :until, :while, :yield)
|
|
172
172
|
end
|
|
173
173
|
|
|
174
174
|
def breakdown_value_types_of_hash(hash_node)
|
|
@@ -63,17 +63,17 @@ module RuboCop
|
|
|
63
63
|
# end
|
|
64
64
|
#
|
|
65
65
|
# @example UseSorbetSigs: false (default)
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
66
|
+
# # bad
|
|
67
|
+
# sig { returns(String) }
|
|
68
|
+
# def is_this_thing_on
|
|
69
|
+
# "yes"
|
|
70
|
+
# end
|
|
71
|
+
#
|
|
72
|
+
# # good - Sorbet signature is not evaluated
|
|
73
|
+
# sig { returns(String) }
|
|
74
|
+
# def is_this_thing_on?
|
|
75
|
+
# "yes"
|
|
76
|
+
# end
|
|
77
77
|
#
|
|
78
78
|
# @example UseSorbetSigs: true
|
|
79
79
|
# # bad
|
data/lib/rubocop/cop/offense.rb
CHANGED
|
@@ -139,7 +139,8 @@ module RuboCop
|
|
|
139
139
|
# @return [Parser::Source::Range]
|
|
140
140
|
# the range of the code that is highlighted
|
|
141
141
|
def highlighted_area
|
|
142
|
-
Parser::Source::
|
|
142
|
+
source_buffer = Parser::Source::Buffer.new(location.source_buffer.name, source: source_line)
|
|
143
|
+
Parser::Source::Range.new(source_buffer, column, column + column_length)
|
|
143
144
|
end
|
|
144
145
|
|
|
145
146
|
# @api private
|
|
@@ -326,8 +326,7 @@ module RuboCop
|
|
|
326
326
|
argument_less_modifier_node = find_argument_less_modifier_node(node)
|
|
327
327
|
if argument_less_modifier_node
|
|
328
328
|
corrector.insert_after(argument_less_modifier_node, "\n\n#{source}")
|
|
329
|
-
elsif (ancestor = node.each_ancestor(:class, :module).first)
|
|
330
|
-
|
|
329
|
+
elsif (ancestor = node.each_ancestor(:class, :module, :sclass).first)
|
|
331
330
|
corrector.insert_before(ancestor.loc.end, "#{node.method_name}\n\n#{source}\n")
|
|
332
331
|
else
|
|
333
332
|
corrector.replace(node, "#{node.method_name}\n\n#{source}")
|
|
@@ -62,12 +62,12 @@ module RuboCop
|
|
|
62
62
|
#
|
|
63
63
|
# @example AllowedConstants: ['ClassMethods']
|
|
64
64
|
#
|
|
65
|
-
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
#
|
|
69
|
-
#
|
|
70
|
-
#
|
|
65
|
+
# # good
|
|
66
|
+
# module A
|
|
67
|
+
# module ClassMethods
|
|
68
|
+
# # ...
|
|
69
|
+
# end
|
|
70
|
+
# end
|
|
71
71
|
#
|
|
72
72
|
class Documentation < Base
|
|
73
73
|
include DocumentationComment
|
|
@@ -97,14 +97,14 @@ module RuboCop
|
|
|
97
97
|
#
|
|
98
98
|
# @example AllowedMethods: ['method_missing', 'respond_to_missing?']
|
|
99
99
|
#
|
|
100
|
-
#
|
|
101
|
-
#
|
|
102
|
-
#
|
|
103
|
-
#
|
|
104
|
-
#
|
|
105
|
-
#
|
|
106
|
-
#
|
|
107
|
-
#
|
|
100
|
+
# # good
|
|
101
|
+
# class Foo
|
|
102
|
+
# def method_missing(name, *args)
|
|
103
|
+
# end
|
|
104
|
+
#
|
|
105
|
+
# def respond_to_missing?(symbol, include_private)
|
|
106
|
+
# end
|
|
107
|
+
# end
|
|
108
108
|
#
|
|
109
109
|
class DocumentationMethod < Base
|
|
110
110
|
include DocumentationComment
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Enforces consistent style for empty class definitions.
|
|
7
|
+
#
|
|
8
|
+
# This cop can enforce either a two-line class definition or `Class.new`
|
|
9
|
+
# for classes with no body.
|
|
10
|
+
#
|
|
11
|
+
# The supported styles are:
|
|
12
|
+
#
|
|
13
|
+
# * class_definition (default) - prefer two-line class definition over `Class.new`
|
|
14
|
+
# * class_new - prefer `Class.new` over class definition
|
|
15
|
+
#
|
|
16
|
+
# @example EnforcedStyle: class_definition (default)
|
|
17
|
+
# # bad
|
|
18
|
+
# FooError = Class.new(StandardError)
|
|
19
|
+
#
|
|
20
|
+
# # okish
|
|
21
|
+
# class FooError < StandardError; end
|
|
22
|
+
#
|
|
23
|
+
# # good
|
|
24
|
+
# class FooError < StandardError
|
|
25
|
+
# end
|
|
26
|
+
#
|
|
27
|
+
# @example EnforcedStyle: class_new
|
|
28
|
+
# # bad
|
|
29
|
+
# class FooError < StandardError
|
|
30
|
+
# end
|
|
31
|
+
#
|
|
32
|
+
# # bad
|
|
33
|
+
# class FooError < StandardError; end
|
|
34
|
+
#
|
|
35
|
+
# # good
|
|
36
|
+
# FooError = Class.new(StandardError)
|
|
37
|
+
#
|
|
38
|
+
class EmptyClassDefinition < Base
|
|
39
|
+
include ConfigurableEnforcedStyle
|
|
40
|
+
include Alignment
|
|
41
|
+
include RangeHelp
|
|
42
|
+
extend AutoCorrector
|
|
43
|
+
|
|
44
|
+
MSG_CLASS_DEFINITION =
|
|
45
|
+
'Prefer a two-line class definition over `Class.new` for classes with no body.'
|
|
46
|
+
MSG_CLASS_NEW = 'Prefer `Class.new` over class definition for classes with no body.'
|
|
47
|
+
|
|
48
|
+
# @!method class_new_assignment?(node)
|
|
49
|
+
def_node_matcher :class_new_assignment?, <<~PATTERN
|
|
50
|
+
(casgn _ _ (send (const _ :Class) :new ...))
|
|
51
|
+
PATTERN
|
|
52
|
+
|
|
53
|
+
def on_casgn(node)
|
|
54
|
+
return unless style == :class_definition
|
|
55
|
+
return unless node.expression
|
|
56
|
+
|
|
57
|
+
class_new_node = find_class_new_node(node.expression)
|
|
58
|
+
return if chained_with_any_method?(node.expression, class_new_node)
|
|
59
|
+
return if variable_parent_class?(class_new_node)
|
|
60
|
+
|
|
61
|
+
add_offense(node, message: MSG_CLASS_DEFINITION) do |corrector|
|
|
62
|
+
autocorrect_class_new(corrector, node)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def on_class(node)
|
|
67
|
+
return unless style == :class_new
|
|
68
|
+
return unless empty_class?(node)
|
|
69
|
+
|
|
70
|
+
add_offense(node, message: MSG_CLASS_NEW) do |corrector|
|
|
71
|
+
autocorrect_class_definition(corrector, node)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
def autocorrect_class_new(corrector, node)
|
|
78
|
+
indent = ' ' * node.loc.column
|
|
79
|
+
class_name = node.name
|
|
80
|
+
class_new_node = find_class_new_node(node.expression)
|
|
81
|
+
parent_class = extract_parent_class(class_new_node)
|
|
82
|
+
|
|
83
|
+
replacement = if parent_class
|
|
84
|
+
"class #{class_name} < #{parent_class}\n#{indent}end"
|
|
85
|
+
else
|
|
86
|
+
"class #{class_name}\n#{indent}end"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
corrector.replace(node, replacement)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def autocorrect_class_definition(corrector, node)
|
|
93
|
+
source_line = processed_source.buffer.source_line(node.loc.line)
|
|
94
|
+
indent = source_line[/\A */]
|
|
95
|
+
class_name = node.identifier.source
|
|
96
|
+
parent_class = node.parent_class&.source
|
|
97
|
+
range = range_by_whole_lines(node.source_range, include_final_newline: true)
|
|
98
|
+
|
|
99
|
+
replacement = if parent_class
|
|
100
|
+
"#{indent}#{class_name} = Class.new(#{parent_class})\n"
|
|
101
|
+
else
|
|
102
|
+
"#{indent}#{class_name} = Class.new\n"
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
corrector.replace(range, replacement)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def extract_parent_class(class_new_node)
|
|
109
|
+
first_arg = class_new_node.first_argument
|
|
110
|
+
first_arg&.source
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def variable_parent_class?(class_new_node)
|
|
114
|
+
first_arg = class_new_node.first_argument
|
|
115
|
+
return false unless first_arg
|
|
116
|
+
|
|
117
|
+
!first_arg.const_type?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def find_class_new_node(node)
|
|
121
|
+
return nil unless node.send_type?
|
|
122
|
+
return nil unless node.receiver&.const_type?
|
|
123
|
+
|
|
124
|
+
return node if node.receiver.const_name.to_sym == :Class && node.method?(:new)
|
|
125
|
+
|
|
126
|
+
nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def chained_with_any_method?(expression_node, class_new_node)
|
|
130
|
+
return true unless expression_node == class_new_node
|
|
131
|
+
|
|
132
|
+
false
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def empty_class?(node)
|
|
136
|
+
body = node.body
|
|
137
|
+
return true unless body
|
|
138
|
+
|
|
139
|
+
body.begin_type? && body.children.empty?
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
@@ -227,12 +227,15 @@ module RuboCop
|
|
|
227
227
|
remove_whole_lines(corrector, node.loc.end)
|
|
228
228
|
return unless node.else?
|
|
229
229
|
|
|
230
|
-
|
|
230
|
+
if leave_branch
|
|
231
|
+
remove_whole_lines(corrector, leave_branch.source_range)
|
|
232
|
+
corrector.insert_after(
|
|
233
|
+
heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
|
|
234
|
+
)
|
|
235
|
+
end
|
|
236
|
+
|
|
231
237
|
remove_whole_lines(corrector, node.loc.else)
|
|
232
238
|
remove_whole_lines(corrector, range_of_branch_to_remove(node, guard))
|
|
233
|
-
corrector.insert_after(
|
|
234
|
-
heredoc_branch.last_argument.loc.heredoc_end, "\n#{leave_branch.source}"
|
|
235
|
-
)
|
|
236
239
|
end
|
|
237
240
|
|
|
238
241
|
def range_of_branch_to_remove(node, guard)
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Style
|
|
6
|
+
# Enforces the use of either `Hash#[]` or `Hash#fetch` for hash lookup.
|
|
7
|
+
#
|
|
8
|
+
# This cop can be configured to prefer either bracket-style (`[]`)
|
|
9
|
+
# or fetch-style lookup. It is disabled by default.
|
|
10
|
+
#
|
|
11
|
+
# When enforcing `fetch` style, only single-argument bracket access is flagged.
|
|
12
|
+
# When enforcing `brackets` style, only `fetch` calls with a single key
|
|
13
|
+
# argument are flagged (not those with default values or blocks).
|
|
14
|
+
#
|
|
15
|
+
# @safety
|
|
16
|
+
# This cop is unsafe because `Hash#[]` and `Hash#fetch` have different
|
|
17
|
+
# semantics. `Hash#[]` returns `nil` for missing keys, while `Hash#fetch`
|
|
18
|
+
# raises a `KeyError`. Replacing one with the other can change program
|
|
19
|
+
# behavior in cases where the key is missing.
|
|
20
|
+
#
|
|
21
|
+
# Additionally, it cannot be guaranteed that the receiver is a `Hash`
|
|
22
|
+
# or responds to the replacement method.
|
|
23
|
+
#
|
|
24
|
+
# @example EnforcedStyle: brackets (default)
|
|
25
|
+
# # bad
|
|
26
|
+
# hash.fetch(key)
|
|
27
|
+
#
|
|
28
|
+
# # good
|
|
29
|
+
# hash[key]
|
|
30
|
+
#
|
|
31
|
+
# # good - fetch with default value is allowed
|
|
32
|
+
# hash.fetch(key, default)
|
|
33
|
+
#
|
|
34
|
+
# # good - fetch with block is allowed
|
|
35
|
+
# hash.fetch(key) { default }
|
|
36
|
+
#
|
|
37
|
+
# @example EnforcedStyle: fetch
|
|
38
|
+
# # bad
|
|
39
|
+
# hash[key]
|
|
40
|
+
#
|
|
41
|
+
# # good
|
|
42
|
+
# hash.fetch(key)
|
|
43
|
+
#
|
|
44
|
+
class HashLookupMethod < Base
|
|
45
|
+
include ConfigurableEnforcedStyle
|
|
46
|
+
extend AutoCorrector
|
|
47
|
+
|
|
48
|
+
BRACKET_MSG = 'Use `Hash#[]` instead of `Hash#fetch`.'
|
|
49
|
+
FETCH_MSG = 'Use `Hash#fetch` instead of `Hash#[]`.'
|
|
50
|
+
|
|
51
|
+
RESTRICT_ON_SEND = %i[[] fetch].freeze
|
|
52
|
+
|
|
53
|
+
def on_send(node)
|
|
54
|
+
if offense_for_brackets?(node)
|
|
55
|
+
add_offense(node.loc.selector, message: BRACKET_MSG) do |corrector|
|
|
56
|
+
correct_fetch_to_brackets(corrector, node)
|
|
57
|
+
end
|
|
58
|
+
elsif offense_for_fetch?(node)
|
|
59
|
+
add_offense(node, message: FETCH_MSG) do |corrector|
|
|
60
|
+
correct_brackets_to_fetch(corrector, node)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
alias on_csend on_send
|
|
65
|
+
|
|
66
|
+
private
|
|
67
|
+
|
|
68
|
+
def offense_for_brackets?(node)
|
|
69
|
+
style == :brackets && node.receiver && node.method?(:fetch) && node.arguments.one? &&
|
|
70
|
+
!node.block_literal?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def offense_for_fetch?(node)
|
|
74
|
+
style == :fetch && node.method?(:[]) && node.arguments.one?
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def correct_fetch_to_brackets(corrector, node)
|
|
78
|
+
receiver = node.receiver.source
|
|
79
|
+
key = node.first_argument.source
|
|
80
|
+
replacement = "#{receiver}[#{key}]"
|
|
81
|
+
replacement = "(#{replacement})" if node.csend_type?
|
|
82
|
+
corrector.replace(node, replacement)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def correct_brackets_to_fetch(corrector, node)
|
|
86
|
+
receiver = node.receiver.source
|
|
87
|
+
key = node.first_argument.source
|
|
88
|
+
operator = node.csend_type? ? '&.' : '.'
|
|
89
|
+
corrector.replace(node, "#{receiver}#{operator}fetch(#{key})")
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
@@ -8,20 +8,20 @@ module RuboCop
|
|
|
8
8
|
#
|
|
9
9
|
# @example
|
|
10
10
|
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
11
|
+
# # bad
|
|
12
|
+
# tired? ? 'stop' : 'go faster' if running?
|
|
13
13
|
#
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
#
|
|
14
|
+
# # bad
|
|
15
|
+
# if tired?
|
|
16
|
+
# "please stop"
|
|
17
|
+
# else
|
|
18
|
+
# "keep going"
|
|
19
|
+
# end if running?
|
|
20
20
|
#
|
|
21
|
-
#
|
|
22
|
-
#
|
|
23
|
-
#
|
|
24
|
-
#
|
|
21
|
+
# # good
|
|
22
|
+
# if running?
|
|
23
|
+
# tired? ? 'stop' : 'go faster'
|
|
24
|
+
# end
|
|
25
25
|
class IfUnlessModifierOfIfUnless < Base
|
|
26
26
|
include StatementModifier
|
|
27
27
|
extend AutoCorrector
|
|
@@ -6,18 +6,18 @@ module RuboCop
|
|
|
6
6
|
# Checks for use of the lambda.(args) syntax.
|
|
7
7
|
#
|
|
8
8
|
# @example EnforcedStyle: call (default)
|
|
9
|
-
#
|
|
10
|
-
#
|
|
9
|
+
# # bad
|
|
10
|
+
# lambda.(x, y)
|
|
11
11
|
#
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# # good
|
|
13
|
+
# lambda.call(x, y)
|
|
14
14
|
#
|
|
15
15
|
# @example EnforcedStyle: braces
|
|
16
|
-
#
|
|
17
|
-
#
|
|
16
|
+
# # bad
|
|
17
|
+
# lambda.call(x, y)
|
|
18
18
|
#
|
|
19
|
-
#
|
|
20
|
-
#
|
|
19
|
+
# # good
|
|
20
|
+
# lambda.(x, y)
|
|
21
21
|
class LambdaCall < Base
|
|
22
22
|
include ConfigurableEnforcedStyle
|
|
23
23
|
extend AutoCorrector
|
|
@@ -26,33 +26,65 @@ module RuboCop
|
|
|
26
26
|
#
|
|
27
27
|
# Array.method_defined?(:find, false)
|
|
28
28
|
#
|
|
29
|
+
# # bad
|
|
30
|
+
# Array.class_variables.include?(:foo)
|
|
31
|
+
# Array.constants.include?(:foo)
|
|
32
|
+
# Array.private_instance_methods.include?(:foo)
|
|
33
|
+
# Array.protected_instance_methods.include?(:foo)
|
|
34
|
+
# Array.public_instance_methods.include?(:foo)
|
|
35
|
+
# Array.included_modules.include?(:foo)
|
|
36
|
+
#
|
|
37
|
+
# # good
|
|
38
|
+
# Array.class_variable_defined?(:foo)
|
|
39
|
+
# Array.const_defined?(:foo)
|
|
40
|
+
# Array.private_method_defined?(:foo)
|
|
41
|
+
# Array.protected_method_defined?(:foo)
|
|
42
|
+
# Array.public_method_defined?(:foo)
|
|
43
|
+
# Array.include?(:foo)
|
|
44
|
+
#
|
|
45
|
+
# @example AllowedMethods: [included_modules]
|
|
46
|
+
#
|
|
47
|
+
# # good
|
|
48
|
+
# Array.included_modules.include?(:foo)
|
|
49
|
+
#
|
|
29
50
|
class ModuleMemberExistenceCheck < Base
|
|
51
|
+
include AllowedMethods
|
|
30
52
|
extend AutoCorrector
|
|
31
53
|
|
|
32
54
|
MSG = 'Use `%<replacement>s` instead.'
|
|
33
55
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# @!method instance_methods_inclusion?(node)
|
|
37
|
-
def_node_matcher :instance_methods_inclusion?, <<~PATTERN
|
|
56
|
+
# @!method module_member_inclusion?(node)
|
|
57
|
+
def_node_matcher :module_member_inclusion?, <<~PATTERN
|
|
38
58
|
(call
|
|
39
|
-
(call _
|
|
59
|
+
{(call _ %METHODS_WITH_INHERIT_PARAM _?) (call _ %METHODS_WITHOUT_INHERIT_PARAM)}
|
|
40
60
|
{:include? :member?}
|
|
41
61
|
_)
|
|
42
62
|
PATTERN
|
|
43
63
|
|
|
44
|
-
|
|
64
|
+
METHOD_REPLACEMENTS = {
|
|
65
|
+
class_variables: :class_variable_defined?,
|
|
66
|
+
constants: :const_defined?,
|
|
67
|
+
included_modules: :include?,
|
|
68
|
+
instance_methods: :method_defined?,
|
|
69
|
+
private_instance_methods: :private_method_defined?,
|
|
70
|
+
protected_instance_methods: :protected_method_defined?,
|
|
71
|
+
public_instance_methods: :public_method_defined?
|
|
72
|
+
}.freeze
|
|
73
|
+
|
|
74
|
+
METHODS_WITHOUT_INHERIT_PARAM = Set[:class_variables, :included_modules].freeze
|
|
75
|
+
METHODS_WITH_INHERIT_PARAM =
|
|
76
|
+
(METHOD_REPLACEMENTS.keys.to_set - METHODS_WITHOUT_INHERIT_PARAM).freeze
|
|
77
|
+
|
|
78
|
+
RESTRICT_ON_SEND = METHOD_REPLACEMENTS.keys.freeze
|
|
79
|
+
|
|
80
|
+
def on_send(node)
|
|
45
81
|
return unless (parent = node.parent)
|
|
46
|
-
return unless
|
|
82
|
+
return unless module_member_inclusion?(parent)
|
|
47
83
|
return unless simple_method_argument?(node) && simple_method_argument?(parent)
|
|
84
|
+
return if allowed_method?(node.method_name)
|
|
48
85
|
|
|
49
86
|
offense_range = node.location.selector.join(parent.source_range.end)
|
|
50
|
-
replacement =
|
|
51
|
-
if node.first_argument.nil? || node.first_argument.true_type?
|
|
52
|
-
"method_defined?(#{parent.first_argument.source})"
|
|
53
|
-
else
|
|
54
|
-
"method_defined?(#{parent.first_argument.source}, #{node.first_argument.source})"
|
|
55
|
-
end
|
|
87
|
+
replacement = replacement_for(node, parent)
|
|
56
88
|
|
|
57
89
|
add_offense(offense_range, message: format(MSG, replacement: replacement)) do |corrector|
|
|
58
90
|
corrector.replace(offense_range, replacement)
|
|
@@ -62,6 +94,17 @@ module RuboCop
|
|
|
62
94
|
|
|
63
95
|
private
|
|
64
96
|
|
|
97
|
+
def replacement_for(node, parent)
|
|
98
|
+
replacement_method = METHOD_REPLACEMENTS.fetch(node.method_name)
|
|
99
|
+
without_inherit_param = METHODS_WITHOUT_INHERIT_PARAM.include?(node.method_name)
|
|
100
|
+
|
|
101
|
+
if without_inherit_param || node.first_argument.nil? || node.first_argument.true_type?
|
|
102
|
+
"#{replacement_method}(#{parent.first_argument.source})"
|
|
103
|
+
else
|
|
104
|
+
"#{replacement_method}(#{parent.first_argument.source}, #{node.first_argument.source})"
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
65
108
|
def simple_method_argument?(node)
|
|
66
109
|
return false if node.splat_argument? || node.block_argument?
|
|
67
110
|
return false if node.first_argument&.hash_type?
|