rubocop 1.88.1 → 1.88.2
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/config/default.yml +2 -0
- data/lib/rubocop/cop/base.rb +13 -8
- data/lib/rubocop/cop/commissioner.rb +13 -11
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +12 -2
- data/lib/rubocop/cop/layout/comment_indentation.rb +9 -2
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -14
- data/lib/rubocop/cop/layout/line_length.rb +10 -5
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +6 -1
- data/lib/rubocop/cop/lint/debugger.rb +13 -1
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +7 -0
- data/lib/rubocop/cop/lint/to_json.rb +8 -1
- data/lib/rubocop/cop/lint/unreachable_code.rb +9 -4
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
- data/lib/rubocop/cop/lint/void.rb +2 -2
- data/lib/rubocop/cop/mixin/allowed_methods.rb +5 -5
- data/lib/rubocop/cop/mixin/allowed_pattern.rb +5 -1
- data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
- data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
- data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
- data/lib/rubocop/cop/style/array_intersect.rb +4 -4
- data/lib/rubocop/cop/style/collection_compact.rb +1 -1
- data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
- data/lib/rubocop/cop/style/documentation_method.rb +5 -1
- data/lib/rubocop/cop/style/hash_conversion.rb +14 -6
- data/lib/rubocop/cop/style/hash_lookup_method.rb +2 -6
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -4
- data/lib/rubocop/cop/style/if_unless_modifier.rb +4 -4
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
- data/lib/rubocop/cop/style/lambda_call.rb +11 -0
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +5 -3
- data/lib/rubocop/cop/style/method_def_parentheses.rb +4 -0
- data/lib/rubocop/cop/style/missing_respond_to_missing.rb +10 -7
- data/lib/rubocop/cop/style/mixin_usage.rb +1 -1
- data/lib/rubocop/cop/style/module_function.rb +4 -2
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
- data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
- data/lib/rubocop/cop/style/struct_inheritance.rb +3 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +18 -4
- data/lib/rubocop/runner.rb +50 -10
- data/lib/rubocop/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 8642dc64b958118e0563ed93c4837ac13aca29cb8f8a6267155ef450380ea1b3
|
|
4
|
+
data.tar.gz: 569ff18924510b28b60ff03563053d0c53983829d7f4755c0ad39cc213b41f64
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1cc966f73a8b890c335fedbc55fa0ec0f8a73099703cf46111e8619933f2d4b9436cf1c7d597f7fef0282e1e1e2f6b6adc4b4e38c7195eabb600fe97d9931526
|
|
7
|
+
data.tar.gz: 94cc97c23eeaaff0ec7ab9046a2275547fe2363449d0582733e30be26868e098d12c609703fdc2318b1e4d96b8205abf6fe537d655950cf558debfcb6224366c
|
data/config/default.yml
CHANGED
|
@@ -2213,7 +2213,9 @@ Lint/NumberedParameterAssignment:
|
|
|
2213
2213
|
Lint/NumericOperationWithConstantResult:
|
|
2214
2214
|
Description: 'Checks for numeric operations with constant results.'
|
|
2215
2215
|
Enabled: pending
|
|
2216
|
+
SafeAutoCorrect: false
|
|
2216
2217
|
VersionAdded: '1.69'
|
|
2218
|
+
VersionChanged: '1.88'
|
|
2217
2219
|
|
|
2218
2220
|
Lint/OrAssignmentToConstant:
|
|
2219
2221
|
Description: 'Checks unintended or-assignment to constant.'
|
data/lib/rubocop/cop/base.rb
CHANGED
|
@@ -331,12 +331,11 @@ module RuboCop
|
|
|
331
331
|
# @api private
|
|
332
332
|
def self.callbacks_needed
|
|
333
333
|
@callbacks_needed ||= public_instance_methods.select do |m|
|
|
334
|
-
# OPTIMIZE:
|
|
335
|
-
#
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
m.start_with?('on_', 'after_')
|
|
334
|
+
# OPTIMIZE: `start_with?` with two string arguments instead of a regex
|
|
335
|
+
# is faster in this specific case.
|
|
336
|
+
m.start_with?('on_', 'after_') &&
|
|
337
|
+
# exclude standard "callbacks" like 'on_new_investigation' unless refined
|
|
338
|
+
instance_method(m).owner != Base
|
|
340
339
|
end
|
|
341
340
|
end
|
|
342
341
|
# rubocop:enable Layout/ClassStructure
|
|
@@ -545,15 +544,21 @@ module RuboCop
|
|
|
545
544
|
end
|
|
546
545
|
|
|
547
546
|
def range_for_original(range)
|
|
547
|
+
buffer = @current_original.buffer
|
|
548
|
+
return range if @current_offset.zero? && range.source_buffer.equal?(buffer)
|
|
549
|
+
|
|
548
550
|
::Parser::Source::Range.new(
|
|
549
|
-
|
|
551
|
+
buffer,
|
|
550
552
|
range.begin_pos + @current_offset,
|
|
551
553
|
range.end_pos + @current_offset
|
|
552
554
|
)
|
|
553
555
|
end
|
|
554
556
|
|
|
555
557
|
def target_satisfies_all_gem_version_requirements?
|
|
556
|
-
self.class.gem_requirements
|
|
558
|
+
gem_requirements = self.class.gem_requirements
|
|
559
|
+
return true if gem_requirements.empty?
|
|
560
|
+
|
|
561
|
+
gem_requirements.all? do |gem_name, version_req|
|
|
557
562
|
all_gem_versions_in_target = @config.gem_versions_in_target
|
|
558
563
|
next false unless all_gem_versions_in_target
|
|
559
564
|
|
|
@@ -64,14 +64,16 @@ module RuboCop
|
|
|
64
64
|
r = '#' unless RESTRICTED_CALLBACKS.include?(method_name) # has Restricted?
|
|
65
65
|
c = '#' if NO_CHILD_NODES.include?(node_type) # has Children?
|
|
66
66
|
|
|
67
|
+
# The `after_` calls are guarded because barely any cop defines an `after_`
|
|
68
|
+
# callback, and the guard is cheaper than the method call it avoids.
|
|
67
69
|
class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
|
68
|
-
def on_#{node_type}(node)
|
|
69
|
-
trigger_responding_cops(:on_#{node_type}, node)
|
|
70
|
-
#{r} trigger_restricted_cops(:on_#{node_type}, node)
|
|
71
|
-
#{c} super(node)
|
|
72
|
-
#{c} trigger_responding_cops(:after_#{node_type}, node)
|
|
73
|
-
#{c}#{r} trigger_restricted_cops(:after_#{node_type}, node)
|
|
74
|
-
end
|
|
70
|
+
def on_#{node_type}(node) # def on_send(node)
|
|
71
|
+
trigger_responding_cops(:on_#{node_type}, node) # trigger_responding_cops(:on_send, node)
|
|
72
|
+
#{r} trigger_restricted_cops(:on_#{node_type}, node) # trigger_restricted_cops(:on_send, node)
|
|
73
|
+
#{c} super(node) # super(node)
|
|
74
|
+
#{c} trigger_responding_cops(:after_#{node_type}, node) if @callbacks[:after_#{node_type}] # trigger_responding_cops(:after_send, node) if @callbacks[:after_send]
|
|
75
|
+
#{c}#{r} trigger_restricted_cops(:after_#{node_type}, node) unless @restricted_map[:after_#{node_type}].empty? # trigger_restricted_cops(:after_send, node) unless @restricted_map[:after_send].empty?
|
|
76
|
+
end # end
|
|
75
77
|
RUBY
|
|
76
78
|
end
|
|
77
79
|
|
|
@@ -81,13 +83,13 @@ module RuboCop
|
|
|
81
83
|
|
|
82
84
|
begin_investigation(processed_source, offset: offset, original: original)
|
|
83
85
|
if processed_source.valid_syntax?
|
|
84
|
-
invoke(:on_new_investigation, @
|
|
86
|
+
invoke(:on_new_investigation, @callbacks[:on_new_investigation])
|
|
85
87
|
invoke_with_argument(:investigate, @forces, processed_source)
|
|
86
88
|
|
|
87
89
|
walk(processed_source.ast) unless @cops.empty?
|
|
88
|
-
invoke(:on_investigation_end, @
|
|
90
|
+
invoke(:on_investigation_end, @callbacks[:on_investigation_end])
|
|
89
91
|
else
|
|
90
|
-
invoke(:on_other_file, @
|
|
92
|
+
invoke(:on_other_file, @callbacks[:on_other_file])
|
|
91
93
|
end
|
|
92
94
|
reports = @cops.map { |cop| cop.send(:complete_investigation) }
|
|
93
95
|
InvestigationReport.new(processed_source, reports, @errors)
|
|
@@ -157,7 +159,7 @@ module RuboCop
|
|
|
157
159
|
end
|
|
158
160
|
|
|
159
161
|
def invoke(callback, cops)
|
|
160
|
-
cops
|
|
162
|
+
cops&.each { |cop| with_cop_error_handling(cop) { cop.send(callback) } }
|
|
161
163
|
end
|
|
162
164
|
|
|
163
165
|
def invoke_with_argument(callback, cops, arg)
|
|
@@ -83,18 +83,28 @@ module RuboCop
|
|
|
83
83
|
def duplicated_assignment_method_nodes
|
|
84
84
|
assignment_method_declarations(processed_source.ast)
|
|
85
85
|
.select(&:assignment_method?)
|
|
86
|
-
.group_by(
|
|
86
|
+
.group_by { |node| [enclosing_specification(node), node.method_name] }
|
|
87
87
|
.values
|
|
88
88
|
.select { |nodes| nodes.size > 1 }
|
|
89
89
|
end
|
|
90
90
|
|
|
91
91
|
def duplicated_indexed_assignment_method_nodes
|
|
92
92
|
indexed_assignment_method_declarations(processed_source.ast)
|
|
93
|
-
.group_by { |node|
|
|
93
|
+
.group_by { |node| indexed_assignment_key(node) }
|
|
94
94
|
.values
|
|
95
95
|
.select { |nodes| nodes.size > 1 }
|
|
96
96
|
end
|
|
97
97
|
|
|
98
|
+
def indexed_assignment_key(node)
|
|
99
|
+
[enclosing_specification(node), node.children.first.method_name, node.first_argument]
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Assignments in separate `Gem::Specification.new` blocks are not duplicates of
|
|
103
|
+
# one another, so the enclosing specification is part of the grouping key.
|
|
104
|
+
def enclosing_specification(node)
|
|
105
|
+
node.each_ancestor(:block).find { |block| gem_specification?(block) }
|
|
106
|
+
end
|
|
107
|
+
|
|
98
108
|
def register_offense(node, assignment, line_of_first_occurrence)
|
|
99
109
|
line_range = node.loc.column...node.loc.last_column
|
|
100
110
|
offense_location = source_range(processed_source.buffer, node.first_line, line_range)
|
|
@@ -155,8 +155,15 @@ module RuboCop
|
|
|
155
155
|
|
|
156
156
|
def less_indented?(line)
|
|
157
157
|
rule = config.for_cop('Layout/AccessModifierIndentation')['EnforcedStyle'] == 'outdent'
|
|
158
|
-
|
|
159
|
-
|
|
158
|
+
/\A\s*(end\b|[)}\]])/.match?(line) || (rule && bare_access_modifier?(line))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Only a bare access modifier (the keyword alone on its line) is outdented by
|
|
162
|
+
# `Layout/AccessModifierIndentation`. An inline modifier such as `private def foo`
|
|
163
|
+
# keeps the regular method indentation, so a comment above it must not be pushed
|
|
164
|
+
# one level deeper.
|
|
165
|
+
def bare_access_modifier?(line)
|
|
166
|
+
/\A\s*(private|protected|public)\s*(#.*)?\z/.match?(line)
|
|
160
167
|
end
|
|
161
168
|
|
|
162
169
|
def two_alternatives?(line)
|
|
@@ -92,13 +92,7 @@ module RuboCop
|
|
|
92
92
|
case parent.type
|
|
93
93
|
when :def, :defs then base_for_method_definition(parent)
|
|
94
94
|
when :kwbegin then parent.loc.begin
|
|
95
|
-
when :block, :numblock, :itblock
|
|
96
|
-
assignment_node = assignment_node(parent)
|
|
97
|
-
if same_line?(parent, assignment_node)
|
|
98
|
-
assignment_node.source_range
|
|
99
|
-
else
|
|
100
|
-
parent.send_node.source_range
|
|
101
|
-
end
|
|
95
|
+
when :block, :numblock, :itblock then start_line_range(parent)
|
|
102
96
|
else node.loc.keyword
|
|
103
97
|
end
|
|
104
98
|
end
|
|
@@ -143,13 +137,6 @@ module RuboCop
|
|
|
143
137
|
autocorrect(corrector, else_range)
|
|
144
138
|
end
|
|
145
139
|
end
|
|
146
|
-
|
|
147
|
-
def assignment_node(node)
|
|
148
|
-
assignment_node = node.ancestors.first
|
|
149
|
-
return unless assignment_node&.assignment?
|
|
150
|
-
|
|
151
|
-
assignment_node
|
|
152
|
-
end
|
|
153
140
|
end
|
|
154
141
|
end
|
|
155
142
|
end
|
|
@@ -420,11 +420,16 @@ module RuboCop
|
|
|
420
420
|
# The maximum allowed length of a string value is:
|
|
421
421
|
# `Max` - end delimiter (quote) - continuation characters (space and slash)
|
|
422
422
|
max_length = max - 3
|
|
423
|
-
#
|
|
424
|
-
#
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
423
|
+
# Offset by the string's starting column so the broken line actually fits
|
|
424
|
+
# within `Max`. When on the same line as its parent, use the column difference;
|
|
425
|
+
# otherwise the string is indented on its own line, so subtract that indentation.
|
|
426
|
+
# (Without this, an indented string under a multi-line parent never shortens
|
|
427
|
+
# below `Max` and the autocorrect loops, inserting empty `"" \` fragments.)
|
|
428
|
+
max_length -= if same_line?(node, node.parent)
|
|
429
|
+
column_offset_between(node.loc, node.parent.loc)
|
|
430
|
+
else
|
|
431
|
+
node.loc.column
|
|
432
|
+
end
|
|
428
433
|
node.source[0...(max_length)]
|
|
429
434
|
end
|
|
430
435
|
end
|
|
@@ -73,7 +73,12 @@ module RuboCop
|
|
|
73
73
|
end
|
|
74
74
|
|
|
75
75
|
def find_pair_ancestor(node)
|
|
76
|
-
node.each_ancestor
|
|
76
|
+
node.each_ancestor do |ancestor|
|
|
77
|
+
return ancestor if ancestor.pair_type?
|
|
78
|
+
break if grouped_expression?(ancestor) || inside_arg_list_parentheses?(node, ancestor)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
nil
|
|
77
82
|
end
|
|
78
83
|
|
|
79
84
|
def unwrap_block_node(node)
|
|
@@ -75,9 +75,10 @@ module RuboCop
|
|
|
75
75
|
MSG = 'Remove debugger entry point `%<source>s`.'
|
|
76
76
|
|
|
77
77
|
def on_send(node)
|
|
78
|
+
return unless debugger_method?(node) || debugger_require?(node)
|
|
78
79
|
return if assumed_usage_context?(node)
|
|
79
80
|
|
|
80
|
-
add_offense(node)
|
|
81
|
+
add_offense(node)
|
|
81
82
|
end
|
|
82
83
|
|
|
83
84
|
private
|
|
@@ -101,9 +102,20 @@ module RuboCop
|
|
|
101
102
|
end
|
|
102
103
|
|
|
103
104
|
def debugger_method?(send_node)
|
|
105
|
+
return false unless debugger_method_names.include?(send_node.method_name)
|
|
106
|
+
|
|
104
107
|
debugger_methods.include?(chained_method_name(send_node))
|
|
105
108
|
end
|
|
106
109
|
|
|
110
|
+
# The last segment of each configured debugger method, used to cheaply
|
|
111
|
+
# rule out the vast majority of `send` nodes before building the
|
|
112
|
+
# chained method name.
|
|
113
|
+
def debugger_method_names
|
|
114
|
+
@debugger_method_names ||= debugger_methods.to_set do |method|
|
|
115
|
+
method.to_s.split('.').last&.to_sym
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
107
119
|
def debugger_require?(send_node)
|
|
108
120
|
return false unless send_node.method?(:require) && send_node.arguments.one?
|
|
109
121
|
return false unless (argument = send_node.first_argument).str_type?
|
|
@@ -15,6 +15,13 @@ module RuboCop
|
|
|
15
15
|
# can't determine the type of `x`. If `x` is an `Array` or `String`, it doesn't perform
|
|
16
16
|
# a numeric operation.
|
|
17
17
|
#
|
|
18
|
+
# @safety
|
|
19
|
+
# This cop is unsafe because the autocorrection drops the operands, which
|
|
20
|
+
# discards any side effects of evaluating them and can change behavior when
|
|
21
|
+
# the result is not actually constant. For example, `x / x` raises
|
|
22
|
+
# `ZeroDivisionError` when `x` is `0`, and returns `Float::NAN` (not `1`)
|
|
23
|
+
# when `x` is `0.0`; replacing it with `1` silences that.
|
|
24
|
+
#
|
|
18
25
|
# @example
|
|
19
26
|
#
|
|
20
27
|
# # bad
|
|
@@ -36,9 +36,16 @@ module RuboCop
|
|
|
36
36
|
# The following used `*_args` because `to_json(*args)` has
|
|
37
37
|
# an offense of `Lint/UnusedMethodArgument` cop if `*args`
|
|
38
38
|
# is not used.
|
|
39
|
-
|
|
39
|
+
if (opening_parenthesis = node.each_child_node(:args).first.loc.begin)
|
|
40
|
+
# Explicit empty parentheses (`def to_json()`) are already present,
|
|
41
|
+
# so insert the argument inside them to avoid producing `to_json(*_args)()`.
|
|
42
|
+
corrector.insert_after(opening_parenthesis, '*_args')
|
|
43
|
+
else
|
|
44
|
+
corrector.insert_after(node.loc.name, '(*_args)')
|
|
45
|
+
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
48
|
+
alias on_defs on_def
|
|
42
49
|
end
|
|
43
50
|
end
|
|
44
51
|
end
|
|
@@ -52,10 +52,15 @@ module RuboCop
|
|
|
52
52
|
def on_begin(node)
|
|
53
53
|
expressions = *node
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
55
|
+
# Once a flow-of-control statement is reached, every following statement
|
|
56
|
+
# in the block is unreachable, not just the one immediately after it.
|
|
57
|
+
flow_reached = false
|
|
58
|
+
expressions.each_with_index do |expression, index|
|
|
59
|
+
if flow_reached
|
|
60
|
+
add_offense(expression)
|
|
61
|
+
elsif index < expressions.size - 1 && flow_expression?(expression)
|
|
62
|
+
flow_reached = true
|
|
63
|
+
end
|
|
59
64
|
end
|
|
60
65
|
end
|
|
61
66
|
|
|
@@ -36,31 +36,31 @@ module RuboCop
|
|
|
36
36
|
RESTRICT_ON_SEND = %i[+ - * / **].freeze
|
|
37
37
|
|
|
38
38
|
# @!method useless_operation?(node)
|
|
39
|
-
def_node_matcher :useless_operation?,
|
|
39
|
+
def_node_matcher :useless_operation?, <<~PATTERN
|
|
40
|
+
(call ${lvar ivar cvar gvar const (send nil? _)} $_ (int $_))
|
|
41
|
+
PATTERN
|
|
40
42
|
|
|
41
43
|
# @!method useless_abbreviated_assignment?(node)
|
|
42
|
-
def_node_matcher :useless_abbreviated_assignment?,
|
|
44
|
+
def_node_matcher :useless_abbreviated_assignment?, <<~PATTERN
|
|
45
|
+
(op-asgn ${lvasgn ivasgn cvasgn gvasgn casgn} $_ (int $_))
|
|
46
|
+
PATTERN
|
|
43
47
|
|
|
44
48
|
def on_send(node)
|
|
45
|
-
return unless useless_operation?(node)
|
|
46
|
-
|
|
47
|
-
variable, operation, number = useless_operation?(node)
|
|
49
|
+
return unless (receiver, operation, number = useless_operation?(node))
|
|
48
50
|
return unless useless?(operation, number)
|
|
49
51
|
|
|
50
52
|
add_offense(node) do |corrector|
|
|
51
|
-
corrector.replace(node,
|
|
53
|
+
corrector.replace(node, receiver.source)
|
|
52
54
|
end
|
|
53
55
|
end
|
|
54
56
|
alias on_csend on_send
|
|
55
57
|
|
|
56
58
|
def on_op_asgn(node)
|
|
57
|
-
return unless useless_abbreviated_assignment?(node)
|
|
58
|
-
|
|
59
|
-
variable, operation, number = useless_abbreviated_assignment?(node)
|
|
59
|
+
return unless (variable, operation, number = useless_abbreviated_assignment?(node))
|
|
60
60
|
return unless useless?(operation, number)
|
|
61
61
|
|
|
62
62
|
add_offense(node) do |corrector|
|
|
63
|
-
corrector.replace(node, "#{variable} = #{variable}")
|
|
63
|
+
corrector.replace(node, "#{variable.source} = #{variable.source}")
|
|
64
64
|
end
|
|
65
65
|
end
|
|
66
66
|
|
|
@@ -212,7 +212,7 @@ module RuboCop
|
|
|
212
212
|
end
|
|
213
213
|
|
|
214
214
|
def check_nonmutating(node)
|
|
215
|
-
return unless node.type?(:
|
|
215
|
+
return unless node.type?(:call, :any_block)
|
|
216
216
|
|
|
217
217
|
method_name = node.method_name
|
|
218
218
|
return unless NONMUTATING_METHODS.include?(method_name)
|
|
@@ -265,7 +265,7 @@ module RuboCop
|
|
|
265
265
|
end
|
|
266
266
|
|
|
267
267
|
def autocorrect_nonmutating_send(corrector, node, suggestion)
|
|
268
|
-
send_node = if node.
|
|
268
|
+
send_node = if node.call_type?
|
|
269
269
|
node
|
|
270
270
|
else
|
|
271
271
|
node.send_node
|
|
@@ -25,11 +25,11 @@ module RuboCop
|
|
|
25
25
|
|
|
26
26
|
# @api public
|
|
27
27
|
def allowed_methods
|
|
28
|
-
if cop_config_deprecated_values.any?(Regexp)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
@allowed_methods ||= if cop_config_deprecated_values.any?(Regexp)
|
|
29
|
+
cop_config_allowed_methods
|
|
30
|
+
else
|
|
31
|
+
cop_config_allowed_methods + cop_config_deprecated_values
|
|
32
|
+
end
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
def cop_config_allowed_methods
|
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def matches_allowed_pattern?(line)
|
|
30
|
-
|
|
30
|
+
allowed_pattern_regexps.any? { |pattern| pattern.match?(line) }
|
|
31
31
|
end
|
|
32
32
|
|
|
33
33
|
# @deprecated Use matches_allowed_pattern? instead
|
|
@@ -49,6 +49,10 @@ module RuboCop
|
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
+
def allowed_pattern_regexps
|
|
53
|
+
@allowed_pattern_regexps ||= allowed_patterns.map { |pattern| Regexp.new(pattern) }
|
|
54
|
+
end
|
|
55
|
+
|
|
52
56
|
def cop_config_patterns_values
|
|
53
57
|
@cop_config_patterns_values ||=
|
|
54
58
|
Array(cop_config.fetch('AllowedPatterns', [])) +
|
|
@@ -5,12 +5,16 @@ module RuboCop
|
|
|
5
5
|
# This module encapsulates the ability to forbid certain patterns in a cop.
|
|
6
6
|
module ForbiddenPattern
|
|
7
7
|
def forbidden_pattern?(name)
|
|
8
|
-
|
|
8
|
+
forbidden_pattern_regexps.any? { |pattern| pattern.match?(name) }
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
def forbidden_patterns
|
|
12
12
|
cop_config.fetch('ForbiddenPatterns', [])
|
|
13
13
|
end
|
|
14
|
+
|
|
15
|
+
def forbidden_pattern_regexps
|
|
16
|
+
@forbidden_pattern_regexps ||= forbidden_patterns.map { |pattern| Regexp.new(pattern) }
|
|
17
|
+
end
|
|
14
18
|
end
|
|
15
19
|
end
|
|
16
20
|
end
|
|
@@ -47,7 +47,7 @@ module RuboCop
|
|
|
47
47
|
#
|
|
48
48
|
# # bad
|
|
49
49
|
# array1.any? { |elem| array2.member?(elem) }
|
|
50
|
-
# array1.none? { |elem| array2.
|
|
50
|
+
# array1.none? { |elem| array2.include?(elem) }
|
|
51
51
|
#
|
|
52
52
|
# # good
|
|
53
53
|
# array1.intersect?(array2)
|
|
@@ -121,15 +121,15 @@ module RuboCop
|
|
|
121
121
|
(block
|
|
122
122
|
(call $_receiver ${:any? :none?})
|
|
123
123
|
(args (arg _key))
|
|
124
|
-
(send $_argument :member? (lvar _key))
|
|
124
|
+
(send $_argument {:member? :include?} (lvar _key))
|
|
125
125
|
)
|
|
126
126
|
(numblock
|
|
127
127
|
(call $_receiver ${:any? :none?}) 1
|
|
128
|
-
(send $_argument :member? (lvar :_1))
|
|
128
|
+
(send $_argument {:member? :include?} (lvar :_1))
|
|
129
129
|
)
|
|
130
130
|
(itblock
|
|
131
131
|
(call $_receiver ${:any? :none?}) :it
|
|
132
|
-
(send $_argument :member? (lvar :it))
|
|
132
|
+
(send $_argument {:member? :include?} (lvar :it))
|
|
133
133
|
)
|
|
134
134
|
}
|
|
135
135
|
PATTERN
|
|
@@ -96,7 +96,7 @@ module RuboCop
|
|
|
96
96
|
|
|
97
97
|
# @!method grep_v_with_nil?(node)
|
|
98
98
|
def_node_matcher :grep_v_with_nil?, <<~PATTERN
|
|
99
|
-
(
|
|
99
|
+
(call _ :grep_v {(nil) (const {nil? cbase} :NilClass)})
|
|
100
100
|
PATTERN
|
|
101
101
|
|
|
102
102
|
def on_send(node)
|
|
@@ -57,11 +57,15 @@ module RuboCop
|
|
|
57
57
|
private
|
|
58
58
|
|
|
59
59
|
def parentheses_required?(node, arguments_range)
|
|
60
|
-
return true if node.single_line? && !node.endless?
|
|
61
|
-
|
|
62
60
|
end_pos = arguments_range.end.end_pos
|
|
63
61
|
token_after_argument = range_between(end_pos, end_pos + 1).source
|
|
64
62
|
|
|
63
|
+
# A `;` after the parentheses (e.g. `def foo(); end`) already separates the
|
|
64
|
+
# signature from the body, so the parentheses can be removed (`def foo; end`).
|
|
65
|
+
return false if token_after_argument == ';'
|
|
66
|
+
|
|
67
|
+
return true if node.single_line? && !node.endless?
|
|
68
|
+
|
|
65
69
|
token_after_argument == '='
|
|
66
70
|
end
|
|
67
71
|
end
|
|
@@ -140,7 +140,11 @@ module RuboCop
|
|
|
140
140
|
end
|
|
141
141
|
|
|
142
142
|
def method_allowed?(node)
|
|
143
|
-
|
|
143
|
+
# For a modifier form like `module_function def foo; end`, `node` is the
|
|
144
|
+
# `module_function`/`ruby2_keywords` send; the real method name is on its
|
|
145
|
+
# `def`/`defs` argument, not the modifier itself.
|
|
146
|
+
method_name = node.send_type? ? node.first_argument.method_name : node.method_name
|
|
147
|
+
allowed_methods.include?(method_name)
|
|
144
148
|
end
|
|
145
149
|
|
|
146
150
|
def allowed_methods
|
|
@@ -121,16 +121,24 @@ module RuboCop
|
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
def multi_argument(node)
|
|
124
|
+
# A splat argument can expand to any number of elements, so the pairs
|
|
125
|
+
# can't be built statically and there is no literal hash to suggest.
|
|
126
|
+
return if node.arguments.any?(&:splat_type?)
|
|
127
|
+
|
|
124
128
|
if node.arguments.count.odd?
|
|
125
129
|
add_offense(node, message: MSG_LITERAL_MULTI_ARG)
|
|
126
130
|
else
|
|
127
|
-
|
|
128
|
-
|
|
131
|
+
correct_multi_argument(node)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
129
134
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
def correct_multi_argument(node)
|
|
136
|
+
add_offense(node, message: MSG_LITERAL_MULTI_ARG) do |corrector|
|
|
137
|
+
corrector.replace(node, args_to_hash(node.arguments))
|
|
138
|
+
|
|
139
|
+
parent = node.parent
|
|
140
|
+
if parent&.send_type? && !parent.method?(:to_h) && !parent.parenthesized?
|
|
141
|
+
add_parentheses(parent, corrector)
|
|
134
142
|
end
|
|
135
143
|
end
|
|
136
144
|
end
|
|
@@ -74,7 +74,7 @@ module RuboCop
|
|
|
74
74
|
|
|
75
75
|
def offense_for_brackets?(node)
|
|
76
76
|
style == :brackets && node.receiver && node.method?(:fetch) && node.arguments.one? &&
|
|
77
|
-
!node.block_literal?
|
|
77
|
+
!node.block_literal? && !node.csend_type?
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def offense_for_fetch?(node)
|
|
@@ -84,11 +84,7 @@ module RuboCop
|
|
|
84
84
|
def correct_fetch_to_brackets(corrector, node)
|
|
85
85
|
key = node.first_argument.source
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
corrector.replace(node, "(#{node.receiver.source}[#{key}])")
|
|
89
|
-
else
|
|
90
|
-
corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
|
|
91
|
-
end
|
|
87
|
+
corrector.replace(node.loc.dot.join(node.source_range.end), "[#{key}]")
|
|
92
88
|
end
|
|
93
89
|
|
|
94
90
|
def correct_brackets_to_fetch(corrector, node)
|
|
@@ -216,23 +216,34 @@ module RuboCop
|
|
|
216
216
|
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
217
217
|
|
|
218
218
|
def correct_assignment(corrector, node, expression, insert_position)
|
|
219
|
+
indentation = indentation_of(node.parent)
|
|
220
|
+
|
|
219
221
|
if insert_position == :after_condition
|
|
220
222
|
assignment = node.parent.source_range.with(end_pos: node.source_range.begin_pos)
|
|
221
223
|
corrector.remove(assignment)
|
|
222
|
-
corrector.insert_after(node, "\n#{assignment.source}#{expression.source}")
|
|
224
|
+
corrector.insert_after(node, "\n#{indentation}#{assignment.source}#{expression.source}")
|
|
223
225
|
else
|
|
224
|
-
corrector.insert_before(node.parent, "#{expression.source}\n")
|
|
226
|
+
corrector.insert_before(node.parent, "#{expression.source}\n#{indentation}")
|
|
225
227
|
end
|
|
226
228
|
end
|
|
227
229
|
|
|
228
230
|
def correct_no_assignment(corrector, node, expression, insert_position)
|
|
231
|
+
indentation = indentation_of(node)
|
|
232
|
+
|
|
229
233
|
if insert_position == :after_condition
|
|
230
|
-
corrector.insert_after(node, "\n#{expression.source}")
|
|
234
|
+
corrector.insert_after(node, "\n#{indentation}#{expression.source}")
|
|
231
235
|
else
|
|
232
|
-
corrector.insert_before(node, "#{expression.source}\n")
|
|
236
|
+
corrector.insert_before(node, "#{expression.source}\n#{indentation}")
|
|
233
237
|
end
|
|
234
238
|
end
|
|
235
239
|
|
|
240
|
+
# The leading indentation of the line the conditional starts on, so a
|
|
241
|
+
# hoisted expression keeps the surrounding nesting instead of landing
|
|
242
|
+
# at column zero.
|
|
243
|
+
def indentation_of(node)
|
|
244
|
+
' ' * node.source_range.column
|
|
245
|
+
end
|
|
246
|
+
|
|
236
247
|
def last_child_of_parent?(node)
|
|
237
248
|
return true unless (parent = node.parent)
|
|
238
249
|
|
|
@@ -91,7 +91,7 @@ module RuboCop
|
|
|
91
91
|
|
|
92
92
|
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
93
93
|
def on_if(node)
|
|
94
|
-
return if endless_method?(node.body) || node.
|
|
94
|
+
return if endless_method?(node.body) || node.each_ancestor(:dstr).any?
|
|
95
95
|
|
|
96
96
|
condition = node.condition
|
|
97
97
|
return if defined_nodes(condition).any? { |n| defined_argument_is_undefined?(node, n) } ||
|
|
@@ -119,7 +119,7 @@ module RuboCop
|
|
|
119
119
|
if condition.defined_type?
|
|
120
120
|
[condition]
|
|
121
121
|
else
|
|
122
|
-
condition.each_descendant
|
|
122
|
+
condition.each_descendant(:defined?)
|
|
123
123
|
end
|
|
124
124
|
end
|
|
125
125
|
|
|
@@ -137,7 +137,7 @@ module RuboCop
|
|
|
137
137
|
if condition.any_match_pattern_type?
|
|
138
138
|
[condition]
|
|
139
139
|
else
|
|
140
|
-
condition.each_descendant
|
|
140
|
+
condition.each_descendant(:any_match_pattern)
|
|
141
141
|
end
|
|
142
142
|
end
|
|
143
143
|
|
|
@@ -350,7 +350,7 @@ module RuboCop
|
|
|
350
350
|
end
|
|
351
351
|
|
|
352
352
|
def comment_on_node_line(node)
|
|
353
|
-
processed_source.
|
|
353
|
+
processed_source.comment_at_line(node.first_line)
|
|
354
354
|
end
|
|
355
355
|
|
|
356
356
|
def remove_comment(corrector, _node, comment)
|
|
@@ -75,7 +75,9 @@ module RuboCop
|
|
|
75
75
|
def invertible?(node) # rubocop:disable Metrics/CyclomaticComplexity
|
|
76
76
|
case node&.type
|
|
77
77
|
when :begin
|
|
78
|
-
|
|
78
|
+
# A multi-statement `begin` evaluates to its last expression, so it
|
|
79
|
+
# cannot be inverted by negating a single child.
|
|
80
|
+
node.children.one? && invertible?(node.children.first)
|
|
79
81
|
when :send
|
|
80
82
|
return false if inheritance_check?(node)
|
|
81
83
|
|
|
@@ -124,12 +126,17 @@ module RuboCop
|
|
|
124
126
|
end
|
|
125
127
|
|
|
126
128
|
def preferred_logical_condition(node)
|
|
127
|
-
preferred_lhs =
|
|
128
|
-
preferred_rhs =
|
|
129
|
+
preferred_lhs = preferred_operand(node, node.lhs)
|
|
130
|
+
preferred_rhs = preferred_operand(node, node.rhs)
|
|
129
131
|
|
|
130
132
|
"#{preferred_lhs} #{node.inverse_operator} #{preferred_rhs}"
|
|
131
133
|
end
|
|
132
134
|
|
|
135
|
+
def preferred_operand(node, operand)
|
|
136
|
+
preferred = preferred_condition(operand)
|
|
137
|
+
parenthesize_inverted_operand?(node, operand) ? "(#{preferred})" : preferred
|
|
138
|
+
end
|
|
139
|
+
|
|
133
140
|
def autocorrect(corrector, node)
|
|
134
141
|
case node.type
|
|
135
142
|
when :begin
|
|
@@ -138,11 +145,24 @@ module RuboCop
|
|
|
138
145
|
autocorrect_send_node(corrector, node)
|
|
139
146
|
when :or, :and
|
|
140
147
|
corrector.replace(node.loc.operator, node.inverse_operator)
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
autocorrect_operand(corrector, node, node.lhs)
|
|
149
|
+
autocorrect_operand(corrector, node, node.rhs)
|
|
143
150
|
end
|
|
144
151
|
end
|
|
145
152
|
|
|
153
|
+
def autocorrect_operand(corrector, node, operand)
|
|
154
|
+
autocorrect(corrector, operand)
|
|
155
|
+
corrector.wrap(operand, '(', ')') if parenthesize_inverted_operand?(node, operand)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# When an `and` is nested in an `or`, inverting both operators turns the
|
|
159
|
+
# `and` into an `or` that now binds looser than its parent, so it must be
|
|
160
|
+
# parenthesized to keep the original grouping (`a && b || c` inverts to
|
|
161
|
+
# `(!a || !b) && !c`, not `!a || !b && !c`).
|
|
162
|
+
def parenthesize_inverted_operand?(node, operand)
|
|
163
|
+
node.or_type? && operand.and_type?
|
|
164
|
+
end
|
|
165
|
+
|
|
146
166
|
def autocorrect_send_node(corrector, node)
|
|
147
167
|
if node.method?(:!)
|
|
148
168
|
corrector.remove(node.loc.selector)
|
|
@@ -27,6 +27,9 @@ module RuboCop
|
|
|
27
27
|
|
|
28
28
|
def on_send(node)
|
|
29
29
|
return unless node.receiver
|
|
30
|
+
# Rewriting the call rebuilds it as a single expression, which would drop
|
|
31
|
+
# any comments inside the argument list, so leave it to be fixed manually.
|
|
32
|
+
return if comments_in_node?(node)
|
|
30
33
|
|
|
31
34
|
if offense?(node)
|
|
32
35
|
prefer = prefer(node)
|
|
@@ -48,6 +51,14 @@ module RuboCop
|
|
|
48
51
|
|
|
49
52
|
private
|
|
50
53
|
|
|
54
|
+
def comments_in_node?(node)
|
|
55
|
+
range = node.source_range
|
|
56
|
+
processed_source.comments.any? do |comment|
|
|
57
|
+
range.begin_pos <= comment.source_range.begin_pos &&
|
|
58
|
+
comment.source_range.end_pos <= range.end_pos
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
51
62
|
def offense?(node)
|
|
52
63
|
(explicit_style? && node.implicit_call?) || (implicit_style? && !node.implicit_call?)
|
|
53
64
|
end
|
|
@@ -50,9 +50,11 @@ module RuboCop
|
|
|
50
50
|
private
|
|
51
51
|
|
|
52
52
|
def register_offense(node)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
range = offense_range(node)
|
|
54
|
+
return if processed_source.contains_comment?(range)
|
|
55
|
+
|
|
56
|
+
add_offense(range) do |corrector|
|
|
57
|
+
corrector.remove(range)
|
|
56
58
|
end
|
|
57
59
|
end
|
|
58
60
|
|
|
@@ -102,6 +102,10 @@ module RuboCop
|
|
|
102
102
|
MSG_PRESENT = 'Use def without parentheses.'
|
|
103
103
|
MSG_MISSING = 'Use def with parentheses when there are parameters.'
|
|
104
104
|
|
|
105
|
+
def self.autocorrect_incompatible_with
|
|
106
|
+
[Style::ArgumentsForwarding]
|
|
107
|
+
end
|
|
108
|
+
|
|
105
109
|
def on_def(node)
|
|
106
110
|
args = node.arguments
|
|
107
111
|
|
|
@@ -65,16 +65,19 @@ module RuboCop
|
|
|
65
65
|
private
|
|
66
66
|
|
|
67
67
|
def implements_respond_to_missing?(node)
|
|
68
|
-
|
|
68
|
+
scope = enclosing_scope(node)
|
|
69
|
+
search_root = scope || node.parent
|
|
70
|
+
return false unless search_root
|
|
69
71
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
child = descendant.children.first
|
|
74
|
-
return true if child.respond_to?(:method?) && child.method?(:respond_to_missing?)
|
|
72
|
+
search_root.each_descendant(node.type).any? do |descendant|
|
|
73
|
+
descendant.method?(:respond_to_missing?) && enclosing_scope(descendant).equal?(scope)
|
|
75
74
|
end
|
|
75
|
+
end
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
# The class/module/`class << self` body that lexically contains `node`,
|
|
78
|
+
# or `nil` when `node` is defined at the top level.
|
|
79
|
+
def enclosing_scope(node)
|
|
80
|
+
node.each_ancestor(:class, :module, :sclass).first
|
|
78
81
|
end
|
|
79
82
|
end
|
|
80
83
|
end
|
|
@@ -110,9 +110,11 @@ module RuboCop
|
|
|
110
110
|
def_node_matcher :private_directive?, '(send nil? :private ...)'
|
|
111
111
|
|
|
112
112
|
def on_module(node)
|
|
113
|
-
return unless node.body
|
|
113
|
+
return unless node.body
|
|
114
114
|
|
|
115
|
-
|
|
115
|
+
body_nodes = node.body.begin_type? ? node.body.children : [node.body]
|
|
116
|
+
|
|
117
|
+
each_wrong_style(body_nodes) do |child_node|
|
|
116
118
|
add_offense(child_node) do |corrector|
|
|
117
119
|
next if style == :forbidden
|
|
118
120
|
|
|
@@ -26,7 +26,9 @@ module RuboCop
|
|
|
26
26
|
def on_send(node)
|
|
27
27
|
return unless (first_argument = node.first_argument)
|
|
28
28
|
return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PREFIX))
|
|
29
|
-
|
|
29
|
+
|
|
30
|
+
content = leading_path_content(first_argument)
|
|
31
|
+
return unless (redundant_length = redundant_path_length(content))
|
|
30
32
|
|
|
31
33
|
begin_pos = first_argument.source_range.begin.begin_pos + index
|
|
32
34
|
end_pos = begin_pos + redundant_length
|
|
@@ -39,6 +41,16 @@ module RuboCop
|
|
|
39
41
|
|
|
40
42
|
private
|
|
41
43
|
|
|
44
|
+
# The literal text at the start of the path, which is the whole string
|
|
45
|
+
# for a plain string and the leading literal segment for an interpolated
|
|
46
|
+
# one (`nil` when it starts with interpolation).
|
|
47
|
+
def leading_path_content(node)
|
|
48
|
+
return node.str_content if node.str_type?
|
|
49
|
+
return unless node.dstr_type? && (first = node.children.first)&.str_type?
|
|
50
|
+
|
|
51
|
+
first.str_content
|
|
52
|
+
end
|
|
53
|
+
|
|
42
54
|
def redundant_path_length(path)
|
|
43
55
|
return unless (match = path&.match(REDUNDANT_CURRENT_DIRECTORY_PREFIX))
|
|
44
56
|
|
|
@@ -48,8 +48,13 @@ module RuboCop
|
|
|
48
48
|
def need_heredoc_delimiter_quotes?(node)
|
|
49
49
|
heredoc_delimiter = node.source.delete(heredoc_type(node))
|
|
50
50
|
return true unless heredoc_delimiter.start_with?("'", '"')
|
|
51
|
+
return true if node.loc.heredoc_end.source.strip.match?(/\W/)
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
# A double-quoted delimiter interpolates exactly like an unquoted one,
|
|
54
|
+
# so its quotes are always redundant. A single-quoted delimiter is
|
|
55
|
+
# required when the body contains interpolation or escapes that would
|
|
56
|
+
# otherwise be evaluated.
|
|
57
|
+
heredoc_delimiter.start_with?("'") &&
|
|
53
58
|
node.loc.heredoc_body.source.match?(STRING_INTERPOLATION_OR_ESCAPED_CHARACTER_PATTERN)
|
|
54
59
|
end
|
|
55
60
|
end
|
|
@@ -41,7 +41,9 @@ module RuboCop
|
|
|
41
41
|
return unless struct_constructor?(node.parent_class)
|
|
42
42
|
|
|
43
43
|
add_offense(node.parent_class) do |corrector|
|
|
44
|
-
corrector.remove(
|
|
44
|
+
corrector.remove(
|
|
45
|
+
range_with_surrounding_space(node.loc.keyword, side: :right, newlines: false)
|
|
46
|
+
)
|
|
45
47
|
corrector.replace(node.loc.operator, '=')
|
|
46
48
|
|
|
47
49
|
correct_parent(node.parent_class, corrector)
|
|
@@ -234,7 +234,7 @@ module RuboCop
|
|
|
234
234
|
def autocorrect_class(corrector, node)
|
|
235
235
|
kind = trivial_accessor_kind(node)
|
|
236
236
|
|
|
237
|
-
return unless names_match?(node) && kind
|
|
237
|
+
return unless names_match?(node) && !node.predicate_method? && kind
|
|
238
238
|
|
|
239
239
|
indent = ' ' * node.loc.column
|
|
240
240
|
corrector.replace(
|
data/lib/rubocop/cop/team.rb
CHANGED
|
@@ -59,6 +59,16 @@ module RuboCop
|
|
|
59
59
|
|
|
60
60
|
attr_reader :errors, :warnings, :updated_source_file, :cops
|
|
61
61
|
|
|
62
|
+
# When set to true, the corrected source is not written back to the
|
|
63
|
+
# inspected file; it is exposed through `#updated_source` instead.
|
|
64
|
+
# @api private
|
|
65
|
+
attr_accessor :defer_corrections
|
|
66
|
+
|
|
67
|
+
# The corrected source of the last investigation, if corrections were
|
|
68
|
+
# made with `#defer_corrections` enabled.
|
|
69
|
+
# @api private
|
|
70
|
+
attr_reader :updated_source
|
|
71
|
+
|
|
62
72
|
alias updated_source_file? updated_source_file
|
|
63
73
|
|
|
64
74
|
def initialize(cops, config = nil, options = {})
|
|
@@ -135,20 +145,24 @@ module RuboCop
|
|
|
135
145
|
|
|
136
146
|
def autocorrect(processed_source, corrector)
|
|
137
147
|
@updated_source_file = false
|
|
148
|
+
@updated_source = nil
|
|
138
149
|
return unless autocorrect?
|
|
139
150
|
return unless corrector
|
|
140
151
|
return if corrector.empty?
|
|
141
152
|
|
|
142
|
-
|
|
153
|
+
apply_correction(processed_source, corrector.rewrite)
|
|
154
|
+
@updated_source_file = true
|
|
155
|
+
end
|
|
143
156
|
|
|
157
|
+
def apply_correction(processed_source, new_source)
|
|
144
158
|
if @options[:stdin]
|
|
145
159
|
# holds source read in from stdin, when --stdin option is used
|
|
146
160
|
@options[:stdin] = new_source
|
|
161
|
+
elsif defer_corrections
|
|
162
|
+
@updated_source = new_source
|
|
147
163
|
else
|
|
148
|
-
|
|
149
|
-
File.write(filename, new_source)
|
|
164
|
+
File.write(processed_source.file_path, new_source)
|
|
150
165
|
end
|
|
151
|
-
@updated_source_file = true
|
|
152
166
|
end
|
|
153
167
|
|
|
154
168
|
def be_ready
|
data/lib/rubocop/runner.rb
CHANGED
|
@@ -346,16 +346,18 @@ module RuboCop
|
|
|
346
346
|
# inspection iteration. This is used to output meaningful infinite loop
|
|
347
347
|
# error message.
|
|
348
348
|
offenses_by_iteration = []
|
|
349
|
+
corrected_source = nil
|
|
349
350
|
|
|
350
|
-
# When running with --autocorrect, we need to inspect the file
|
|
351
|
-
#
|
|
352
|
-
#
|
|
353
|
-
#
|
|
351
|
+
# When running with --autocorrect, we need to inspect the file until no
|
|
352
|
+
# more corrections are made. This is because automatic corrections can
|
|
353
|
+
# introduce new offenses. In the normal case the loop is only executed
|
|
354
|
+
# once. The corrections are kept in memory while iterating and written
|
|
355
|
+
# back to the file when the loop is done.
|
|
354
356
|
iterate_until_no_changes(processed_source, offenses_by_iteration) do
|
|
355
357
|
# The offenses that couldn't be corrected will be found again so we
|
|
356
358
|
# only keep the corrected ones in order to avoid duplicate reporting.
|
|
357
359
|
!offenses_by_iteration.empty? && offenses_by_iteration.last.select!(&:corrected?)
|
|
358
|
-
new_offenses, updated_source_file =
|
|
360
|
+
team, new_offenses, updated_source_file = inspect_iteration(processed_source)
|
|
359
361
|
offenses_by_iteration.push(new_offenses)
|
|
360
362
|
|
|
361
363
|
# We have to reprocess the source to pickup the changes. Since the
|
|
@@ -364,12 +366,43 @@ module RuboCop
|
|
|
364
366
|
break unless updated_source_file
|
|
365
367
|
|
|
366
368
|
# Autocorrect has happened, don't use the prism result since it is stale.
|
|
367
|
-
|
|
369
|
+
# With --stdin the corrected source is kept in @options[:stdin] instead.
|
|
370
|
+
corrected_source = team.updated_source
|
|
371
|
+
processed_source = get_processed_source(file, nil, source: corrected_source)
|
|
368
372
|
end
|
|
369
373
|
|
|
370
374
|
# Return summary of corrected offenses after all iterations
|
|
371
|
-
|
|
372
|
-
|
|
375
|
+
[processed_source, offenses_by_iteration.flatten.uniq]
|
|
376
|
+
ensure
|
|
377
|
+
# Write the file once, even when the loop was left through an exception
|
|
378
|
+
# (e.g. an infinite correction loop), like the per-iteration writes
|
|
379
|
+
# used to be.
|
|
380
|
+
File.write(file, corrected_source) if corrected_source
|
|
381
|
+
end
|
|
382
|
+
|
|
383
|
+
def inspect_iteration(processed_source)
|
|
384
|
+
team = mobilize_team(processed_source)
|
|
385
|
+
team.defer_corrections = in_memory_corrections_possible?
|
|
386
|
+
offenses, updated_source_file = inspect_file(processed_source, team)
|
|
387
|
+
[team, offenses, updated_source_file]
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
# When corrections were written to disk and read back between iterations,
|
|
391
|
+
# the text-mode write converted LF to CRLF on Windows, and cops like
|
|
392
|
+
# `Layout/EndOfLine` rely on seeing the source as it would be on disk.
|
|
393
|
+
# Apply the same conversion to the in-memory source. The final `File.write`
|
|
394
|
+
# still performs it for the file itself.
|
|
395
|
+
def emulate_write_read_cycle(source)
|
|
396
|
+
return source unless Platform.windows?
|
|
397
|
+
|
|
398
|
+
source.encode(source.encoding, crlf_newline: true)
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
# Custom ruby extractors may derive their fragments from the file on
|
|
402
|
+
# disk rather than from the passed processed source, so corrections can
|
|
403
|
+
# only be kept in memory when the default extractor is used.
|
|
404
|
+
def in_memory_corrections_possible?
|
|
405
|
+
self.class.ruby_extractors.one?
|
|
373
406
|
end
|
|
374
407
|
|
|
375
408
|
def iterate_until_no_changes(source, offenses_by_iteration)
|
|
@@ -552,12 +585,19 @@ module RuboCop
|
|
|
552
585
|
end
|
|
553
586
|
|
|
554
587
|
# rubocop:disable Metrics/MethodLength
|
|
555
|
-
def get_processed_source(file, prism_result)
|
|
588
|
+
def get_processed_source(file, prism_result, source: nil)
|
|
556
589
|
config = @config_store.for_file(file)
|
|
557
590
|
ruby_version = config.target_ruby_version
|
|
558
591
|
parser_engine = config.parser_engine
|
|
559
592
|
|
|
560
|
-
processed_source = if
|
|
593
|
+
processed_source = if source
|
|
594
|
+
ProcessedSource.new(
|
|
595
|
+
emulate_write_read_cycle(source),
|
|
596
|
+
ruby_version,
|
|
597
|
+
file,
|
|
598
|
+
parser_engine: parser_engine
|
|
599
|
+
)
|
|
600
|
+
elsif @options[:stdin]
|
|
561
601
|
ProcessedSource.new(
|
|
562
602
|
@options[:stdin],
|
|
563
603
|
ruby_version,
|
data/lib/rubocop/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubocop
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.88.
|
|
4
|
+
version: 1.88.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Bozhidar Batsov
|
|
@@ -1119,7 +1119,7 @@ licenses:
|
|
|
1119
1119
|
- MIT
|
|
1120
1120
|
metadata:
|
|
1121
1121
|
homepage_uri: https://rubocop.org/
|
|
1122
|
-
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.88.
|
|
1122
|
+
changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.88.2
|
|
1123
1123
|
source_code_uri: https://github.com/rubocop/rubocop/
|
|
1124
1124
|
documentation_uri: https://docs.rubocop.org/rubocop/1.88/
|
|
1125
1125
|
bug_tracker_uri: https://github.com/rubocop/rubocop/issues
|