rubocop 1.62.1 → 1.63.5
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 +22 -3
- data/lib/rubocop/cached_data.rb +11 -3
- data/lib/rubocop/cli.rb +4 -0
- data/lib/rubocop/config.rb +33 -10
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/cop/base.rb +40 -1
- data/lib/rubocop/cop/internal_affairs/example_description.rb +2 -1
- data/lib/rubocop/cop/layout/comment_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -2
- data/lib/rubocop/cop/lint/debugger.rb +27 -2
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
- data/lib/rubocop/cop/lint/mixed_case_range.rb +9 -4
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +4 -2
- data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +2 -2
- data/lib/rubocop/cop/mixin/code_length.rb +12 -1
- data/lib/rubocop/cop/mixin/method_complexity.rb +15 -6
- data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -1
- data/lib/rubocop/cop/naming/block_forwarding.rb +31 -12
- data/lib/rubocop/cop/naming/file_name.rb +2 -2
- data/lib/rubocop/cop/naming/inclusive_language.rb +1 -2
- data/lib/rubocop/cop/security/compound_hash.rb +2 -2
- data/lib/rubocop/cop/style/alias.rb +1 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -1
- data/lib/rubocop/cop/style/collection_compact.rb +3 -3
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -1
- data/lib/rubocop/cop/style/copyright.rb +16 -11
- data/lib/rubocop/cop/style/eval_with_location.rb +3 -1
- data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
- data/lib/rubocop/cop/style/format_string.rb +9 -9
- data/lib/rubocop/cop/style/map_into_array.rb +175 -0
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
- data/lib/rubocop/cop/style/map_to_set.rb +1 -1
- data/lib/rubocop/cop/style/numeric_predicate.rb +10 -2
- data/lib/rubocop/cop/style/redundant_argument.rb +24 -1
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
- data/lib/rubocop/cop/style/redundant_each.rb +1 -1
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +11 -15
- data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
- data/lib/rubocop/cop/style/require_order.rb +1 -1
- data/lib/rubocop/cop/style/send.rb +4 -4
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -2
- data/lib/rubocop/cop/style/top_level_method_definition.rb +1 -1
- data/lib/rubocop/cop/team.rb +3 -0
- data/lib/rubocop/formatter/clang_style_formatter.rb +3 -7
- data/lib/rubocop/formatter/tap_formatter.rb +3 -7
- data/lib/rubocop/lockfile.rb +56 -7
- data/lib/rubocop/lsp/routes.rb +3 -4
- data/lib/rubocop/lsp/server.rb +2 -0
- data/lib/rubocop/options.rb +3 -3
- data/lib/rubocop/rake_task.rb +1 -1
- data/lib/rubocop/rspec/expect_offense.rb +8 -0
- data/lib/rubocop/rspec/shared_contexts.rb +13 -1
- data/lib/rubocop/runner.rb +5 -1
- data/lib/rubocop/version.rb +5 -5
- data/lib/rubocop.rb +1 -0
- metadata +5 -4
@@ -51,29 +51,25 @@ module RuboCop
|
|
51
51
|
[Lint::AmbiguousOperator, Style::ArgumentsForwarding]
|
52
52
|
end
|
53
53
|
|
54
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
55
54
|
def on_def(node)
|
56
55
|
return if node.arguments.empty?
|
57
56
|
|
58
57
|
last_argument = node.last_argument
|
59
58
|
return if expected_block_forwarding_style?(node, last_argument)
|
60
59
|
|
61
|
-
|
62
|
-
|
63
|
-
next
|
64
|
-
last_argument.source != block_pass_node.source
|
60
|
+
forwarded_args = node.each_descendant(:block_pass).with_object([]) do |block_pass, result|
|
61
|
+
return nil if invalidates_syntax?(block_pass)
|
62
|
+
next unless block_argument_name_matched?(block_pass, last_argument)
|
65
63
|
|
66
|
-
|
67
|
-
|
68
|
-
next
|
69
|
-
end
|
64
|
+
result << block_pass
|
65
|
+
end
|
70
66
|
|
71
|
-
|
67
|
+
forwarded_args.each do |forwarded_arg|
|
68
|
+
register_offense(forwarded_arg, node)
|
72
69
|
end
|
73
70
|
|
74
|
-
register_offense(last_argument, node)
|
71
|
+
register_offense(last_argument, node)
|
75
72
|
end
|
76
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
77
73
|
alias on_defs on_def
|
78
74
|
|
79
75
|
private
|
@@ -88,6 +84,29 @@ module RuboCop
|
|
88
84
|
end
|
89
85
|
end
|
90
86
|
|
87
|
+
def block_argument_name_matched?(block_pass_node, last_argument)
|
88
|
+
return false if block_pass_node.children.first&.sym_type?
|
89
|
+
|
90
|
+
last_argument.source == block_pass_node.source
|
91
|
+
end
|
92
|
+
|
93
|
+
# Prevents the following syntax error:
|
94
|
+
#
|
95
|
+
# # foo.rb
|
96
|
+
# def foo(&)
|
97
|
+
# block_method do
|
98
|
+
# bar(&)
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
#
|
102
|
+
# $ ruby -vc foo.rb
|
103
|
+
# ruby 3.3.0 (2023-12-25 revision 5124f9ac75) [x86_64-darwin22]
|
104
|
+
# foo.rb: foo.rb:4: anonymous block parameter is also used within block (SyntaxError)
|
105
|
+
#
|
106
|
+
def invalidates_syntax?(block_pass_node)
|
107
|
+
block_pass_node.each_ancestor(:block, :numblock).any?
|
108
|
+
end
|
109
|
+
|
91
110
|
def use_kwarg_in_method_definition?(node)
|
92
111
|
node.arguments.each_descendant(:kwarg, :kwoptarg).any?
|
93
112
|
end
|
@@ -57,7 +57,7 @@ module RuboCop
|
|
57
57
|
file_path = processed_source.file_path
|
58
58
|
return if config.file_to_exclude?(file_path) || config.allowed_camel_case_file?(file_path)
|
59
59
|
|
60
|
-
for_bad_filename(file_path)
|
60
|
+
for_bad_filename(file_path)
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|
@@ -71,7 +71,7 @@ module RuboCop
|
|
71
71
|
msg = other_message(basename) unless bad_filename_allowed?
|
72
72
|
end
|
73
73
|
|
74
|
-
|
74
|
+
add_global_offense(msg) if msg
|
75
75
|
end
|
76
76
|
|
77
77
|
def perform_class_and_module_naming_checks(file_path, basename)
|
@@ -207,8 +207,7 @@ module RuboCop
|
|
207
207
|
message = create_multiple_word_message_for_file(words)
|
208
208
|
end
|
209
209
|
|
210
|
-
|
211
|
-
add_offense(range, message: message)
|
210
|
+
add_global_offense(message)
|
212
211
|
end
|
213
212
|
|
214
213
|
def create_single_word_message_for_file(word)
|
@@ -30,8 +30,8 @@ module RuboCop
|
|
30
30
|
class CompoundHash < Base
|
31
31
|
COMBINATOR_IN_HASH_MSG = 'Use `[...].hash` instead of combining hash values manually.'
|
32
32
|
MONUPLE_HASH_MSG =
|
33
|
-
'Delegate hash directly without wrapping in an array when only using a single value'
|
34
|
-
REDUNDANT_HASH_MSG = 'Calling .hash on elements of a hashed array is redundant'
|
33
|
+
'Delegate hash directly without wrapping in an array when only using a single value.'
|
34
|
+
REDUNDANT_HASH_MSG = 'Calling .hash on elements of a hashed array is redundant.'
|
35
35
|
|
36
36
|
# @!method hash_method_definition?(node)
|
37
37
|
def_node_matcher :hash_method_definition?, <<~PATTERN
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
41
41
|
def on_send(node)
|
42
42
|
return unless node.command?(:alias_method)
|
43
43
|
return unless style == :prefer_alias && alias_keyword_possible?(node)
|
44
|
+
return unless node.arguments.count == 2
|
44
45
|
|
45
46
|
msg = format(MSG_ALIAS_METHOD, current: lexical_scope_type(node))
|
46
47
|
add_offense(node.loc.selector, message: msg) do |corrector|
|
@@ -312,7 +312,8 @@ module RuboCop
|
|
312
312
|
end
|
313
313
|
|
314
314
|
def register_forward_block_arg_offense(add_parens, def_arguments_or_send, block_arg)
|
315
|
-
return if target_ruby_version <= 3.0 ||
|
315
|
+
return if target_ruby_version <= 3.0 ||
|
316
|
+
block_arg.nil? || block_arg.source == '&' || explicit_block_name?
|
316
317
|
|
317
318
|
add_offense(block_arg, message: BLOCK_MSG) do |corrector|
|
318
319
|
add_parens_if_missing(def_arguments_or_send, corrector) if add_parens
|
@@ -19,9 +19,7 @@ module RuboCop
|
|
19
19
|
# @example
|
20
20
|
# # bad
|
21
21
|
# array.reject(&:nil?)
|
22
|
-
# array.delete_if(&:nil?)
|
23
22
|
# array.reject { |e| e.nil? }
|
24
|
-
# array.delete_if { |e| e.nil? }
|
25
23
|
# array.select { |e| !e.nil? }
|
26
24
|
# array.grep_v(nil)
|
27
25
|
# array.grep_v(NilClass)
|
@@ -31,7 +29,9 @@ module RuboCop
|
|
31
29
|
#
|
32
30
|
# # bad
|
33
31
|
# hash.reject!(&:nil?)
|
32
|
+
# array.delete_if(&:nil?)
|
34
33
|
# hash.reject! { |k, v| v.nil? }
|
34
|
+
# array.delete_if { |e| e.nil? }
|
35
35
|
# hash.select! { |k, v| !v.nil? }
|
36
36
|
#
|
37
37
|
# # good
|
@@ -127,7 +127,7 @@ module RuboCop
|
|
127
127
|
end
|
128
128
|
|
129
129
|
def good_method_name(node)
|
130
|
-
if node.bang_method?
|
130
|
+
if node.bang_method? || node.method?(:delete_if)
|
131
131
|
'compact!'
|
132
132
|
else
|
133
133
|
'compact'
|
@@ -214,7 +214,7 @@ module RuboCop
|
|
214
214
|
extend AutoCorrector
|
215
215
|
|
216
216
|
MSG = 'Use the return of the conditional for variable assignment and comparison.'
|
217
|
-
ASSIGN_TO_CONDITION_MSG = 'Assign variables inside of conditionals'
|
217
|
+
ASSIGN_TO_CONDITION_MSG = 'Assign variables inside of conditionals.'
|
218
218
|
VARIABLE_ASSIGNMENT_TYPES = %i[casgn cvasgn gvasgn ivasgn lvasgn].freeze
|
219
219
|
ASSIGNMENT_TYPES = VARIABLE_ASSIGNMENT_TYPES + %i[and_asgn or_asgn op_asgn masgn].freeze
|
220
220
|
LINE_LENGTH = 'Layout/LineLength'
|
@@ -28,18 +28,27 @@ module RuboCop
|
|
28
28
|
def on_new_investigation
|
29
29
|
return if notice.empty? || notice_found?(processed_source)
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
31
|
+
verify_autocorrect_notice!
|
32
|
+
message = format(MSG, notice: notice)
|
33
|
+
if processed_source.blank?
|
34
|
+
add_global_offense(message)
|
35
|
+
else
|
36
|
+
offense_range = source_range(processed_source.buffer, 1, 0)
|
37
|
+
add_offense(offense_range, message: message) do |corrector|
|
38
|
+
autocorrect(corrector)
|
39
|
+
end
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
43
|
private
|
42
44
|
|
45
|
+
def autocorrect(corrector)
|
46
|
+
token = insert_notice_before(processed_source)
|
47
|
+
range = token.nil? ? range_between(0, 0) : token.pos
|
48
|
+
|
49
|
+
corrector.insert_before(range, "#{autocorrect_notice}\n")
|
50
|
+
end
|
51
|
+
|
43
52
|
def notice
|
44
53
|
cop_config['Notice']
|
45
54
|
end
|
@@ -48,10 +57,6 @@ module RuboCop
|
|
48
57
|
cop_config['AutocorrectNotice']
|
49
58
|
end
|
50
59
|
|
51
|
-
def offense_range
|
52
|
-
source_range(processed_source.buffer, 1, 0)
|
53
|
-
end
|
54
|
-
|
55
60
|
def verify_autocorrect_notice!
|
56
61
|
raise Warning, AUTOCORRECT_EMPTY_WARNING if autocorrect_notice.empty?
|
57
62
|
|
@@ -141,7 +141,7 @@ module RuboCop
|
|
141
141
|
end
|
142
142
|
|
143
143
|
def check_file(node, file_node)
|
144
|
-
return
|
144
|
+
return if special_file_keyword?(file_node)
|
145
145
|
|
146
146
|
message = format(MSG_INCORRECT_FILE,
|
147
147
|
method_name: node.method_name,
|
@@ -155,6 +155,8 @@ module RuboCop
|
|
155
155
|
|
156
156
|
def check_line(node, code)
|
157
157
|
line_node = node.last_argument
|
158
|
+
return if line_node.variable? || (line_node.send_type? && !line_node.method?(:+))
|
159
|
+
|
158
160
|
line_diff = line_difference(line_node, code)
|
159
161
|
if line_diff.zero?
|
160
162
|
add_offense_for_same_line(node, line_node)
|
@@ -38,12 +38,13 @@ module RuboCop
|
|
38
38
|
PATTERN
|
39
39
|
|
40
40
|
def on_send(node)
|
41
|
+
return unless (receiver = node.receiver)
|
41
42
|
return unless (regexp = exact_regexp_match(node))
|
42
43
|
|
43
44
|
parsed_regexp = Regexp::Parser.parse(regexp)
|
44
45
|
return unless exact_match_pattern?(parsed_regexp)
|
45
46
|
|
46
|
-
prefer = "#{
|
47
|
+
prefer = "#{receiver.source} #{new_method(node)} '#{parsed_regexp[1].text}'"
|
47
48
|
|
48
49
|
add_offense(node, message: format(MSG, prefer: prefer)) do |corrector|
|
49
50
|
corrector.replace(node, prefer)
|
@@ -25,27 +25,27 @@ module RuboCop
|
|
25
25
|
#
|
26
26
|
# @example EnforcedStyle: format (default)
|
27
27
|
# # bad
|
28
|
-
# puts sprintf('%10s', '
|
29
|
-
# puts '%10s' % '
|
28
|
+
# puts sprintf('%10s', 'foo')
|
29
|
+
# puts '%10s' % 'foo'
|
30
30
|
#
|
31
31
|
# # good
|
32
|
-
# puts format('%10s', '
|
32
|
+
# puts format('%10s', 'foo')
|
33
33
|
#
|
34
34
|
# @example EnforcedStyle: sprintf
|
35
35
|
# # bad
|
36
|
-
# puts format('%10s', '
|
37
|
-
# puts '%10s' % '
|
36
|
+
# puts format('%10s', 'foo')
|
37
|
+
# puts '%10s' % 'foo'
|
38
38
|
#
|
39
39
|
# # good
|
40
|
-
# puts sprintf('%10s', '
|
40
|
+
# puts sprintf('%10s', 'foo')
|
41
41
|
#
|
42
42
|
# @example EnforcedStyle: percent
|
43
43
|
# # bad
|
44
|
-
# puts format('%10s', '
|
45
|
-
# puts sprintf('%10s', '
|
44
|
+
# puts format('%10s', 'foo')
|
45
|
+
# puts sprintf('%10s', 'foo')
|
46
46
|
#
|
47
47
|
# # good
|
48
|
-
# puts '%10s' % '
|
48
|
+
# puts '%10s' % 'foo'
|
49
49
|
#
|
50
50
|
class FormatString < Base
|
51
51
|
include ConfigurableEnforcedStyle
|
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Style
|
6
|
+
# Checks for usages of `each` with `<<`, `push`, or `append` which
|
7
|
+
# can be replaced by `map`.
|
8
|
+
#
|
9
|
+
# If `PreferredMethods` is configured for `map` in `Style/CollectionMethods`,
|
10
|
+
# this cop uses the specified method for replacement.
|
11
|
+
#
|
12
|
+
# NOTE: The return value of `Enumerable#each` is `self`, whereas the
|
13
|
+
# return value of `Enumerable#map` is an `Array`. They are not autocorrected
|
14
|
+
# when a return value could be used because these types differ.
|
15
|
+
#
|
16
|
+
# NOTE: It only detects when the mapping destination is a local variable
|
17
|
+
# initialized as an empty array and referred to only by the pushing operation.
|
18
|
+
# This is because, if not, it's challenging to statically guarantee that the
|
19
|
+
# mapping destination variable remains an empty array:
|
20
|
+
#
|
21
|
+
# [source,ruby]
|
22
|
+
# ----
|
23
|
+
# ret = []
|
24
|
+
# src.each { |e| ret << e * 2 } # `<<` method may mutate `ret`
|
25
|
+
#
|
26
|
+
# dest = []
|
27
|
+
# src.each { |e| dest << transform(e, dest) } # `transform` method may mutate `dest`
|
28
|
+
# ----
|
29
|
+
#
|
30
|
+
# @safety
|
31
|
+
# This cop is unsafe because not all objects that have an `each`
|
32
|
+
# method also have a `map` method (e.g. `ENV`). Additionally, for calls
|
33
|
+
# with a block, not all objects that have a `map` method return an array
|
34
|
+
# (e.g. `Enumerator::Lazy`).
|
35
|
+
#
|
36
|
+
# @example
|
37
|
+
# # bad
|
38
|
+
# dest = []
|
39
|
+
# src.each { |e| dest << e * 2 }
|
40
|
+
# dest
|
41
|
+
#
|
42
|
+
# # good
|
43
|
+
# dest = src.map { |e| e * 2 }
|
44
|
+
#
|
45
|
+
# # good - contains another operation
|
46
|
+
# dest = []
|
47
|
+
# src.each { |e| dest << e * 2; puts e }
|
48
|
+
# dest
|
49
|
+
#
|
50
|
+
class MapIntoArray < Base
|
51
|
+
include RangeHelp
|
52
|
+
extend AutoCorrector
|
53
|
+
|
54
|
+
MSG = 'Use `%<new_method_name>s` instead of `each` to map elements into an array.'
|
55
|
+
|
56
|
+
# @!method each_block_with_push?(node)
|
57
|
+
def_node_matcher :each_block_with_push?, <<-PATTERN
|
58
|
+
[
|
59
|
+
^({begin kwbegin} ...)
|
60
|
+
({block numblock} (send _ :each) _
|
61
|
+
(send (lvar _) {:<< :push :append} _))
|
62
|
+
]
|
63
|
+
PATTERN
|
64
|
+
|
65
|
+
# @!method empty_array_asgn?(node)
|
66
|
+
def_node_matcher :empty_array_asgn?, '(lvasgn _ (array))'
|
67
|
+
|
68
|
+
# @!method lvar_ref?(node, name)
|
69
|
+
def_node_matcher :lvar_ref?, '(lvar %1)'
|
70
|
+
|
71
|
+
def self.joining_forces
|
72
|
+
VariableForce
|
73
|
+
end
|
74
|
+
|
75
|
+
def after_leaving_scope(scope, _variable_table)
|
76
|
+
(@scopes ||= []) << scope
|
77
|
+
end
|
78
|
+
|
79
|
+
def on_block(node)
|
80
|
+
return unless each_block_with_push?(node)
|
81
|
+
|
82
|
+
dest_var = find_dest_var(node)
|
83
|
+
return unless (asgn = find_closest_assignment(node, dest_var))
|
84
|
+
return unless empty_array_asgn?(asgn)
|
85
|
+
return unless dest_used_only_for_mapping?(node, dest_var, asgn)
|
86
|
+
|
87
|
+
register_offense(node, dest_var, asgn)
|
88
|
+
end
|
89
|
+
|
90
|
+
alias on_numblock on_block
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def find_dest_var(block)
|
95
|
+
node = block.body.receiver
|
96
|
+
name = node.children.first
|
97
|
+
|
98
|
+
candidates = @scopes.lazy.filter_map { |s| s.variables[name] }
|
99
|
+
candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_closest_assignment(block, dest_var)
|
103
|
+
dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
|
104
|
+
node.source_range.end_pos < block.source_range.begin_pos
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def dest_used_only_for_mapping?(block, dest_var, asgn)
|
109
|
+
range = asgn.source_range.join(block.source_range)
|
110
|
+
|
111
|
+
asgn.parent.equal?(block.parent) &&
|
112
|
+
dest_var.references.one? { |r| range.contains?(r.node.source_range) } &&
|
113
|
+
dest_var.assignments.one? { |a| range.contains?(a.node.source_range) }
|
114
|
+
end
|
115
|
+
|
116
|
+
def register_offense(block, dest_var, asgn)
|
117
|
+
add_offense(block, message: format(MSG, new_method_name: new_method_name)) do |corrector|
|
118
|
+
next if return_value_used?(block)
|
119
|
+
|
120
|
+
corrector.replace(block.send_node.selector, new_method_name)
|
121
|
+
remove_assignment(corrector, asgn)
|
122
|
+
correct_push_node(corrector, block.body)
|
123
|
+
correct_return_value_handling(corrector, block, dest_var)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
def new_method_name
|
128
|
+
default = 'map'
|
129
|
+
alternative = config.for_cop('Style/CollectionMethods').dig('PreferredMethods', default)
|
130
|
+
alternative || default
|
131
|
+
end
|
132
|
+
|
133
|
+
def return_value_used?(node)
|
134
|
+
parent = node.parent
|
135
|
+
|
136
|
+
case parent&.type
|
137
|
+
when nil
|
138
|
+
false
|
139
|
+
when :begin, :kwbegin
|
140
|
+
!node.right_sibling && return_value_used?(parent)
|
141
|
+
when :block, :numblock
|
142
|
+
!parent.void_context?
|
143
|
+
else
|
144
|
+
true
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def remove_assignment(corrector, asgn)
|
149
|
+
range = range_with_surrounding_space(asgn.source_range, side: :right)
|
150
|
+
range = range_with_surrounding_space(range, side: :right, newlines: false)
|
151
|
+
|
152
|
+
corrector.remove(range)
|
153
|
+
end
|
154
|
+
|
155
|
+
def correct_push_node(corrector, push_node)
|
156
|
+
range = push_node.source_range
|
157
|
+
arg_range = push_node.first_argument.source_range
|
158
|
+
|
159
|
+
corrector.remove(range_between(range.begin_pos, arg_range.begin_pos))
|
160
|
+
corrector.remove(range_between(arg_range.end_pos, range.end_pos))
|
161
|
+
end
|
162
|
+
|
163
|
+
def correct_return_value_handling(corrector, block, dest_var)
|
164
|
+
next_node = block.right_sibling
|
165
|
+
|
166
|
+
if lvar_ref?(next_node, dest_var.name)
|
167
|
+
corrector.remove(range_with_surrounding_space(next_node.source_range, side: :left))
|
168
|
+
end
|
169
|
+
|
170
|
+
corrector.insert_before(block, "#{dest_var.name} = ")
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
@@ -55,7 +55,7 @@ module RuboCop
|
|
55
55
|
message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
|
56
56
|
add_offense(map_node.loc.selector, message: message) do |corrector|
|
57
57
|
# If the `to_h` call already has a block, do not autocorrect.
|
58
|
-
next if to_h_node.
|
58
|
+
next if to_h_node.block_literal?
|
59
59
|
|
60
60
|
autocorrect(corrector, to_h_node, map_node)
|
61
61
|
end
|
@@ -44,7 +44,7 @@ module RuboCop
|
|
44
44
|
message = format(MSG, method: map_node.loc.selector.source)
|
45
45
|
add_offense(map_node.loc.selector, message: message) do |corrector|
|
46
46
|
# If the `to_set` call already has a block, do not autocorrect.
|
47
|
-
next if to_set_node.
|
47
|
+
next if to_set_node.block_literal?
|
48
48
|
|
49
49
|
autocorrect(corrector, to_set_node, map_node)
|
50
50
|
end
|
@@ -118,12 +118,14 @@ module RuboCop
|
|
118
118
|
|
119
119
|
return unless numeric && operator && replacement_supported?(operator)
|
120
120
|
|
121
|
-
[numeric, replacement(numeric, operator)]
|
121
|
+
[numeric, replacement(node, numeric, operator)]
|
122
122
|
end
|
123
123
|
|
124
|
-
def replacement(numeric, operation)
|
124
|
+
def replacement(node, numeric, operation)
|
125
125
|
if style == :predicate
|
126
126
|
[parenthesized_source(numeric), REPLACEMENTS.invert[operation.to_s]].join('.')
|
127
|
+
elsif negated?(node)
|
128
|
+
"(#{numeric.source} #{REPLACEMENTS[operation.to_s]} 0)"
|
127
129
|
else
|
128
130
|
[numeric.source, REPLACEMENTS[operation.to_s], 0].join(' ')
|
129
131
|
end
|
@@ -157,6 +159,12 @@ module RuboCop
|
|
157
159
|
end
|
158
160
|
end
|
159
161
|
|
162
|
+
def negated?(node)
|
163
|
+
return false unless (parent = node.parent)
|
164
|
+
|
165
|
+
parent.send_type? && parent.method?(:!)
|
166
|
+
end
|
167
|
+
|
160
168
|
# @!method predicate(node)
|
161
169
|
def_node_matcher :predicate, <<~PATTERN
|
162
170
|
(send $(...) ${:zero? :positive? :negative?})
|
@@ -81,7 +81,13 @@ module RuboCop
|
|
81
81
|
redundant_argument = redundant_arg_for_method(node.method_name.to_s)
|
82
82
|
return false if redundant_argument.nil?
|
83
83
|
|
84
|
-
node.first_argument.
|
84
|
+
target_argument = if node.first_argument.respond_to?(:value)
|
85
|
+
node.first_argument.value
|
86
|
+
else
|
87
|
+
node.first_argument
|
88
|
+
end
|
89
|
+
|
90
|
+
argument_matched?(target_argument, redundant_argument)
|
85
91
|
end
|
86
92
|
|
87
93
|
def redundant_arg_for_method(method_name)
|
@@ -98,6 +104,23 @@ module RuboCop
|
|
98
104
|
range_with_surrounding_space(node.first_argument.source_range, newlines: false)
|
99
105
|
end
|
100
106
|
end
|
107
|
+
|
108
|
+
def argument_matched?(target_argument, redundant_argument)
|
109
|
+
argument = if target_argument.is_a?(AST::Node)
|
110
|
+
target_argument.source
|
111
|
+
elsif exclude_cntrl_character?(target_argument, redundant_argument)
|
112
|
+
target_argument.inspect
|
113
|
+
else
|
114
|
+
target_argument.to_s
|
115
|
+
end
|
116
|
+
|
117
|
+
argument == redundant_argument
|
118
|
+
end
|
119
|
+
|
120
|
+
def exclude_cntrl_character?(target_argument, redundant_argument)
|
121
|
+
!target_argument.to_s.sub(/\A'/, '"').sub(/'\z/, '"').match?(/[[:cntrl:]]/) ||
|
122
|
+
!redundant_argument.match?(/[[:cntrl:]]/)
|
123
|
+
end
|
101
124
|
end
|
102
125
|
end
|
103
126
|
end
|
@@ -18,10 +18,10 @@ module RuboCop
|
|
18
18
|
extend AutoCorrector
|
19
19
|
|
20
20
|
MSG = 'Remove the redundant current directory path.'
|
21
|
+
RESTRICT_ON_SEND = %i[require_relative].freeze
|
21
22
|
CURRENT_DIRECTORY_PATH = './'
|
22
23
|
|
23
24
|
def on_send(node)
|
24
|
-
return unless node.method?(:require_relative)
|
25
25
|
return unless (first_argument = node.first_argument)
|
26
26
|
return unless first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH)
|
27
27
|
return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PATH))
|
@@ -79,7 +79,7 @@ module RuboCop
|
|
79
79
|
private_constant :REPLACEMENT_METHODS
|
80
80
|
|
81
81
|
def on_send(node)
|
82
|
-
return if node.arguments? || node.
|
82
|
+
return if node.arguments? || node.block_literal?
|
83
83
|
|
84
84
|
select_predicate?(node) do |select_node, filter_method|
|
85
85
|
return if RAILS_METHODS.include?(filter_method) && !active_support_extensions_enabled?
|
@@ -72,7 +72,7 @@ module RuboCop
|
|
72
72
|
ALLOWED_STRING_TOKENS = %i[tSTRING tSTRING_CONTENT].freeze
|
73
73
|
ARGUMENT_TYPES = %i[
|
74
74
|
kFALSE kNIL kSELF kTRUE tCONSTANT tCVAR tFLOAT tGVAR tIDENTIFIER tINTEGER tIVAR
|
75
|
-
|
75
|
+
tLBRACK tLCURLY tLPAREN_ARG tSTRING tSTRING_BEG tSYMBOL tXSTRING_BEG
|
76
76
|
].freeze
|
77
77
|
|
78
78
|
def on_new_investigation
|
@@ -124,10 +124,8 @@ module RuboCop
|
|
124
124
|
return true unless (node = find_node_for_line(range.line))
|
125
125
|
return false if argument_newline?(node)
|
126
126
|
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
continuation_node.source.include?("\n") || continuation_node.source.include?("\\\n")
|
127
|
+
source = node.parent ? node.parent.source : node.source
|
128
|
+
parse(source.gsub("\\\n", "\n")).valid_syntax?
|
131
129
|
end
|
132
130
|
|
133
131
|
def inside_string_literal?(range, token)
|
@@ -139,25 +137,27 @@ module RuboCop
|
|
139
137
|
# do_something \
|
140
138
|
# argument
|
141
139
|
def method_with_argument?(current_token, next_token)
|
142
|
-
current_token.type
|
140
|
+
return false if current_token.type != :tIDENTIFIER && current_token.type != :kRETURN
|
141
|
+
|
142
|
+
ARGUMENT_TYPES.include?(next_token.type)
|
143
143
|
end
|
144
144
|
|
145
|
-
# rubocop:disable Metrics/AbcSize
|
145
|
+
# rubocop:disable Metrics/AbcSize
|
146
146
|
def argument_newline?(node)
|
147
147
|
node = node.to_a.last if node.assignment?
|
148
148
|
return false if node.parenthesized_call?
|
149
149
|
|
150
150
|
node = node.children.first if node.root? && node.begin_type?
|
151
151
|
|
152
|
-
if argument_is_method?(node)
|
153
|
-
argument_newline?(node.
|
152
|
+
if argument_is_method?(node)
|
153
|
+
argument_newline?(node.first_argument)
|
154
154
|
else
|
155
155
|
return false unless method_call_with_arguments?(node)
|
156
156
|
|
157
|
-
|
157
|
+
node.loc.selector.line != node.first_argument.loc.line
|
158
158
|
end
|
159
159
|
end
|
160
|
-
# rubocop:enable Metrics/AbcSize
|
160
|
+
# rubocop:enable Metrics/AbcSize
|
161
161
|
|
162
162
|
def find_node_for_line(line)
|
163
163
|
processed_source.ast.each_node do |node|
|
@@ -165,10 +165,6 @@ module RuboCop
|
|
165
165
|
end
|
166
166
|
end
|
167
167
|
|
168
|
-
def allowed_type?(node)
|
169
|
-
node.and_type? || node.or_type? || (node.if_type? && node.ternary?)
|
170
|
-
end
|
171
|
-
|
172
168
|
def same_line?(node, line)
|
173
169
|
return false unless (source_range = node.source_range)
|
174
170
|
|
@@ -53,7 +53,7 @@ module RuboCop
|
|
53
53
|
return if interpolated_quotes?(node) || allowed_percent_q?(node)
|
54
54
|
|
55
55
|
add_offense(node) do |corrector|
|
56
|
-
delimiter =
|
56
|
+
delimiter = /\A%Q[^"]+\z|'/.match?(node.source) ? QUOTE : SINGLE_QUOTE
|
57
57
|
|
58
58
|
corrector.replace(node.loc.begin, delimiter)
|
59
59
|
corrector.replace(node.loc.end, delimiter)
|