rubocop 1.88.0 → 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 +5 -1
- data/lib/rubocop/cop/base.rb +13 -8
- data/lib/rubocop/cop/bundler/gem_comment.rb +3 -1
- data/lib/rubocop/cop/commissioner.rb +13 -11
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +7 -1
- data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +8 -1
- data/lib/rubocop/cop/gemspec/development_dependencies.rb +1 -1
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +12 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +4 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +17 -0
- data/lib/rubocop/cop/layout/class_structure.rb +7 -3
- data/lib/rubocop/cop/layout/comment_indentation.rb +9 -2
- data/lib/rubocop/cop/layout/condition_position.rb +13 -3
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -14
- data/lib/rubocop/cop/layout/empty_comment.rb +8 -10
- data/lib/rubocop/cop/layout/empty_line_between_defs.rb +14 -1
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +13 -14
- data/lib/rubocop/cop/layout/indentation_width.rb +28 -0
- 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/layout/space_around_operators.rb +6 -2
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +13 -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_enum_arguments.rb +7 -1
- data/lib/rubocop/cop/lint/to_json.rb +8 -1
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +32 -8
- 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/metrics/method_length.rb +1 -1
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +38 -7
- 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/hash_subset.rb +8 -0
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +4 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +1 -1
- data/lib/rubocop/cop/naming/file_name.rb +4 -3
- data/lib/rubocop/cop/naming/inclusive_language.rb +8 -2
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +9 -0
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +9 -3
- data/lib/rubocop/cop/security/io_methods.rb +1 -1
- data/lib/rubocop/cop/security/marshal_load.rb +1 -1
- data/lib/rubocop/cop/style/accessor_grouping.rb +11 -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/data_inheritance.rb +4 -0
- data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
- data/lib/rubocop/cop/style/dir_empty.rb +4 -0
- data/lib/rubocop/cop/style/documentation_method.rb +5 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +12 -2
- data/lib/rubocop/cop/style/empty_class_definition.rb +8 -1
- data/lib/rubocop/cop/style/empty_heredoc.rb +4 -0
- data/lib/rubocop/cop/style/empty_literal.rb +7 -2
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +30 -20
- data/lib/rubocop/cop/style/env_home.rb +4 -0
- data/lib/rubocop/cop/style/even_odd.rb +11 -1
- data/lib/rubocop/cop/style/exact_regexp_match.rb +8 -1
- data/lib/rubocop/cop/style/file_null.rb +4 -2
- data/lib/rubocop/cop/style/format_string.rb +13 -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/hash_syntax.rb +2 -0
- 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/if_with_semicolon.rb +9 -1
- data/lib/rubocop/cop/style/inline_comment.rb +1 -1
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
- data/lib/rubocop/cop/style/keyword_arguments_merging.rb +4 -0
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +7 -3
- data/lib/rubocop/cop/style/lambda.rb +7 -1
- data/lib/rubocop/cop/style/lambda_call.rb +11 -0
- data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +11 -0
- data/lib/rubocop/cop/style/map_into_array.rb +1 -1
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +11 -5
- data/lib/rubocop/cop/style/method_def_parentheses.rb +5 -1
- data/lib/rubocop/cop/style/min_max_comparison.rb +3 -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/multiline_if_then.rb +1 -1
- data/lib/rubocop/cop/style/multiline_memoization.rb +7 -1
- data/lib/rubocop/cop/style/multiline_method_signature.rb +11 -4
- data/lib/rubocop/cop/style/nil_lambda.rb +8 -0
- data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
- data/lib/rubocop/cop/style/open_struct_use.rb +1 -1
- data/lib/rubocop/cop/style/option_hash.rb +1 -1
- data/lib/rubocop/cop/style/optional_arguments.rb +1 -0
- data/lib/rubocop/cop/style/parallel_assignment.rb +11 -2
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +2 -0
- data/lib/rubocop/cop/style/perl_backrefs.rb +5 -3
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
- data/lib/rubocop/cop/style/redundant_exception.rb +6 -0
- data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +28 -0
- data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +11 -3
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +8 -4
- data/lib/rubocop/cop/style/redundant_self.rb +9 -0
- data/lib/rubocop/cop/style/redundant_struct_keyword_init.rb +23 -4
- data/lib/rubocop/cop/style/semicolon.rb +4 -4
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +17 -4
- data/lib/rubocop/cop/style/string_hash_keys.rb +1 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +3 -1
- data/lib/rubocop/cop/style/ternary_parentheses.rb +11 -0
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +7 -8
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/team.rb +18 -4
- data/lib/rubocop/runner.rb +55 -13
- 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.'
|
|
@@ -2844,7 +2846,7 @@ Metrics/PerceivedComplexity:
|
|
|
2844
2846
|
Description: 'Checks that the perceived complexity of methods is not higher than the configured maximum.'
|
|
2845
2847
|
Enabled: true
|
|
2846
2848
|
VersionAdded: '0.25'
|
|
2847
|
-
VersionChanged: '
|
|
2849
|
+
VersionChanged: '1.88'
|
|
2848
2850
|
AllowedMethods: []
|
|
2849
2851
|
AllowedPatterns: []
|
|
2850
2852
|
Max: 8
|
|
@@ -4658,7 +4660,9 @@ Style/MagicCommentFormat:
|
|
|
4658
4660
|
Style/MapCompactWithConditionalBlock:
|
|
4659
4661
|
Description: 'Prefer `select` or `reject` over `map { ... }.compact`.'
|
|
4660
4662
|
Enabled: pending
|
|
4663
|
+
SafeAutoCorrect: false
|
|
4661
4664
|
VersionAdded: '1.30'
|
|
4665
|
+
VersionChanged: '1.88'
|
|
4662
4666
|
|
|
4663
4667
|
Style/MapIntoArray:
|
|
4664
4668
|
Description: 'Checks for usages of `each` with `<<`, `push`, or `append` which can be replaced by `map`.'
|
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
|
|
|
@@ -163,7 +163,9 @@ module RuboCop
|
|
|
163
163
|
def gem_options(node)
|
|
164
164
|
return [] unless node.last_argument&.hash_type?
|
|
165
165
|
|
|
166
|
-
|
|
166
|
+
# Only literal keys carry an option name to check; a non-literal key
|
|
167
|
+
# (e.g. a variable or method call) has no `value` and must be skipped.
|
|
168
|
+
node.last_argument.keys.filter_map { |key| key.value if key.type?(:sym, :str) }
|
|
167
169
|
end
|
|
168
170
|
end
|
|
169
171
|
end
|
|
@@ -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)
|
|
@@ -27,7 +27,7 @@ module RuboCop
|
|
|
27
27
|
if block_node.arguments?
|
|
28
28
|
format(CORRECTION_WITH_ARGUMENTS,
|
|
29
29
|
collection: collection_node.source,
|
|
30
|
-
variables: argument_node.children.
|
|
30
|
+
variables: argument_node.children.map(&:source).join(', '))
|
|
31
31
|
else
|
|
32
32
|
format(CORRECTION_WITHOUT_ARGUMENTS, enumerable: collection_node.source)
|
|
33
33
|
end
|
|
@@ -86,7 +86,13 @@ module RuboCop
|
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
def lambda_arg_string
|
|
89
|
-
|
|
89
|
+
# Block-local (shadow) arguments are separated from regular arguments by a
|
|
90
|
+
# `;`; joining everything with `,` would turn them into extra parameters
|
|
91
|
+
# and change the lambda's arity.
|
|
92
|
+
regular, shadow = arguments.children.partition { |arg| !arg.shadowarg_type? }
|
|
93
|
+
arg_string = regular.map(&:source).join(', ')
|
|
94
|
+
arg_string += "; #{shadow.map(&:source).join(', ')}" unless shadow.empty?
|
|
95
|
+
arg_string
|
|
90
96
|
end
|
|
91
97
|
|
|
92
98
|
def needs_separating_space?
|
|
@@ -18,7 +18,14 @@ module RuboCop
|
|
|
18
18
|
current_range = declaration_with_comment(node)
|
|
19
19
|
previous_range = declaration_with_comment(previous_declaration)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
lambda do |corrector|
|
|
22
|
+
if current_range.source.end_with?("\n")
|
|
23
|
+
corrector.swap(current_range, previous_range)
|
|
24
|
+
else
|
|
25
|
+
corrector.insert_before(previous_range, "#{current_range.source}\n")
|
|
26
|
+
corrector.remove(current_range)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
22
29
|
end
|
|
23
30
|
|
|
24
31
|
private
|
|
@@ -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)
|
|
@@ -143,7 +143,10 @@ module RuboCop
|
|
|
143
143
|
#{block_var}.metadata['rubygems_mfa_required'] = 'true'
|
|
144
144
|
RUBY
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
# Scope the search to the current spec block. Searching the whole file
|
|
147
|
+
# would, for a second `Gem::Specification.new` block, insert the directive
|
|
148
|
+
# into the first block, leaving this block uncorrected and looping forever.
|
|
149
|
+
if (last_assignment = metadata_assignment(node).to_a.last)
|
|
147
150
|
corrector.insert_after(last_assignment, "\n#{require_mfa_directive}")
|
|
148
151
|
else
|
|
149
152
|
corrector.insert_before(node.loc.end, "#{require_mfa_directive}\n")
|
|
@@ -281,15 +281,32 @@ module RuboCop
|
|
|
281
281
|
end
|
|
282
282
|
end
|
|
283
283
|
|
|
284
|
+
# rubocop:disable Metrics/AbcSize
|
|
284
285
|
def do_line_begins_inside_argument?(node, do_loc)
|
|
285
286
|
line_begin_pos = do_loc.begin_pos - do_loc.column
|
|
286
287
|
first_char_pos = line_begin_pos + (do_loc.source_line =~ /\S/)
|
|
288
|
+
return false unless inside_parentheses?(node, first_char_pos)
|
|
287
289
|
|
|
288
290
|
(node.send_node.arguments + node.arguments).any? do |argument|
|
|
289
291
|
argument.source_range.begin_pos <= first_char_pos &&
|
|
290
292
|
first_char_pos < argument.source_range.end_pos
|
|
291
293
|
end
|
|
292
294
|
end
|
|
295
|
+
# rubocop:enable Metrics/AbcSize
|
|
296
|
+
|
|
297
|
+
# The continuation line indentation is only an unmeaningful alignment target when
|
|
298
|
+
# it is dictated by an opening delimiter, i.e. the line begins inside `(` or `[`.
|
|
299
|
+
# For a bare argument list without parentheses the indentation is the author's
|
|
300
|
+
# deliberate alignment, so the anchor must not move to the method dispatch line.
|
|
301
|
+
def inside_parentheses?(node, pos)
|
|
302
|
+
preceding_tokens = processed_source.tokens.select do |token|
|
|
303
|
+
token.begin_pos >= node.source_range.begin_pos && token.begin_pos < pos
|
|
304
|
+
end
|
|
305
|
+
opens = preceding_tokens.count { |token| token.left_parens? || token.left_bracket? }
|
|
306
|
+
closes = preceding_tokens.count { |token| token.right_parens? || token.right_bracket? }
|
|
307
|
+
|
|
308
|
+
opens > closes
|
|
309
|
+
end
|
|
293
310
|
end
|
|
294
311
|
end
|
|
295
312
|
end
|
|
@@ -278,10 +278,14 @@ module RuboCop
|
|
|
278
278
|
|
|
279
279
|
return [] unless class_def
|
|
280
280
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
# Only a multi-statement body (`begin`/`kwbegin`) wraps several elements; any
|
|
282
|
+
# single statement (`def`, `send`, `csend`, `if`, ...) is itself the sole element.
|
|
283
|
+
# Exploding such a node into its children would yield non-node values (e.g. a
|
|
284
|
+
# method-name `Symbol` from a `csend`) and crash later checks.
|
|
285
|
+
if class_def.type?(:begin, :kwbegin)
|
|
284
286
|
class_def.children.compact
|
|
287
|
+
else
|
|
288
|
+
[class_def]
|
|
285
289
|
end
|
|
286
290
|
end
|
|
287
291
|
|
|
@@ -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)
|
|
@@ -44,16 +44,26 @@ module RuboCop
|
|
|
44
44
|
message = message(condition)
|
|
45
45
|
|
|
46
46
|
add_offense(condition, message: message) do |corrector|
|
|
47
|
-
range = range_by_whole_lines(condition.source_range, include_final_newline: true)
|
|
48
|
-
|
|
49
47
|
corrector.insert_after(condition.parent.loc.keyword, " #{condition.source}")
|
|
50
|
-
corrector.remove(
|
|
48
|
+
corrector.remove(removal_range(node, condition))
|
|
51
49
|
end
|
|
52
50
|
end
|
|
53
51
|
|
|
54
52
|
def message(condition)
|
|
55
53
|
format(MSG, keyword: condition.parent.keyword)
|
|
56
54
|
end
|
|
55
|
+
|
|
56
|
+
# When a body statement shares the condition's line (e.g. `while\n cond; body\nend`),
|
|
57
|
+
# removing the whole line would delete the body too. In that case only remove the
|
|
58
|
+
# condition and its trailing separator, preserving the body statement.
|
|
59
|
+
def removal_range(node, condition)
|
|
60
|
+
body = node.body
|
|
61
|
+
if body && body.source_range.line == condition.source_range.last_line
|
|
62
|
+
range_between(condition.source_range.begin_pos, body.source_range.begin_pos)
|
|
63
|
+
else
|
|
64
|
+
range_by_whole_lines(condition.source_range, include_final_newline: true)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
57
67
|
end
|
|
58
68
|
end
|
|
59
69
|
end
|
|
@@ -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
|
|
@@ -95,8 +95,7 @@ module RuboCop
|
|
|
95
95
|
end
|
|
96
96
|
|
|
97
97
|
def autocorrect(corrector, node)
|
|
98
|
-
|
|
99
|
-
range = if previous_token && same_line?(node, previous_token)
|
|
98
|
+
range = if inline_comment?(node)
|
|
100
99
|
range_with_surrounding_space(node.source_range, newlines: false)
|
|
101
100
|
else
|
|
102
101
|
range_by_whole_lines(node.source_range, include_final_newline: true)
|
|
@@ -138,14 +137,13 @@ module RuboCop
|
|
|
138
137
|
cop_config['AllowMarginComment']
|
|
139
138
|
end
|
|
140
139
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
def
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
index.zero? ? nil : processed_source.tokens[index - 1]
|
|
140
|
+
# A comment is inline when code precedes it on the same line. Detecting this
|
|
141
|
+
# from the source (rather than the token stream) is required because, for a
|
|
142
|
+
# comment trailing a heredoc opener, the preceding token is the heredoc end on
|
|
143
|
+
# a later line, which would wrongly trigger whole-line removal of the opener.
|
|
144
|
+
def inline_comment?(comment)
|
|
145
|
+
preceding_source = processed_source.lines[comment.loc.line - 1][0...comment.loc.column]
|
|
146
|
+
!preceding_source.strip.empty?
|
|
149
147
|
end
|
|
150
148
|
end
|
|
151
149
|
end
|
|
@@ -274,7 +274,20 @@ module RuboCop
|
|
|
274
274
|
end
|
|
275
275
|
|
|
276
276
|
def end_loc(node)
|
|
277
|
-
node.source_range.end
|
|
277
|
+
end_location = node.source_range.end
|
|
278
|
+
trailing_heredoc_end(node, end_location) || end_location
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
# For an endless method whose body is a heredoc (e.g. `def a = <<~TEXT`), the
|
|
282
|
+
# node's source range ends at the heredoc opening line, before the heredoc body.
|
|
283
|
+
# Use the heredoc's closing delimiter so the def's real end is located after the
|
|
284
|
+
# heredoc and blank-line insertion does not land inside the heredoc body.
|
|
285
|
+
def trailing_heredoc_end(node, end_location)
|
|
286
|
+
heredocs = node.each_descendant(:any_str).select(&:heredoc?)
|
|
287
|
+
return if heredocs.empty?
|
|
288
|
+
|
|
289
|
+
heredoc_end = heredocs.map { |heredoc| heredoc.loc.heredoc_end }.max_by(&:end_pos)
|
|
290
|
+
heredoc_end if heredoc_end.end_pos > end_location.end_pos
|
|
278
291
|
end
|
|
279
292
|
|
|
280
293
|
def autocorrect_remove_lines(corrector, newline_pos, count)
|
|
@@ -28,10 +28,10 @@ module RuboCop
|
|
|
28
28
|
# # bad
|
|
29
29
|
# hash = {
|
|
30
30
|
# key: :value
|
|
31
|
-
#
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
#
|
|
31
|
+
# }
|
|
32
|
+
# in_a_method_call({
|
|
33
|
+
# foo: :bar
|
|
34
|
+
# })
|
|
35
35
|
# takes_multi_pairs_hash(x: {
|
|
36
36
|
# a: 1,
|
|
37
37
|
# b: 2
|
|
@@ -42,13 +42,12 @@ module RuboCop
|
|
|
42
42
|
# })
|
|
43
43
|
#
|
|
44
44
|
# # good
|
|
45
|
-
# special_inside_parentheses
|
|
46
45
|
# hash = {
|
|
47
46
|
# key: :value
|
|
48
47
|
# }
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
48
|
+
# in_a_method_call({
|
|
49
|
+
# foo: :bar
|
|
50
|
+
# })
|
|
52
51
|
# takes_multi_pairs_hash(x: {
|
|
53
52
|
# a: 1,
|
|
54
53
|
# b: 2
|
|
@@ -67,17 +66,17 @@ module RuboCop
|
|
|
67
66
|
# # bad
|
|
68
67
|
# hash = {
|
|
69
68
|
# key: :value
|
|
70
|
-
#
|
|
71
|
-
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
69
|
+
# }
|
|
70
|
+
# in_a_method_call({
|
|
71
|
+
# foo: :bar
|
|
72
|
+
# })
|
|
74
73
|
#
|
|
75
74
|
# # good
|
|
76
75
|
# hash = {
|
|
77
76
|
# key: :value
|
|
78
77
|
# }
|
|
79
|
-
#
|
|
80
|
-
#
|
|
78
|
+
# in_a_method_call({
|
|
79
|
+
# foo: :bar
|
|
81
80
|
# })
|
|
82
81
|
#
|
|
83
82
|
#
|
|
@@ -25,6 +25,17 @@ module RuboCop
|
|
|
25
25
|
# end
|
|
26
26
|
# end
|
|
27
27
|
#
|
|
28
|
+
# @example
|
|
29
|
+
# # bad
|
|
30
|
+
# value = (
|
|
31
|
+
# foo - bar
|
|
32
|
+
# )
|
|
33
|
+
#
|
|
34
|
+
# # good
|
|
35
|
+
# value = (
|
|
36
|
+
# foo - bar
|
|
37
|
+
# )
|
|
38
|
+
#
|
|
28
39
|
# @example AllowedPatterns: ['^\s*module']
|
|
29
40
|
# # bad
|
|
30
41
|
# module A
|
|
@@ -95,6 +106,16 @@ module RuboCop
|
|
|
95
106
|
check_indentation(node.loc.end, node.children.first)
|
|
96
107
|
end
|
|
97
108
|
|
|
109
|
+
def on_begin(node)
|
|
110
|
+
# Only a parenthesized grouping expression (e.g. `(\n foo\n)`) has
|
|
111
|
+
# explicit delimiters. Indent the body one step from the line
|
|
112
|
+
# the opening parenthesis is on, but only when the closing parenthesis is
|
|
113
|
+
# first on its line (a body on the opening line is skipped downstream).
|
|
114
|
+
return unless parentheses?(node) && begins_its_line?(node.loc.end)
|
|
115
|
+
|
|
116
|
+
check_indentation(opening_line_start(node.loc.begin), node.children.first)
|
|
117
|
+
end
|
|
118
|
+
|
|
98
119
|
def on_block(node)
|
|
99
120
|
end_loc = node.loc.end
|
|
100
121
|
|
|
@@ -188,6 +209,13 @@ module RuboCop
|
|
|
188
209
|
AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
|
|
189
210
|
end
|
|
190
211
|
|
|
212
|
+
# Returns a range at the first non-space column of the line the opening parenthesis is on,
|
|
213
|
+
# used as the base to indent the body from.
|
|
214
|
+
def opening_line_start(begin_loc)
|
|
215
|
+
column = begin_loc.source_line =~ /\S/
|
|
216
|
+
source_range(processed_source.buffer, begin_loc.line, column)
|
|
217
|
+
end
|
|
218
|
+
|
|
191
219
|
def check_members(base, members)
|
|
192
220
|
check_indentation(base, select_check_member(members.first))
|
|
193
221
|
|
|
@@ -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)
|
|
@@ -204,10 +204,14 @@ module RuboCop
|
|
|
204
204
|
|
|
205
205
|
def autocorrect(corrector, range, right_operand)
|
|
206
206
|
range_source = range.source
|
|
207
|
+
# Match the operator exactly, not by substring, so compound assignments
|
|
208
|
+
# like `**=` and `/=` are not mistaken for `**` and `/` (which would drop
|
|
209
|
+
# the `=` and silently change the program's behavior).
|
|
210
|
+
operator = range_source.strip
|
|
207
211
|
|
|
208
|
-
if
|
|
212
|
+
if operator == '**' && !space_around_exponent_operator?
|
|
209
213
|
corrector.replace(range, '**')
|
|
210
|
-
elsif
|
|
214
|
+
elsif operator == '/' && !space_around_slash_operator?(right_operand)
|
|
211
215
|
corrector.replace(range, '/')
|
|
212
216
|
elsif range_source.end_with?("\n")
|
|
213
217
|
corrector.replace(range, " #{range_source.strip}\n")
|
|
@@ -78,13 +78,25 @@ module RuboCop
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
def allowed_construct?(asgn_node)
|
|
81
|
-
asgn_node.begin_type?
|
|
81
|
+
return true if asgn_node.begin_type?
|
|
82
|
+
|
|
83
|
+
conditional_assignment?(asgn_node) || discarded_assignment?(asgn_node)
|
|
82
84
|
end
|
|
83
85
|
|
|
84
86
|
def conditional_assignment?(asgn_node)
|
|
85
87
|
!asgn_node.loc.operator
|
|
86
88
|
end
|
|
87
89
|
|
|
90
|
+
# An assignment that is a statement of a multi-statement `begin`
|
|
91
|
+
# (e.g. `(foo = bar; baz)`) has its value discarded, so it is not used
|
|
92
|
+
# as the condition. Wrapping it in parentheses would only conflict with
|
|
93
|
+
# `Style/RedundantParentheses`, so it is left alone.
|
|
94
|
+
def discarded_assignment?(asgn_node)
|
|
95
|
+
parent = asgn_node.parent
|
|
96
|
+
|
|
97
|
+
parent&.begin_type? && parent.children.size > 1
|
|
98
|
+
end
|
|
99
|
+
|
|
88
100
|
def skip_children?(asgn_node)
|
|
89
101
|
(asgn_node.call_type? && !asgn_node.assignment_method?) ||
|
|
90
102
|
empty_condition?(asgn_node) ||
|