rubocop 1.88.1 → 1.89.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/config/default.yml +69 -11
- data/lib/rubocop/comment_config.rb +35 -19
- data/lib/rubocop/config.rb +53 -0
- data/lib/rubocop/config_validator.rb +38 -1
- data/lib/rubocop/cop/badge.rb +8 -0
- data/lib/rubocop/cop/base.rb +19 -12
- data/lib/rubocop/cop/bundler.rb +19 -0
- data/lib/rubocop/cop/commissioner.rb +13 -11
- data/lib/rubocop/cop/corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
- data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +12 -2
- data/lib/rubocop/cop/gemspec.rb +22 -0
- data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
- data/lib/rubocop/cop/generator.rb +4 -0
- data/lib/rubocop/cop/layout/class_structure.rb +8 -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/first_array_element_indentation.rb +1 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
- data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
- data/lib/rubocop/cop/layout/line_length.rb +95 -17
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +18 -3
- data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +33 -34
- data/lib/rubocop/cop/layout.rb +114 -0
- data/lib/rubocop/cop/lazy_loader.rb +41 -0
- data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
- data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +1 -10
- data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
- data/lib/rubocop/cop/lint/debugger.rb +13 -1
- data/lib/rubocop/cop/lint/deprecated_reference.rb +185 -0
- data/lib/rubocop/cop/lint/duplicate_methods.rb +202 -22
- data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
- data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
- data/lib/rubocop/cop/lint/missing_super.rb +31 -1
- data/lib/rubocop/cop/lint/name_typo.rb +223 -0
- data/lib/rubocop/cop/lint/numeric_operation_with_constant_result.rb +7 -0
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +0 -4
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +7 -0
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +37 -1
- 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/unused_private_method.rb +157 -0
- data/lib/rubocop/cop/lint/useless_numeric_operation.rb +10 -10
- data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
- data/lib/rubocop/cop/lint/void.rb +19 -8
- data/lib/rubocop/cop/lint.rb +176 -0
- data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
- data/lib/rubocop/cop/metrics.rb +31 -0
- data/lib/rubocop/cop/migration/department_name.rb +0 -4
- data/lib/rubocop/cop/migration.rb +13 -0
- 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/check_line_breakable.rb +23 -5
- data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
- data/lib/rubocop/cop/mixin/forbidden_pattern.rb +5 -1
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
- data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
- data/lib/rubocop/cop/mixin/statement_modifier.rb +43 -7
- data/lib/rubocop/cop/mixin.rb +78 -77
- data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
- data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
- data/lib/rubocop/cop/naming/variable_number.rb +3 -1
- data/lib/rubocop/cop/naming.rb +31 -0
- data/lib/rubocop/cop/offense.rb +10 -6
- data/lib/rubocop/cop/registry.rb +153 -43
- data/lib/rubocop/cop/security.rb +19 -0
- data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
- data/lib/rubocop/cop/style/array_intersect.rb +24 -5
- data/lib/rubocop/cop/style/class_and_module_children.rb +78 -18
- data/lib/rubocop/cop/style/collection_compact.rb +1 -1
- data/lib/rubocop/cop/style/conditional_assignment.rb +22 -0
- data/lib/rubocop/cop/style/copyright.rb +6 -1
- data/lib/rubocop/cop/style/def_with_parentheses.rb +6 -2
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +41 -3
- data/lib/rubocop/cop/style/documentation.rb +47 -0
- data/lib/rubocop/cop/style/documentation_method.rb +5 -1
- data/lib/rubocop/cop/style/endless_method.rb +0 -10
- 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 +11 -52
- data/lib/rubocop/cop/style/invertible_unless_condition.rb +25 -5
- data/lib/rubocop/cop/style/lambda.rb +4 -0
- data/lib/rubocop/cop/style/lambda_call.rb +11 -0
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -3
- 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 +40 -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 +7 -1
- data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
- data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
- data/lib/rubocop/cop/style/negative_array_index.rb +3 -1
- data/lib/rubocop/cop/style/redundant_assignment.rb +11 -0
- data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
- data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +13 -1
- data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
- data/lib/rubocop/cop/style/redundant_format.rb +12 -3
- data/lib/rubocop/cop/style/redundant_heredoc_delimiter_quotes.rb +6 -1
- data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
- data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -63
- data/lib/rubocop/cop/style/safe_navigation.rb +10 -0
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +20 -3
- data/lib/rubocop/cop/style/static_class.rb +21 -0
- data/lib/rubocop/cop/style/struct_inheritance.rb +3 -1
- data/lib/rubocop/cop/style/trivial_accessors.rb +1 -1
- data/lib/rubocop/cop/style.rb +310 -0
- data/lib/rubocop/cop/team.rb +66 -7
- data/lib/rubocop/cop/variable_force/variable.rb +18 -6
- data/lib/rubocop/cops_documentation_generator.rb +1 -1
- data/lib/rubocop/directive_comment.rb +2 -0
- data/lib/rubocop/feature_loader.rb +0 -6
- data/lib/rubocop/lsp/routes.rb +41 -3
- data/lib/rubocop/lsp/runtime.rb +4 -0
- data/lib/rubocop/lsp/server.rb +4 -0
- data/lib/rubocop/lsp/stdin_runner.rb +20 -0
- data/lib/rubocop/pending_cops_reporter.rb +1 -6
- data/lib/rubocop/project_index_loader.rb +16 -0
- data/lib/rubocop/result_cache.rb +6 -4
- data/lib/rubocop/rspec/shared_contexts.rb +14 -2
- data/lib/rubocop/runner.rb +95 -30
- data/lib/rubocop/server/cache.rb +10 -5
- data/lib/rubocop/target_ruby.rb +1 -1
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +17 -626
- metadata +18 -3
|
@@ -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)
|
|
@@ -405,7 +410,7 @@ module RuboCop
|
|
|
405
410
|
|
|
406
411
|
def find_continuation_node(node)
|
|
407
412
|
receiver = node.receiver
|
|
408
|
-
return receiver
|
|
413
|
+
return leftmost_call_on_same_line(receiver) if single_line_block_receiver?(receiver)
|
|
409
414
|
return unless receiver.call_type? && receiver.loc.dot
|
|
410
415
|
return receiver if receiver.receiver.begin_type? && node.block_node.single_line?
|
|
411
416
|
return unless receiver.loc.dot.line > receiver.receiver.last_line
|
|
@@ -417,9 +422,19 @@ module RuboCop
|
|
|
417
422
|
receiver.single_line? && receiver.any_block_type?
|
|
418
423
|
end
|
|
419
424
|
|
|
425
|
+
def leftmost_call_on_same_line(node)
|
|
426
|
+
current = node.any_block_type? ? node.send_node : node
|
|
427
|
+
while current.receiver&.call_type? &&
|
|
428
|
+
current.receiver.loc?(:dot) &&
|
|
429
|
+
current.receiver.loc.dot.line == current.loc.dot.line
|
|
430
|
+
current = current.receiver
|
|
431
|
+
end
|
|
432
|
+
current
|
|
433
|
+
end
|
|
434
|
+
|
|
420
435
|
def handle_descendant_block(node)
|
|
421
436
|
receiver = node.receiver
|
|
422
|
-
return receiver
|
|
437
|
+
return leftmost_call_on_same_line(receiver) if single_line_block_receiver?(receiver)
|
|
423
438
|
|
|
424
439
|
block_node = node.each_descendant(:any_block).first
|
|
425
440
|
return unless block_node&.multiline?
|
|
@@ -49,6 +49,7 @@ module RuboCop
|
|
|
49
49
|
class RedundantLineBreak < Base
|
|
50
50
|
include CheckAssignment
|
|
51
51
|
include CheckSingleLineSuitability
|
|
52
|
+
include ReparsedEquivalence
|
|
52
53
|
extend AutoCorrector
|
|
53
54
|
|
|
54
55
|
MSG = 'Redundant line break detected.'
|
|
@@ -82,12 +83,88 @@ module RuboCop
|
|
|
82
83
|
end
|
|
83
84
|
|
|
84
85
|
def register_offense(node)
|
|
86
|
+
# The exact single-line correction is verified to parse equivalently
|
|
87
|
+
# before the offense is registered, so a join that would change how
|
|
88
|
+
# the code parses is never reported or offered.
|
|
89
|
+
return if verified_by_reparse([node], oversized: :verify).empty?
|
|
90
|
+
|
|
85
91
|
add_offense(node) do |corrector|
|
|
86
92
|
corrector.replace(node, to_single_line(node.source).strip)
|
|
87
93
|
end
|
|
88
94
|
ignore_node(node)
|
|
89
95
|
end
|
|
90
96
|
|
|
97
|
+
def apply_reparse_correction(corrector, node)
|
|
98
|
+
corrector.replace(node, to_single_line(node.source).strip)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Joining lines shifts the value of any later `__LINE__`, exactly as
|
|
102
|
+
# any other line-removing correction does; neutralize it on both sides
|
|
103
|
+
# with an identifier, which stays valid in every position the keyword
|
|
104
|
+
# can appear in.
|
|
105
|
+
def preprocess_reparsed_source(source)
|
|
106
|
+
source.gsub(/\b__LINE__\b/, '__LINE0__')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def normalize_reparsed_ast(node)
|
|
110
|
+
fold_string_concatenation(node)
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Joining lines merges split string literals (`"a" \<newline> "b"` into
|
|
114
|
+
# `"ab"`) and turns mixed-quote pairs into `+` concatenation, which
|
|
115
|
+
# changes the tree but not the resulting string. Normalize all string
|
|
116
|
+
# concatenation to a canonical form before comparing: nested
|
|
117
|
+
# concatenations are flattened and adjacent literal parts merged.
|
|
118
|
+
def fold_string_concatenation(node)
|
|
119
|
+
return node unless node.is_a?(::Parser::AST::Node)
|
|
120
|
+
|
|
121
|
+
children = node.children.map { |child| fold_string_concatenation(child) }
|
|
122
|
+
node = node.updated(nil, children)
|
|
123
|
+
|
|
124
|
+
parts = string_concatenation_parts(node)
|
|
125
|
+
return node unless parts
|
|
126
|
+
|
|
127
|
+
merged = merge_string_parts(parts)
|
|
128
|
+
if merged.one? && merged.first.str_type?
|
|
129
|
+
node.updated(:str, merged.first.children)
|
|
130
|
+
else
|
|
131
|
+
node.updated(:dstr, merged)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def string_concatenation_parts(node)
|
|
136
|
+
case node.type
|
|
137
|
+
when :dstr
|
|
138
|
+
node.children
|
|
139
|
+
when :send
|
|
140
|
+
parts = [node.receiver, *node.arguments]
|
|
141
|
+
parts if node.method?(:+) && parts.all? { |part| stringish?(part) }
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def merge_string_parts(parts)
|
|
146
|
+
flattened = parts.flat_map do |part|
|
|
147
|
+
part.is_a?(::Parser::AST::Node) && part.dstr_type? ? part.children : [part]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
flattened.chunk_while { |left, right| plain_string?(left) && plain_string?(right) }
|
|
151
|
+
.map { |chunk| merge_plain_strings(chunk) }
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def merge_plain_strings(chunk)
|
|
155
|
+
return chunk.first if chunk.one?
|
|
156
|
+
|
|
157
|
+
chunk.first.updated(:str, [chunk.map { |part| part.children.first }.join])
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def stringish?(node)
|
|
161
|
+
node.is_a?(::Parser::AST::Node) && %i[str dstr].include?(node.type)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def plain_string?(node)
|
|
165
|
+
node.is_a?(::Parser::AST::Node) && node.str_type? && node.children.one?
|
|
166
|
+
end
|
|
167
|
+
|
|
91
168
|
def offense?(node)
|
|
92
169
|
return false unless node.multiline? && suitable_as_single_line?(node)
|
|
93
170
|
return require_backslash?(node) if node.operator_keyword?
|
|
@@ -94,10 +94,10 @@ module RuboCop
|
|
|
94
94
|
return empty_offenses(node, left, right, EMPTY_MSG)
|
|
95
95
|
end
|
|
96
96
|
|
|
97
|
-
start_ok = next_to_newline?(
|
|
97
|
+
start_ok = next_to_newline?(tokens, left)
|
|
98
98
|
end_ok = node.single_line? ? false : end_has_own_line?(right)
|
|
99
99
|
|
|
100
|
-
issue_offenses(node, left, right, start_ok, end_ok)
|
|
100
|
+
issue_offenses(node, tokens, left, right, start_ok, end_ok)
|
|
101
101
|
end
|
|
102
102
|
alias on_array_pattern on_array
|
|
103
103
|
|
|
@@ -117,7 +117,7 @@ module RuboCop
|
|
|
117
117
|
elsif style == :space
|
|
118
118
|
SpaceCorrector.add_space(processed_source, corrector, left, right)
|
|
119
119
|
else
|
|
120
|
-
compact_corrections(corrector,
|
|
120
|
+
compact_corrections(corrector, tokens, left, right)
|
|
121
121
|
end
|
|
122
122
|
end
|
|
123
123
|
|
|
@@ -134,8 +134,8 @@ module RuboCop
|
|
|
134
134
|
cop_config['EnforcedStyleForEmptyBrackets']
|
|
135
135
|
end
|
|
136
136
|
|
|
137
|
-
def next_to_newline?(
|
|
138
|
-
|
|
137
|
+
def next_to_newline?(tokens, token)
|
|
138
|
+
tokens[index_for(tokens, token) + 1].line != token.line
|
|
139
139
|
end
|
|
140
140
|
|
|
141
141
|
def end_has_own_line?(token)
|
|
@@ -145,68 +145,67 @@ module RuboCop
|
|
|
145
145
|
!/\S/.match?(processed_source.lines[line][0..col])
|
|
146
146
|
end
|
|
147
147
|
|
|
148
|
-
def index_for(
|
|
149
|
-
|
|
148
|
+
def index_for(tokens, token)
|
|
149
|
+
tokens.index(token)
|
|
150
150
|
end
|
|
151
151
|
|
|
152
152
|
def line_and_column_for(token)
|
|
153
153
|
[token.line - 1, token.column - 1]
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
-
|
|
156
|
+
# rubocop:disable Metrics/ParameterLists
|
|
157
|
+
def issue_offenses(node, tokens, left, right, start_ok, end_ok)
|
|
157
158
|
case style
|
|
158
159
|
when :no_space
|
|
159
|
-
start_ok = next_to_comment?(
|
|
160
|
+
start_ok = next_to_comment?(tokens, left)
|
|
160
161
|
no_space_offenses(node, left, right, MSG, start_ok: start_ok, end_ok: end_ok)
|
|
161
162
|
when :space
|
|
162
163
|
space_offenses(node, left, right, MSG, start_ok: start_ok, end_ok: end_ok)
|
|
163
164
|
else
|
|
164
|
-
compact_offenses(node, left, right, start_ok, end_ok)
|
|
165
|
+
compact_offenses(node, tokens, left, right, start_ok, end_ok)
|
|
165
166
|
end
|
|
166
167
|
end
|
|
168
|
+
# rubocop:enable Metrics/ParameterLists
|
|
167
169
|
|
|
168
|
-
def next_to_comment?(
|
|
169
|
-
|
|
170
|
+
def next_to_comment?(tokens, token)
|
|
171
|
+
tokens[index_for(tokens, token) + 1].comment?
|
|
170
172
|
end
|
|
171
173
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
+
# rubocop:disable Metrics/ParameterLists
|
|
175
|
+
def compact_offenses(node, tokens, left, right, start_ok, end_ok)
|
|
176
|
+
if qualifies_for_compact?(tokens, left, side: :left)
|
|
174
177
|
compact_offense(node, left, side: :left)
|
|
175
|
-
elsif !multi_dimensional_array?(
|
|
178
|
+
elsif !multi_dimensional_array?(tokens, left, side: :left)
|
|
176
179
|
space_offenses(node, left, nil, MSG, start_ok: start_ok, end_ok: true)
|
|
177
180
|
end
|
|
178
|
-
|
|
181
|
+
|
|
182
|
+
if qualifies_for_compact?(tokens, right)
|
|
179
183
|
compact_offense(node, right)
|
|
180
|
-
elsif !multi_dimensional_array?(
|
|
184
|
+
elsif !multi_dimensional_array?(tokens, right)
|
|
181
185
|
space_offenses(node, nil, right, MSG, start_ok: true, end_ok: end_ok)
|
|
182
186
|
end
|
|
183
187
|
end
|
|
188
|
+
# rubocop:enable Metrics/ParameterLists
|
|
184
189
|
|
|
185
|
-
def qualifies_for_compact?(
|
|
190
|
+
def qualifies_for_compact?(tokens, token, side: :right)
|
|
186
191
|
if side == :right
|
|
187
|
-
multi_dimensional_array?(
|
|
192
|
+
multi_dimensional_array?(tokens, token) && token.space_before?
|
|
188
193
|
else
|
|
189
|
-
multi_dimensional_array?(
|
|
194
|
+
multi_dimensional_array?(tokens, token, side: :left) && token.space_after?
|
|
190
195
|
end
|
|
191
196
|
end
|
|
192
197
|
|
|
193
|
-
def multi_dimensional_array?(
|
|
198
|
+
def multi_dimensional_array?(tokens, token, side: :right)
|
|
194
199
|
offset = side == :right ? -1 : +1
|
|
195
|
-
i = index_for(
|
|
196
|
-
i += offset while
|
|
200
|
+
i = index_for(tokens, token) + offset
|
|
201
|
+
i += offset while tokens[i].new_line?
|
|
197
202
|
if side == :right
|
|
198
|
-
|
|
203
|
+
tokens[i].right_bracket?
|
|
199
204
|
else
|
|
200
|
-
|
|
205
|
+
tokens[i].left_bracket?
|
|
201
206
|
end
|
|
202
207
|
end
|
|
203
208
|
|
|
204
|
-
def next_to_bracket?(token, side: :right)
|
|
205
|
-
line_index, col = line_and_column_for(token)
|
|
206
|
-
line = processed_source.lines[line_index]
|
|
207
|
-
side == :right ? line[col] == ']' : line[col + 2] == '['
|
|
208
|
-
end
|
|
209
|
-
|
|
210
209
|
def compact_offense(node, token, side: :right)
|
|
211
210
|
if side == :right
|
|
212
211
|
space_offense(node, token, :left, MSG, NO_SPACE_COMMAND)
|
|
@@ -215,14 +214,14 @@ module RuboCop
|
|
|
215
214
|
end
|
|
216
215
|
end
|
|
217
216
|
|
|
218
|
-
def compact_corrections(corrector,
|
|
219
|
-
if multi_dimensional_array?(
|
|
217
|
+
def compact_corrections(corrector, tokens, left, right)
|
|
218
|
+
if multi_dimensional_array?(tokens, left, side: :left)
|
|
220
219
|
compact(corrector, left, :right)
|
|
221
220
|
elsif !left.space_after?
|
|
222
221
|
corrector.insert_after(left.pos, ' ')
|
|
223
222
|
end
|
|
224
223
|
|
|
225
|
-
if multi_dimensional_array?(
|
|
224
|
+
if multi_dimensional_array?(tokens, right)
|
|
226
225
|
compact(corrector, right, :left)
|
|
227
226
|
elsif !right.space_before?
|
|
228
227
|
corrector.insert_before(right.pos, ' ')
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Cops for the `Layout` department. The department's cops are registered for lazy loading
|
|
6
|
+
# and their files are loaded on demand.
|
|
7
|
+
module Layout
|
|
8
|
+
extend LazyLoader
|
|
9
|
+
|
|
10
|
+
register_cop :AccessModifierIndentation, "#{__dir__}/layout/access_modifier_indentation"
|
|
11
|
+
register_cop :ArgumentAlignment, "#{__dir__}/layout/argument_alignment"
|
|
12
|
+
register_cop :ArrayAlignment, "#{__dir__}/layout/array_alignment"
|
|
13
|
+
register_cop :AssignmentIndentation, "#{__dir__}/layout/assignment_indentation"
|
|
14
|
+
register_cop :BeginEndAlignment, "#{__dir__}/layout/begin_end_alignment"
|
|
15
|
+
register_cop :BlockAlignment, "#{__dir__}/layout/block_alignment"
|
|
16
|
+
register_cop :BlockEndNewline, "#{__dir__}/layout/block_end_newline"
|
|
17
|
+
register_cop :CaseIndentation, "#{__dir__}/layout/case_indentation"
|
|
18
|
+
register_cop :ClassStructure, "#{__dir__}/layout/class_structure"
|
|
19
|
+
register_cop :ClosingHeredocIndentation, "#{__dir__}/layout/closing_heredoc_indentation"
|
|
20
|
+
register_cop :ClosingParenthesisIndentation, "#{__dir__}/layout/closing_parenthesis_indentation"
|
|
21
|
+
register_cop :CommentIndentation, "#{__dir__}/layout/comment_indentation"
|
|
22
|
+
register_cop :ConditionPosition, "#{__dir__}/layout/condition_position"
|
|
23
|
+
register_cop :DefEndAlignment, "#{__dir__}/layout/def_end_alignment"
|
|
24
|
+
register_cop :DotPosition, "#{__dir__}/layout/dot_position"
|
|
25
|
+
register_cop :ElseAlignment, "#{__dir__}/layout/else_alignment"
|
|
26
|
+
register_cop :EmptyComment, "#{__dir__}/layout/empty_comment"
|
|
27
|
+
register_cop :EmptyLineAfterGuardClause, "#{__dir__}/layout/empty_line_after_guard_clause"
|
|
28
|
+
register_cop :EmptyLineAfterMagicComment, "#{__dir__}/layout/empty_line_after_magic_comment"
|
|
29
|
+
register_cop :EmptyLineAfterMultilineCondition, "#{__dir__}/layout/empty_line_after_multiline_condition"
|
|
30
|
+
register_cop :EmptyLineBetweenDefs, "#{__dir__}/layout/empty_line_between_defs"
|
|
31
|
+
register_cop :EmptyLinesAfterModuleInclusion, "#{__dir__}/layout/empty_lines_after_module_inclusion"
|
|
32
|
+
register_cop :EmptyLinesAroundAccessModifier, "#{__dir__}/layout/empty_lines_around_access_modifier"
|
|
33
|
+
register_cop :EmptyLinesAroundArguments, "#{__dir__}/layout/empty_lines_around_arguments"
|
|
34
|
+
register_cop :EmptyLinesAroundAttributeAccessor, "#{__dir__}/layout/empty_lines_around_attribute_accessor"
|
|
35
|
+
register_cop :EmptyLinesAroundBeginBody, "#{__dir__}/layout/empty_lines_around_begin_body"
|
|
36
|
+
register_cop :EmptyLinesAroundBlockBody, "#{__dir__}/layout/empty_lines_around_block_body"
|
|
37
|
+
register_cop :EmptyLinesAroundClassBody, "#{__dir__}/layout/empty_lines_around_class_body"
|
|
38
|
+
register_cop :EmptyLinesAroundExceptionHandlingKeywords, "#{__dir__}/layout/empty_lines_around_exception_handling_keywords"
|
|
39
|
+
register_cop :EmptyLinesAroundMethodBody, "#{__dir__}/layout/empty_lines_around_method_body"
|
|
40
|
+
register_cop :EmptyLinesAroundModuleBody, "#{__dir__}/layout/empty_lines_around_module_body"
|
|
41
|
+
register_cop :EmptyLines, "#{__dir__}/layout/empty_lines"
|
|
42
|
+
register_cop :EndAlignment, "#{__dir__}/layout/end_alignment"
|
|
43
|
+
register_cop :EndOfLine, "#{__dir__}/layout/end_of_line"
|
|
44
|
+
register_cop :ExtraSpacing, "#{__dir__}/layout/extra_spacing"
|
|
45
|
+
register_cop :FirstArgumentIndentation, "#{__dir__}/layout/first_argument_indentation"
|
|
46
|
+
register_cop :FirstArrayElementIndentation, "#{__dir__}/layout/first_array_element_indentation"
|
|
47
|
+
register_cop :FirstArrayElementLineBreak, "#{__dir__}/layout/first_array_element_line_break"
|
|
48
|
+
register_cop :FirstHashElementIndentation, "#{__dir__}/layout/first_hash_element_indentation"
|
|
49
|
+
register_cop :FirstHashElementLineBreak, "#{__dir__}/layout/first_hash_element_line_break"
|
|
50
|
+
register_cop :FirstMethodArgumentLineBreak, "#{__dir__}/layout/first_method_argument_line_break"
|
|
51
|
+
register_cop :FirstMethodParameterLineBreak, "#{__dir__}/layout/first_method_parameter_line_break"
|
|
52
|
+
register_cop :FirstParameterIndentation, "#{__dir__}/layout/first_parameter_indentation"
|
|
53
|
+
register_cop :HashAlignment, "#{__dir__}/layout/hash_alignment"
|
|
54
|
+
register_cop :HeredocArgumentClosingParenthesis, "#{__dir__}/layout/heredoc_argument_closing_parenthesis"
|
|
55
|
+
register_cop :HeredocIndentation, "#{__dir__}/layout/heredoc_indentation"
|
|
56
|
+
register_cop :IndentationConsistency, "#{__dir__}/layout/indentation_consistency"
|
|
57
|
+
register_cop :IndentationStyle, "#{__dir__}/layout/indentation_style"
|
|
58
|
+
register_cop :IndentationWidth, "#{__dir__}/layout/indentation_width"
|
|
59
|
+
register_cop :InitialIndentation, "#{__dir__}/layout/initial_indentation"
|
|
60
|
+
register_cop :LeadingCommentSpace, "#{__dir__}/layout/leading_comment_space"
|
|
61
|
+
register_cop :LeadingEmptyLines, "#{__dir__}/layout/leading_empty_lines"
|
|
62
|
+
register_cop :LineContinuationLeadingSpace, "#{__dir__}/layout/line_continuation_leading_space"
|
|
63
|
+
register_cop :LineContinuationSpacing, "#{__dir__}/layout/line_continuation_spacing"
|
|
64
|
+
register_cop :LineEndStringConcatenationIndentation, "#{__dir__}/layout/line_end_string_concatenation_indentation"
|
|
65
|
+
register_cop :LineLength, "#{__dir__}/layout/line_length"
|
|
66
|
+
register_cop :MultilineArrayBraceLayout, "#{__dir__}/layout/multiline_array_brace_layout"
|
|
67
|
+
register_cop :MultilineArrayLineBreaks, "#{__dir__}/layout/multiline_array_line_breaks"
|
|
68
|
+
register_cop :MultilineAssignmentLayout, "#{__dir__}/layout/multiline_assignment_layout"
|
|
69
|
+
register_cop :MultilineBlockLayout, "#{__dir__}/layout/multiline_block_layout"
|
|
70
|
+
register_cop :MultilineHashBraceLayout, "#{__dir__}/layout/multiline_hash_brace_layout"
|
|
71
|
+
register_cop :MultilineHashKeyLineBreaks, "#{__dir__}/layout/multiline_hash_key_line_breaks"
|
|
72
|
+
register_cop :MultilineMethodArgumentLineBreaks, "#{__dir__}/layout/multiline_method_argument_line_breaks"
|
|
73
|
+
register_cop :MultilineMethodCallBraceLayout, "#{__dir__}/layout/multiline_method_call_brace_layout"
|
|
74
|
+
register_cop :MultilineMethodCallIndentation, "#{__dir__}/layout/multiline_method_call_indentation"
|
|
75
|
+
register_cop :MultilineMethodDefinitionBraceLayout, "#{__dir__}/layout/multiline_method_definition_brace_layout"
|
|
76
|
+
register_cop :MultilineMethodParameterLineBreaks, "#{__dir__}/layout/multiline_method_parameter_line_breaks"
|
|
77
|
+
register_cop :MultilineOperationIndentation, "#{__dir__}/layout/multiline_operation_indentation"
|
|
78
|
+
register_cop :ParameterAlignment, "#{__dir__}/layout/parameter_alignment"
|
|
79
|
+
register_cop :RedundantLineBreak, "#{__dir__}/layout/redundant_line_break"
|
|
80
|
+
register_cop :RescueEnsureAlignment, "#{__dir__}/layout/rescue_ensure_alignment"
|
|
81
|
+
register_cop :SingleLineBlockChain, "#{__dir__}/layout/single_line_block_chain"
|
|
82
|
+
register_cop :SpaceAfterColon, "#{__dir__}/layout/space_after_colon"
|
|
83
|
+
register_cop :SpaceAfterComma, "#{__dir__}/layout/space_after_comma"
|
|
84
|
+
register_cop :SpaceAfterMethodName, "#{__dir__}/layout/space_after_method_name"
|
|
85
|
+
register_cop :SpaceAfterNot, "#{__dir__}/layout/space_after_not"
|
|
86
|
+
register_cop :SpaceAfterSemicolon, "#{__dir__}/layout/space_after_semicolon"
|
|
87
|
+
register_cop :SpaceAroundBlockParameters, "#{__dir__}/layout/space_around_block_parameters"
|
|
88
|
+
register_cop :SpaceAroundEqualsInParameterDefault, "#{__dir__}/layout/space_around_equals_in_parameter_default"
|
|
89
|
+
register_cop :SpaceAroundKeyword, "#{__dir__}/layout/space_around_keyword"
|
|
90
|
+
register_cop :SpaceAroundMethodCallOperator, "#{__dir__}/layout/space_around_method_call_operator"
|
|
91
|
+
register_cop :SpaceAroundOperators, "#{__dir__}/layout/space_around_operators"
|
|
92
|
+
register_cop :SpaceBeforeBlockBraces, "#{__dir__}/layout/space_before_block_braces"
|
|
93
|
+
register_cop :SpaceBeforeBrackets, "#{__dir__}/layout/space_before_brackets"
|
|
94
|
+
register_cop :SpaceBeforeComma, "#{__dir__}/layout/space_before_comma"
|
|
95
|
+
register_cop :SpaceBeforeComment, "#{__dir__}/layout/space_before_comment"
|
|
96
|
+
register_cop :SpaceBeforeFirstArg, "#{__dir__}/layout/space_before_first_arg"
|
|
97
|
+
register_cop :SpaceBeforeSemicolon, "#{__dir__}/layout/space_before_semicolon"
|
|
98
|
+
register_cop :SpaceInLambdaLiteral, "#{__dir__}/layout/space_in_lambda_literal"
|
|
99
|
+
register_cop :SpaceInsideArrayPercentLiteral, "#{__dir__}/layout/space_inside_array_percent_literal"
|
|
100
|
+
register_cop :SpaceInsideArrayLiteralBrackets, "#{__dir__}/layout/space_inside_array_literal_brackets"
|
|
101
|
+
register_cop :SpaceInsideBlockBraces, "#{__dir__}/layout/space_inside_block_braces"
|
|
102
|
+
register_cop :SpaceInsideHashLiteralBraces, "#{__dir__}/layout/space_inside_hash_literal_braces"
|
|
103
|
+
register_cop :SpaceInsideParens, "#{__dir__}/layout/space_inside_parens"
|
|
104
|
+
register_cop :SpaceInsidePercentLiteralDelimiters, "#{__dir__}/layout/space_inside_percent_literal_delimiters"
|
|
105
|
+
register_cop :SpaceInsideRangeLiteral, "#{__dir__}/layout/space_inside_range_literal"
|
|
106
|
+
register_cop :SpaceInsideReferenceBrackets, "#{__dir__}/layout/space_inside_reference_brackets"
|
|
107
|
+
register_cop :SpaceInsideStringInterpolation, "#{__dir__}/layout/space_inside_string_interpolation"
|
|
108
|
+
register_cop :TrailingEmptyLines, "#{__dir__}/layout/trailing_empty_lines"
|
|
109
|
+
register_cop :TrailingWhitespace, "#{__dir__}/layout/trailing_whitespace"
|
|
110
|
+
|
|
111
|
+
autoload :EmptyLinesAroundBody, "#{__dir__}/mixin/empty_lines_around_body"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
# Extend a department module with this module and call `register_cop` to register
|
|
6
|
+
# the department's cops with the global registry without loading their files until
|
|
7
|
+
# a cop class itself is needed:
|
|
8
|
+
#
|
|
9
|
+
# module RuboCop
|
|
10
|
+
# module Cop
|
|
11
|
+
# # The Foo department.
|
|
12
|
+
# module Foo
|
|
13
|
+
# extend LazyLoader
|
|
14
|
+
#
|
|
15
|
+
# register_cop :BarBaz, "#{__dir__}/foo/bar_baz"
|
|
16
|
+
# end
|
|
17
|
+
# end
|
|
18
|
+
# end
|
|
19
|
+
module LazyLoader
|
|
20
|
+
# Registers a cop for lazy loading. The cop appears in the registry by name right away,
|
|
21
|
+
# but its file is loaded only when the cop class is needed, e.g. when the cop is enabled
|
|
22
|
+
# for an inspection run.
|
|
23
|
+
#
|
|
24
|
+
# The path must be absolute so that loading does not depend on `$LOAD_PATH` and cannot resolve
|
|
25
|
+
# to another RuboCop installation.
|
|
26
|
+
#
|
|
27
|
+
# @param constant_name [Symbol] name of the cop class, e.g. `:BarBaz`
|
|
28
|
+
# @param path [String] absolute path to the file defining the cop class,
|
|
29
|
+
# with or without the `.rb` extension
|
|
30
|
+
def register_cop(constant_name, path)
|
|
31
|
+
raise ArgumentError, "cop path must be absolute: #{path}" unless File.absolute_path?(path)
|
|
32
|
+
|
|
33
|
+
autoload constant_name, path
|
|
34
|
+
|
|
35
|
+
qualified_name = "#{name}::#{constant_name}"
|
|
36
|
+
|
|
37
|
+
Registry.global.lazy_load(Badge.for(qualified_name), qualified_name)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -19,25 +19,27 @@ module RuboCop
|
|
|
19
19
|
# x != y # or x = !y
|
|
20
20
|
#
|
|
21
21
|
class AmbiguousAssignment < Base
|
|
22
|
+
include CheckAssignment
|
|
22
23
|
include RangeHelp
|
|
23
24
|
|
|
24
25
|
MSG = 'Suspicious assignment detected. Did you mean `%<op>s`?'
|
|
25
26
|
|
|
26
|
-
SIMPLE_ASSIGNMENT_TYPES = %i[lvasgn ivasgn cvasgn gvasgn casgn].freeze
|
|
27
|
-
|
|
28
27
|
MISTAKES = { '=-' => '-=', '=+' => '+=', '=*' => '*=', '=!' => '!=' }.freeze
|
|
29
28
|
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
alias on_csend on_send
|
|
30
|
+
|
|
31
|
+
private
|
|
32
32
|
|
|
33
|
-
|
|
33
|
+
def check_assignment(node, rhs)
|
|
34
|
+
return unless rhs
|
|
35
|
+
return unless (operator = node.loc.operator)
|
|
36
|
+
|
|
37
|
+
range = range_between(operator.end_pos - 1, rhs.source_range.begin_pos + 1)
|
|
34
38
|
source = range.source
|
|
35
39
|
return unless MISTAKES.key?(source)
|
|
36
40
|
|
|
37
41
|
add_offense(range, message: format(MSG, op: MISTAKES[source]))
|
|
38
42
|
end
|
|
39
|
-
|
|
40
|
-
SIMPLE_ASSIGNMENT_TYPES.each { |asgn_type| alias_method :"on_#{asgn_type}", :on_asgn }
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
end
|
|
@@ -6,6 +6,11 @@ module RuboCop
|
|
|
6
6
|
# Checks for ambiguous block association with method
|
|
7
7
|
# when param passed without parentheses.
|
|
8
8
|
#
|
|
9
|
+
# This cop also detects `do...end` blocks that are likely intended for
|
|
10
|
+
# an enumerable method in the arguments but actually bind to the outer
|
|
11
|
+
# method call. For example, in `render json: data.map do |x| x end`,
|
|
12
|
+
# Ruby parses the `do...end` block as belonging to `render`, not `map`.
|
|
13
|
+
#
|
|
9
14
|
# This cop can customize allowed methods with `AllowedMethods`.
|
|
10
15
|
# By default, there are no allowed methods.
|
|
11
16
|
#
|
|
@@ -20,6 +25,18 @@ module RuboCop
|
|
|
20
25
|
# # or (different meaning)
|
|
21
26
|
# some_method(a) { |val| puts val }
|
|
22
27
|
#
|
|
28
|
+
# # bad
|
|
29
|
+
# render json: data.map do |item|
|
|
30
|
+
# item.to_h
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# # good
|
|
34
|
+
# render json: data.map { |item| item.to_h }
|
|
35
|
+
#
|
|
36
|
+
# # good
|
|
37
|
+
# mapped = data.map { |item| item.to_h }
|
|
38
|
+
# render json: mapped
|
|
39
|
+
#
|
|
23
40
|
# # good
|
|
24
41
|
# # Operator methods require no disambiguation
|
|
25
42
|
# foo == bar { |b| b.baz }
|
|
@@ -58,6 +75,19 @@ module RuboCop
|
|
|
58
75
|
MSG = 'Parenthesize the param `%<param>s` to make sure that the ' \
|
|
59
76
|
'block will be associated with the `%<method>s` method ' \
|
|
60
77
|
'call.'
|
|
78
|
+
MSG_DO_END_BLOCK = '`%<inner_method>s` is called without a block because the ' \
|
|
79
|
+
'`do` block binds to `%<outer_method>s`. ' \
|
|
80
|
+
'Use braces or extract to a variable.'
|
|
81
|
+
|
|
82
|
+
BLOCK_METHODS = %i[
|
|
83
|
+
map collect flat_map collect_concat
|
|
84
|
+
select filter find_all reject
|
|
85
|
+
find detect
|
|
86
|
+
each each_with_object each_with_index
|
|
87
|
+
reduce inject
|
|
88
|
+
sort_by min_by max_by
|
|
89
|
+
group_by filter_map
|
|
90
|
+
].to_set.freeze
|
|
61
91
|
|
|
62
92
|
def on_send(node)
|
|
63
93
|
return unless node.arguments?
|
|
@@ -74,8 +104,36 @@ module RuboCop
|
|
|
74
104
|
end
|
|
75
105
|
alias on_csend on_send
|
|
76
106
|
|
|
107
|
+
def on_block(node)
|
|
108
|
+
return if node.braces?
|
|
109
|
+
|
|
110
|
+
send_node = node.send_node
|
|
111
|
+
block_method_arg = find_ambiguous_block_method(node)
|
|
112
|
+
return unless block_method_arg
|
|
113
|
+
|
|
114
|
+
add_offense(block_method_arg,
|
|
115
|
+
message: format(MSG_DO_END_BLOCK,
|
|
116
|
+
inner_method: block_method_arg.method_name,
|
|
117
|
+
outer_method: send_node.method_name))
|
|
118
|
+
end
|
|
119
|
+
alias on_numblock on_block
|
|
120
|
+
alias on_itblock on_block
|
|
121
|
+
|
|
77
122
|
private
|
|
78
123
|
|
|
124
|
+
def find_ambiguous_block_method(block_node)
|
|
125
|
+
send_node = block_node.send_node
|
|
126
|
+
return unless send_node.send_type? && send_node.arguments?
|
|
127
|
+
return if send_node.parenthesized?
|
|
128
|
+
|
|
129
|
+
block_method_arg = find_block_method_arg(send_node)
|
|
130
|
+
return unless block_method_arg
|
|
131
|
+
return if allowed_method?(block_method_arg.method_name) ||
|
|
132
|
+
matches_allowed_pattern?(block_method_arg.source)
|
|
133
|
+
|
|
134
|
+
block_method_arg
|
|
135
|
+
end
|
|
136
|
+
|
|
79
137
|
def ambiguous_block_association?(send_node)
|
|
80
138
|
send_node.last_argument.any_block_type? && !send_node.last_argument.send_node.arguments?
|
|
81
139
|
end
|
|
@@ -92,6 +150,19 @@ module RuboCop
|
|
|
92
150
|
format(MSG, param: block_param.source, method: block_param.send_node.source)
|
|
93
151
|
end
|
|
94
152
|
|
|
153
|
+
# Only the call the `do` block could actually have attached to is a candidate:
|
|
154
|
+
# the one whose source ends where the arguments end. This skips calls chained
|
|
155
|
+
# into another call (`foo bar.map.to_a`) and calls buried inside a
|
|
156
|
+
# parenthesized subcall (`foo bar(baz.map)`), which were never block candidates.
|
|
157
|
+
def find_block_method_arg(send_node)
|
|
158
|
+
end_pos = send_node.source_range.end_pos
|
|
159
|
+
|
|
160
|
+
send_node.last_argument.each_node(:call).find do |node|
|
|
161
|
+
node.source_range.end_pos == end_pos &&
|
|
162
|
+
BLOCK_METHODS.include?(node.method_name) && !node.arguments?
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
95
166
|
def wrap_in_parentheses(corrector, node)
|
|
96
167
|
range = node.loc.selector.end.join(node.first_argument.source_range.begin)
|
|
97
168
|
|
|
@@ -209,21 +209,12 @@ module RuboCop
|
|
|
209
209
|
def report_cross_file_collision(node, fully_qualified_name, display_name)
|
|
210
210
|
return unless project_index
|
|
211
211
|
return unless (declaration = project_index[fully_qualified_name.delete_prefix('::')])
|
|
212
|
-
return unless (prior = prior_definition_in_other_file(declaration))
|
|
212
|
+
return unless (prior = prior_definition_in_other_file(declaration.definitions))
|
|
213
213
|
|
|
214
214
|
msg = format(CROSS_FILE_MSG, constant: display_name, path: prior.location.to_file_path)
|
|
215
215
|
|
|
216
216
|
add_offense(node, message: msg)
|
|
217
217
|
end
|
|
218
|
-
|
|
219
|
-
def prior_definition_in_other_file(declaration)
|
|
220
|
-
current = processed_source.file_path
|
|
221
|
-
|
|
222
|
-
declaration.definitions.find do |definition|
|
|
223
|
-
other = definition.location.to_file_path
|
|
224
|
-
!File.identical?(other, current)
|
|
225
|
-
end
|
|
226
|
-
end
|
|
227
218
|
end
|
|
228
219
|
end
|
|
229
220
|
end
|
|
@@ -20,6 +20,12 @@ module RuboCop
|
|
|
20
20
|
# to prevent conflicting rules. This is because it respects user configurations
|
|
21
21
|
# that want to enable this cop which is disabled by default.
|
|
22
22
|
#
|
|
23
|
+
# When `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is
|
|
24
|
+
# installed, only genuinely ambiguous constants are reported: those
|
|
25
|
+
# that resolve to a different declaration through the surrounding
|
|
26
|
+
# nesting than they would fully qualified. This makes the cop practical
|
|
27
|
+
# to enable without `Only`/`Ignore` lists.
|
|
28
|
+
#
|
|
23
29
|
# @example
|
|
24
30
|
# # By default checks every constant
|
|
25
31
|
#
|
|
@@ -60,6 +66,8 @@ module RuboCop
|
|
|
60
66
|
# Login
|
|
61
67
|
#
|
|
62
68
|
class ConstantResolution < Base
|
|
69
|
+
include ProjectIndexHelp
|
|
70
|
+
|
|
63
71
|
MSG = 'Fully qualify this constant to avoid possibly ambiguous resolution.'
|
|
64
72
|
|
|
65
73
|
# @!method unqualified_const?(node)
|
|
@@ -69,12 +77,29 @@ module RuboCop
|
|
|
69
77
|
|
|
70
78
|
def on_const(node)
|
|
71
79
|
return if !unqualified_const?(node) || node.parent&.defined_module || node.loc.nil?
|
|
80
|
+
return if project_index && !ambiguous_resolution?(node)
|
|
72
81
|
|
|
73
82
|
add_offense(node)
|
|
74
83
|
end
|
|
75
84
|
|
|
76
85
|
private
|
|
77
86
|
|
|
87
|
+
# When `AllCops/UseProjectIndex` is enabled, only genuinely ambiguous
|
|
88
|
+
# references are reported: the constant resolves to a different
|
|
89
|
+
# declaration through the lexical nesting than it would fully
|
|
90
|
+
# qualified. Names that resolve identically either way, or that the
|
|
91
|
+
# index cannot resolve at all (e.g. constants from gems), are not
|
|
92
|
+
# reported.
|
|
93
|
+
def ambiguous_resolution?(node)
|
|
94
|
+
name = node.short_name.to_s
|
|
95
|
+
relative = project_index.resolve_constant(name, lexical_nesting_of(node))
|
|
96
|
+
absolute = project_index.resolve_constant(name, [])
|
|
97
|
+
|
|
98
|
+
!relative.nil? && !absolute.nil? && relative.name != absolute.name
|
|
99
|
+
rescue StandardError
|
|
100
|
+
false
|
|
101
|
+
end
|
|
102
|
+
|
|
78
103
|
def const_name?(name)
|
|
79
104
|
name = name.to_s
|
|
80
105
|
(allowed_names.empty? || allowed_names.include?(name)) && !ignored_names.include?(name)
|