rubocop 1.89.0 → 1.91.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 +309 -14
- data/config/obsoletion.yml +9 -0
- data/exe/rubocop +1 -1
- data/lib/rubocop/cached_data.rb +7 -2
- data/lib/rubocop/changed_files.rb +112 -0
- data/lib/rubocop/cli/command/execute_runner.rb +52 -1
- data/lib/rubocop/cli.rb +11 -7
- data/lib/rubocop/comment_config/directive_range.rb +34 -0
- data/lib/rubocop/comment_config/disable_next.rb +143 -0
- data/lib/rubocop/comment_config/push_pop.rb +55 -0
- data/lib/rubocop/comment_config.rb +50 -64
- data/lib/rubocop/config.rb +33 -5
- data/lib/rubocop/config_loader.rb +13 -6
- data/lib/rubocop/config_loader_resolver.rb +50 -9
- data/lib/rubocop/config_obsoletion/cop_rule.rb +2 -1
- data/lib/rubocop/config_obsoletion/removed_cop.rb +8 -0
- data/lib/rubocop/config_obsoletion.rb +1 -1
- data/lib/rubocop/config_validator.rb +19 -5
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +49 -5
- data/lib/rubocop/cop/corrector.rb +3 -3
- data/lib/rubocop/cop/correctors.rb +2 -2
- data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
- data/lib/rubocop/cop/generator.rb +1 -1
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +1 -2
- data/lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb +1 -2
- data/lib/rubocop/cop/internal_affairs/on_send_without_on_csend.rb +1 -1
- data/lib/rubocop/cop/layout/argument_alignment.rb +2 -0
- data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/class_structure.rb +102 -26
- data/lib/rubocop/cop/layout/comment_indentation.rb +8 -5
- data/lib/rubocop/cop/layout/else_alignment.rb +1 -0
- data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -2
- data/lib/rubocop/cop/layout/empty_line_after_magic_comment.rb +63 -5
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/extra_spacing.rb +105 -3
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +21 -1
- data/lib/rubocop/cop/layout/first_array_element_indentation.rb +17 -4
- data/lib/rubocop/cop/layout/first_parameter_indentation.rb +11 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +6 -5
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/line_length.rb +20 -3
- data/lib/rubocop/cop/layout/multiline_assignment_layout.rb +1 -2
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +5 -5
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +14 -3
- data/lib/rubocop/cop/layout/space_around_operators.rb +12 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +2 -4
- data/lib/rubocop/cop/lint/ambiguous_range.rb +1 -2
- data/lib/rubocop/cop/lint/argument_mismatch.rb +99 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +165 -15
- data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +113 -15
- data/lib/rubocop/cop/lint/empty_block.rb +1 -1
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +1 -2
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/interpolation_check.rb +1 -2
- data/lib/rubocop/cop/lint/literal_as_condition.rb +11 -8
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +185 -0
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +25 -12
- data/lib/rubocop/cop/lint/name_typo.rb +53 -6
- data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
- data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +15 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +17 -1
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +122 -29
- data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +47 -7
- data/lib/rubocop/cop/lint/redundant_require_statement.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_type_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/redundant_with_index.rb +1 -2
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +1 -2
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +2 -4
- data/lib/rubocop/cop/lint/super_argument_mismatch.rb +179 -0
- data/lib/rubocop/cop/lint/suppressed_exception_in_number_conversion.rb +1 -2
- data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -2
- data/lib/rubocop/cop/lint/unreachable_code.rb +1 -2
- data/lib/rubocop/cop/lint/unused_private_method.rb +94 -17
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +1 -2
- data/lib/rubocop/cop/lint/useless_assignment.rb +1 -2
- data/lib/rubocop/cop/lint/useless_constant_scoping.rb +36 -0
- data/lib/rubocop/cop/lint/useless_method_definition.rb +1 -1
- data/lib/rubocop/cop/lint/useless_rescue.rb +1 -2
- data/lib/rubocop/cop/lint/utils/nil_receiver_checker.rb +61 -7
- data/lib/rubocop/cop/lint/void.rb +1 -2
- data/lib/rubocop/cop/lint.rb +3 -0
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
- data/lib/rubocop/cop/migration/department_name.rb +1 -2
- data/lib/rubocop/cop/mixin/alignment.rb +1 -1
- data/lib/rubocop/cop/mixin/annotation_comment.rb +1 -1
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -2
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +11 -1
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +7 -4
- data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_transform_method.rb +1 -1
- data/lib/rubocop/cop/mixin/indexed_method_arity.rb +94 -0
- data/lib/rubocop/cop/mixin/line_length_help.rb +7 -1
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +62 -6
- data/lib/rubocop/cop/mixin/range_help.rb +1 -2
- data/lib/rubocop/cop/mixin/statement_modifier.rb +13 -7
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -4
- data/lib/rubocop/cop/mixin.rb +6 -2
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
- data/lib/rubocop/cop/naming/block_forwarding.rb +4 -1
- data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
- data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +2 -4
- data/lib/rubocop/cop/naming/method_name.rb +5 -2
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -2
- data/lib/rubocop/cop/offense.rb +27 -4
- data/lib/rubocop/cop/registry.rb +43 -11
- data/lib/rubocop/cop/severity.rb +12 -0
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +3 -2
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +5 -8
- data/lib/rubocop/cop/style/array_first_last.rb +1 -2
- data/lib/rubocop/cop/style/bitwise_predicate.rb +1 -2
- data/lib/rubocop/cop/style/block_delimiters.rb +3 -6
- data/lib/rubocop/cop/style/case_like_if.rb +5 -7
- data/lib/rubocop/cop/style/class_and_module_children.rb +1 -2
- data/lib/rubocop/cop/style/combinable_loops.rb +1 -2
- data/lib/rubocop/cop/style/concat_array_literals.rb +1 -2
- data/lib/rubocop/cop/style/conditional_assignment.rb +1 -2
- data/lib/rubocop/cop/style/constant_visibility.rb +6 -3
- data/lib/rubocop/cop/style/copyright.rb +1 -2
- data/lib/rubocop/cop/style/data_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/directive_scope.rb +304 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +83 -13
- data/lib/rubocop/cop/style/documentation_method.rb +1 -9
- data/lib/rubocop/cop/style/each_for_simple_loop.rb +1 -1
- data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
- data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +12 -2
- data/lib/rubocop/cop/style/endless_method.rb +31 -12
- data/lib/rubocop/cop/style/file_null.rb +2 -2
- data/lib/rubocop/cop/style/float_division.rb +10 -16
- data/lib/rubocop/cop/style/for.rb +5 -0
- data/lib/rubocop/cop/style/format_string_token.rb +1 -2
- data/lib/rubocop/cop/style/guard_clause.rb +5 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
- data/lib/rubocop/cop/style/hash_lookup_method.rb +3 -0
- data/lib/rubocop/cop/style/hash_syntax.rb +1 -2
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +2 -4
- data/lib/rubocop/cop/style/if_inside_else.rb +1 -2
- data/lib/rubocop/cop/style/if_unless_modifier.rb +1 -2
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +1 -2
- data/lib/rubocop/cop/style/magic_comment_format.rb +1 -1
- data/lib/rubocop/cop/style/map_to_hash.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +1 -1
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +1 -2
- data/lib/rubocop/cop/style/multiline_method_signature.rb +1 -2
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -2
- data/lib/rubocop/cop/style/negative_array_index.rb +2 -4
- data/lib/rubocop/cop/style/nested_modifier.rb +9 -4
- data/lib/rubocop/cop/style/nil_comparison.rb +36 -9
- data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
- data/lib/rubocop/cop/style/non_nil_check.rb +18 -2
- data/lib/rubocop/cop/style/not.rb +3 -1
- data/lib/rubocop/cop/style/numeric_literal_prefix.rb +11 -1
- data/lib/rubocop/cop/style/one_line_conditional.rb +1 -1
- data/lib/rubocop/cop/style/operator_method_call.rb +1 -2
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +6 -1
- data/lib/rubocop/cop/style/redundant_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_condition.rb +6 -8
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +11 -3
- data/lib/rubocop/cop/style/redundant_each.rb +1 -2
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
- data/lib/rubocop/cop/style/redundant_format.rb +2 -4
- data/lib/rubocop/cop/style/redundant_parentheses.rb +13 -5
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
- data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +6 -5
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +6 -6
- data/lib/rubocop/cop/style/redundant_return.rb +1 -2
- data/lib/rubocop/cop/style/redundant_self_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_string_escape.rb +1 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +1 -2
- data/lib/rubocop/cop/style/safe_navigation.rb +1 -2
- data/lib/rubocop/cop/style/sample.rb +11 -6
- data/lib/rubocop/cop/style/select_by_regexp.rb +1 -2
- data/lib/rubocop/cop/style/semicolon.rb +2 -4
- data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
- data/lib/rubocop/cop/style/single_line_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
- data/lib/rubocop/cop/style/special_global_vars.rb +1 -1
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +2 -0
- data/lib/rubocop/cop/style/string_concatenation.rb +18 -5
- data/lib/rubocop/cop/style/struct_inheritance.rb +14 -0
- data/lib/rubocop/cop/style/super_arguments.rb +1 -2
- data/lib/rubocop/cop/style/symbol_proc.rb +1 -2
- data/lib/rubocop/cop/style/time_now.rb +41 -0
- data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +1 -2
- data/lib/rubocop/cop/style.rb +2 -1
- data/lib/rubocop/cop/team.rb +11 -3
- data/lib/rubocop/cop/util.rb +3 -5
- data/lib/rubocop/cop/variable_force/variable.rb +1 -2
- data/lib/rubocop/cops_documentation_generator.rb +26 -8
- data/lib/rubocop/directive_comment.rb +73 -10
- data/lib/rubocop/formatter/disabled_config_formatter.rb +19 -1
- data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/formatter/github_actions_formatter.rb +6 -5
- data/lib/rubocop/formatter/html_formatter.rb +2 -4
- data/lib/rubocop/formatter/json_formatter.rb +11 -6
- data/lib/rubocop/formatter/junit_formatter.rb +26 -9
- data/lib/rubocop/formatter/offense_count_formatter.rb +1 -2
- data/lib/rubocop/formatter/sarif_formatter.rb +181 -0
- data/lib/rubocop/formatter/simple_text_formatter.rb +4 -3
- data/lib/rubocop/formatter/tap_formatter.rb +3 -1
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +1 -2
- data/lib/rubocop/formatter.rb +1 -0
- data/lib/rubocop/lockfile.rb +18 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -8
- data/lib/rubocop/lsp/routes.rb +48 -19
- data/lib/rubocop/magic_comment.rb +27 -0
- data/lib/rubocop/mcp/server.rb +1 -2
- data/lib/rubocop/options.rb +69 -7
- data/lib/rubocop/path_util.rb +2 -3
- data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
- data/lib/rubocop/plugin/loader.rb +1 -1
- data/lib/rubocop/rake_task.rb +1 -2
- data/lib/rubocop/remote_config.rb +4 -4
- data/lib/rubocop/result_cache.rb +16 -5
- data/lib/rubocop/rspec/expect_offense.rb +2 -4
- data/lib/rubocop/rspec/shared_contexts.rb +4 -2
- data/lib/rubocop/runner.rb +90 -44
- data/lib/rubocop/server/cache.rb +1 -2
- data/lib/rubocop/server/cli.rb +2 -4
- data/lib/rubocop/server.rb +14 -0
- data/lib/rubocop/target_finder.rb +4 -1
- data/lib/rubocop/todo_audit.rb +111 -0
- data/lib/rubocop/unified_diff.rb +257 -0
- data/lib/rubocop/version.rb +3 -5
- data/lib/rubocop.rb +7 -1
- data/lib/ruby_lsp/rubocop/addon.rb +1 -2
- metadata +18 -6
- data/lib/rubocop/cop/style/double_cop_disable_directive.rb +0 -46
|
@@ -41,6 +41,7 @@ module RuboCop
|
|
|
41
41
|
|
|
42
42
|
@aligned_comments = aligned_locations(processed_source.comments.map(&:loc))
|
|
43
43
|
@corrected = Set.new if force_equal_sign_alignment?
|
|
44
|
+
prepare_alignment_data
|
|
44
45
|
|
|
45
46
|
processed_source.tokens.each_cons(2) do |token1, token2|
|
|
46
47
|
check_tokens(processed_source.ast, token1, token2)
|
|
@@ -95,19 +96,120 @@ module RuboCop
|
|
|
95
96
|
end_pos = token2.begin_pos - 1
|
|
96
97
|
return if end_pos <= start_pos
|
|
97
98
|
|
|
98
|
-
return if allow_for_alignment? && aligned_tok?(token2)
|
|
99
|
+
return if allow_for_alignment? && aligned_tok?(token1, token2)
|
|
99
100
|
|
|
100
101
|
yield range_between(start_pos, end_pos)
|
|
101
102
|
end
|
|
102
103
|
|
|
103
|
-
def aligned_tok?(token)
|
|
104
|
+
def aligned_tok?(previous_token, token)
|
|
104
105
|
if token.comment?
|
|
105
106
|
@aligned_comments.include?(token.line)
|
|
106
107
|
else
|
|
107
|
-
aligned_with_something?(token.pos)
|
|
108
|
+
aligned_with_something?(token.pos) &&
|
|
109
|
+
(ASSIGNMENT_OR_COMPARISON_TOKENS.include?(token.type) ||
|
|
110
|
+
spacing_varies?(previous_token, token))
|
|
108
111
|
end
|
|
109
112
|
end
|
|
110
113
|
|
|
114
|
+
def spacing_varies?(previous_token, token)
|
|
115
|
+
by_column, typed = group_profile(token.line)
|
|
116
|
+
column = token.pos.column
|
|
117
|
+
lines, spacings = typed[[column, token.type]]
|
|
118
|
+
entries = by_column[column]
|
|
119
|
+
|
|
120
|
+
if lines.size > 1
|
|
121
|
+
uniform_alignment_allowed?(token, lines, spacings)
|
|
122
|
+
elsif entries.size > 1
|
|
123
|
+
distinct_spacings(entries).size > 1
|
|
124
|
+
else
|
|
125
|
+
nearest_spacing_varies?(previous_token, token)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def uniform_alignment_allowed?(token, lines, spacings)
|
|
130
|
+
return @alignment_verdicts[lines] if @alignment_verdicts.key?(lines)
|
|
131
|
+
|
|
132
|
+
@alignment_verdicts[lines] =
|
|
133
|
+
spacings.uniq.size > 1 || aligned_on_other_column?(token, lines)
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def aligned_on_other_column?(token, lines)
|
|
137
|
+
line_set = Set.new(lines)
|
|
138
|
+
by_column, = group_profile(token.line)
|
|
139
|
+
|
|
140
|
+
by_column.any? do |column, entries|
|
|
141
|
+
next false if column == token.pos.column
|
|
142
|
+
|
|
143
|
+
shared = entries.filter_map { |line, spacing| spacing if line_set.include?(line) }
|
|
144
|
+
shared.uniq.size > 1
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def distinct_spacings(entries)
|
|
149
|
+
entries.map(&:last).uniq
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def group_profile(line_number)
|
|
153
|
+
group = alignment_lines(line_number)
|
|
154
|
+
|
|
155
|
+
@group_profiles[group] ||= build_group_profile(group)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def build_group_profile(group)
|
|
159
|
+
by_column = Hash.new { |hash, key| hash[key] = [] }
|
|
160
|
+
typed = Hash.new { |hash, key| hash[key] = [[], []] }
|
|
161
|
+
|
|
162
|
+
group.each { |line| record_line_spacings(by_column, typed, line) }
|
|
163
|
+
|
|
164
|
+
[by_column, typed]
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def record_line_spacings(by_column, typed, line)
|
|
168
|
+
@tokens_by_line[line]&.each_cons(2) do |previous_token, token|
|
|
169
|
+
spacing = token.begin_pos - previous_token.end_pos
|
|
170
|
+
by_column[token.pos.column] << [line, spacing]
|
|
171
|
+
lines, spacings = typed[[token.pos.column, token.type]]
|
|
172
|
+
lines << line
|
|
173
|
+
spacings << spacing
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def nearest_spacing_varies?(previous_token, token)
|
|
178
|
+
spacing = token.begin_pos - previous_token.end_pos
|
|
179
|
+
line_ranges = [(token.line - 1).downto(1),
|
|
180
|
+
(token.line + 1).upto(processed_source.lines.length)]
|
|
181
|
+
|
|
182
|
+
line_ranges.any? do |line_numbers|
|
|
183
|
+
nearest_spacing_differs?(line_numbers, token, spacing)
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def nearest_spacing_differs?(line_numbers, token, spacing)
|
|
188
|
+
line_numbers.each do |line|
|
|
189
|
+
break if definition_boundary_lines.include?(line)
|
|
190
|
+
next if aligned_comment_lines.include?(line)
|
|
191
|
+
|
|
192
|
+
other_spacing = spacing_before_token_at(line, token.pos.column)
|
|
193
|
+
return other_spacing != spacing if other_spacing
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
false
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def spacing_before_token_at(line_number, column)
|
|
200
|
+
tokens = @tokens_by_line[line_number]
|
|
201
|
+
index = tokens&.index { |token| token.pos.column == column }
|
|
202
|
+
return unless index&.positive?
|
|
203
|
+
|
|
204
|
+
tokens[index].begin_pos - tokens[index - 1].end_pos
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def prepare_alignment_data
|
|
208
|
+
@group_profiles = {}.compare_by_identity
|
|
209
|
+
@alignment_verdicts = {}.compare_by_identity
|
|
210
|
+
@tokens_by_line = processed_source.tokens.group_by(&:line)
|
|
211
|
+
end
|
|
212
|
+
|
|
111
213
|
def ignored_range?(ast, start_pos)
|
|
112
214
|
ignored_ranges(ast).any? { |r| r.include?(start_pos) }
|
|
113
215
|
end
|
|
@@ -156,7 +156,8 @@ module RuboCop
|
|
|
156
156
|
return unless should_check?(node)
|
|
157
157
|
return if same_line?(node, node.first_argument)
|
|
158
158
|
return if enforce_first_argument_with_fixed_indentation? &&
|
|
159
|
-
!enable_layout_first_method_argument_line_break?
|
|
159
|
+
(!enable_layout_first_method_argument_line_break? ||
|
|
160
|
+
conflicts_with_fixed_indentation_alignment?(node))
|
|
160
161
|
|
|
161
162
|
indent = base_indentation(node) + configured_indentation_width
|
|
162
163
|
|
|
@@ -309,6 +310,25 @@ module RuboCop
|
|
|
309
310
|
def enable_layout_first_method_argument_line_break?
|
|
310
311
|
config.cop_enabled?('Layout/FirstMethodArgumentLineBreak')
|
|
311
312
|
end
|
|
313
|
+
|
|
314
|
+
def conflicts_with_fixed_indentation_alignment?(node)
|
|
315
|
+
return false unless special_inner_call_indentation?(node)
|
|
316
|
+
return false unless argument_alignment_applies?(node)
|
|
317
|
+
|
|
318
|
+
base_indentation(node) != indentation_of_method_line(node)
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
def argument_alignment_applies?(node)
|
|
322
|
+
return false if !node.call_type? || node.method?(:[]=)
|
|
323
|
+
|
|
324
|
+
node.arguments.size >= 2 ||
|
|
325
|
+
(node.first_argument.hash_type? && node.first_argument.pairs.count >= 2)
|
|
326
|
+
end
|
|
327
|
+
|
|
328
|
+
def indentation_of_method_line(node)
|
|
329
|
+
lineno = node.loc.selector ? node.loc.selector.line : node.loc.begin.line
|
|
330
|
+
processed_source.lines[lineno - 1] =~ /\S/
|
|
331
|
+
end
|
|
312
332
|
end
|
|
313
333
|
end
|
|
314
334
|
end
|
|
@@ -89,15 +89,16 @@ module RuboCop
|
|
|
89
89
|
'in an array, relative to %<base_description>s.'
|
|
90
90
|
|
|
91
91
|
def on_array(node)
|
|
92
|
-
return
|
|
92
|
+
return unless node.loc.begin
|
|
93
|
+
return if autocorrect_incompatible_with_other_cops?(node, nil)
|
|
93
94
|
|
|
94
|
-
check(node, nil)
|
|
95
|
+
check(node, nil)
|
|
95
96
|
end
|
|
96
97
|
|
|
97
98
|
def on_send(node)
|
|
98
|
-
return if style != :consistent && enforce_first_argument_with_fixed_indentation?
|
|
99
|
-
|
|
100
99
|
each_argument_node(node, :array) do |array_node, left_parenthesis|
|
|
100
|
+
next if autocorrect_incompatible_with_other_cops?(array_node, left_parenthesis)
|
|
101
|
+
|
|
101
102
|
check(array_node, left_parenthesis)
|
|
102
103
|
end
|
|
103
104
|
end
|
|
@@ -105,6 +106,18 @@ module RuboCop
|
|
|
105
106
|
|
|
106
107
|
private
|
|
107
108
|
|
|
109
|
+
def autocorrect_incompatible_with_other_cops?(array_node, left_parenthesis)
|
|
110
|
+
return false unless enforce_first_argument_with_fixed_indentation?
|
|
111
|
+
return true if style != :consistent
|
|
112
|
+
|
|
113
|
+
# `Layout/ArrayAlignment` does not align single-element arrays.
|
|
114
|
+
return false if array_node.children.size < 2
|
|
115
|
+
|
|
116
|
+
_base_column, indent_base_type =
|
|
117
|
+
indent_base(array_node.loc.begin, array_node.values.first, left_parenthesis)
|
|
118
|
+
indent_base_type != :start_of_line
|
|
119
|
+
end
|
|
120
|
+
|
|
108
121
|
def autocorrect(corrector, node)
|
|
109
122
|
AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
|
|
110
123
|
end
|
|
@@ -53,6 +53,7 @@ module RuboCop
|
|
|
53
53
|
def on_def(node)
|
|
54
54
|
return if node.arguments.empty?
|
|
55
55
|
return if node.arguments.loc.begin.nil?
|
|
56
|
+
return if autocorrect_incompatible_with_other_cops?(node)
|
|
56
57
|
|
|
57
58
|
check(node)
|
|
58
59
|
end
|
|
@@ -60,6 +61,11 @@ module RuboCop
|
|
|
60
61
|
|
|
61
62
|
private
|
|
62
63
|
|
|
64
|
+
def autocorrect_incompatible_with_other_cops?(node)
|
|
65
|
+
node.arguments.size >= 2 &&
|
|
66
|
+
style == :align_parentheses && enforce_parameter_with_fixed_indentation?
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
def autocorrect(corrector, node)
|
|
64
70
|
AlignmentCorrector.correct(corrector, processed_source, node, @column_delta)
|
|
65
71
|
end
|
|
@@ -95,6 +101,11 @@ module RuboCop
|
|
|
95
101
|
base_description: base_description
|
|
96
102
|
)
|
|
97
103
|
end
|
|
104
|
+
|
|
105
|
+
def enforce_parameter_with_fixed_indentation?
|
|
106
|
+
parameter_alignment_config = config.for_enabled_cop('Layout/ParameterAlignment')
|
|
107
|
+
parameter_alignment_config['EnforcedStyle'] == 'with_fixed_indentation'
|
|
108
|
+
end
|
|
98
109
|
end
|
|
99
110
|
end
|
|
100
111
|
end
|
|
@@ -330,7 +330,11 @@ module RuboCop
|
|
|
330
330
|
end
|
|
331
331
|
|
|
332
332
|
def correct_no_value(corrector, key_delta, key)
|
|
333
|
-
adjust(corrector, key_delta, key)
|
|
333
|
+
adjust(corrector, clamped_key_delta(key_delta, key), key)
|
|
334
|
+
end
|
|
335
|
+
|
|
336
|
+
def clamped_key_delta(key_delta, key)
|
|
337
|
+
[key_delta, -key.column].max
|
|
334
338
|
end
|
|
335
339
|
|
|
336
340
|
def correct_key_value(corrector, delta, key, value, separator)
|
|
@@ -341,10 +345,7 @@ module RuboCop
|
|
|
341
345
|
value_delta = delta[:value] || 0
|
|
342
346
|
key_delta = delta[:key] || 0
|
|
343
347
|
|
|
344
|
-
|
|
345
|
-
key_delta = -key_column if key_delta < -key_column
|
|
346
|
-
|
|
347
|
-
adjust(corrector, key_delta, key)
|
|
348
|
+
adjust(corrector, clamped_key_delta(key_delta, key), key)
|
|
348
349
|
adjust(corrector, separator_delta, separator)
|
|
349
350
|
adjust(corrector, value_delta, value)
|
|
350
351
|
end
|
|
@@ -400,7 +400,7 @@ module RuboCop
|
|
|
400
400
|
if body_node.rescue_type?
|
|
401
401
|
check_rescue?(body_node)
|
|
402
402
|
elsif body_node.ensure_type?
|
|
403
|
-
block_body, = *body_node # rubocop:disable InternalAffairs/NodeDestructuring
|
|
403
|
+
block_body, = *body_node # rubocop:disable InternalAffairs/NodeDestructuring -- `EnsureNode` has no accessor for the protected body
|
|
404
404
|
if block_body&.rescue_type?
|
|
405
405
|
check_rescue?(block_body)
|
|
406
406
|
else
|
|
@@ -440,7 +440,7 @@ module RuboCop
|
|
|
440
440
|
begin_pos - indentation
|
|
441
441
|
end
|
|
442
442
|
|
|
443
|
-
pos =
|
|
443
|
+
pos = ind <= begin_pos ? ind..begin_pos : begin_pos..ind
|
|
444
444
|
range_between(pos.begin, pos.end)
|
|
445
445
|
end
|
|
446
446
|
|
|
@@ -87,7 +87,7 @@ module RuboCop
|
|
|
87
87
|
corrector.replace(range, correction)
|
|
88
88
|
end
|
|
89
89
|
|
|
90
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
90
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
91
91
|
def ignored_literal_ranges(ast)
|
|
92
92
|
# which lines start inside a string literal?
|
|
93
93
|
return [] if ast.nil?
|
|
@@ -106,7 +106,6 @@ module RuboCop
|
|
|
106
106
|
end
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
110
109
|
|
|
111
110
|
def comment_ranges(comments)
|
|
112
111
|
comments.map(&:source_range)
|
|
@@ -104,6 +104,11 @@ module RuboCop
|
|
|
104
104
|
alias on_defs on_def
|
|
105
105
|
|
|
106
106
|
def on_new_investigation
|
|
107
|
+
@breakable_range_by_line_index = {}
|
|
108
|
+
@breakable_string_delimiters = {}
|
|
109
|
+
@endless_methods_by_line = {}
|
|
110
|
+
@heredocs = nil
|
|
111
|
+
|
|
107
112
|
return unless processed_source.raw_source.include?(';')
|
|
108
113
|
|
|
109
114
|
check_for_breakable_semicolons(processed_source)
|
|
@@ -288,7 +293,7 @@ module RuboCop
|
|
|
288
293
|
source_range = node.source_range
|
|
289
294
|
relevant_substr = largest_possible_string(node)
|
|
290
295
|
|
|
291
|
-
if (space_pos = relevant_substr
|
|
296
|
+
if (space_pos = breakable_space_position(node, relevant_substr))
|
|
292
297
|
source_range.resize(space_pos + 1)
|
|
293
298
|
elsif (escape_pos = relevant_substr.rindex(/\\(u[\da-f]{0,4}|x[\da-f]{0,2})?\z/))
|
|
294
299
|
source_range.resize(escape_pos)
|
|
@@ -300,6 +305,19 @@ module RuboCop
|
|
|
300
305
|
end
|
|
301
306
|
end
|
|
302
307
|
|
|
308
|
+
def breakable_space_position(node, substr)
|
|
309
|
+
limit = string_content_length(node) - 1
|
|
310
|
+
space_pos = substr.rindex(/\s/)
|
|
311
|
+
space_pos = substr[0, limit].rindex(/\s/) if space_pos && space_pos + 1 > limit
|
|
312
|
+
space_pos
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
def string_content_length(node)
|
|
316
|
+
content_end = node.loc?(:end) ? node.loc.end.begin_pos : node.source_range.end_pos
|
|
317
|
+
|
|
318
|
+
content_end - node.source_range.begin_pos
|
|
319
|
+
end
|
|
320
|
+
|
|
303
321
|
def breakable_dstr_begin_position(node)
|
|
304
322
|
source_range = node.source_range
|
|
305
323
|
source_range.begin_pos if source_range.column < max && source_range.last_column >= max
|
|
@@ -328,7 +346,7 @@ module RuboCop
|
|
|
328
346
|
[max - indentation_difference(line), 0].max
|
|
329
347
|
end
|
|
330
348
|
|
|
331
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
349
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
332
350
|
def check_line(line, line_index)
|
|
333
351
|
return if line_length(line) <= max
|
|
334
352
|
return if allowed_line?(line, line_index)
|
|
@@ -348,7 +366,6 @@ module RuboCop
|
|
|
348
366
|
|
|
349
367
|
register_offense(excess_range(nil, line, line_index), line, line_index)
|
|
350
368
|
end
|
|
351
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
352
369
|
|
|
353
370
|
def allowed_line?(line, line_index)
|
|
354
371
|
matches_allowed_pattern?(line) ||
|
|
@@ -71,7 +71,7 @@ module RuboCop
|
|
|
71
71
|
|
|
72
72
|
BLOCK_TYPES = %i[block numblock itblock].freeze
|
|
73
73
|
|
|
74
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
74
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
75
75
|
def check_assignment(node, rhs)
|
|
76
76
|
return if node.send_type? && node.loc.operator&.source != '='
|
|
77
77
|
return unless rhs
|
|
@@ -80,7 +80,6 @@ module RuboCop
|
|
|
80
80
|
|
|
81
81
|
check_by_enforced_style(node, rhs)
|
|
82
82
|
end
|
|
83
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
84
83
|
|
|
85
84
|
def check_by_enforced_style(node, rhs)
|
|
86
85
|
case style
|
|
@@ -423,11 +423,11 @@ module RuboCop
|
|
|
423
423
|
end
|
|
424
424
|
|
|
425
425
|
def leftmost_call_on_same_line(node)
|
|
426
|
-
current = node
|
|
427
|
-
while current.receiver
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
current =
|
|
426
|
+
current = unwrap_block_node(node)
|
|
427
|
+
while (receiver = unwrap_block_node(current.receiver)) &&
|
|
428
|
+
receiver.call_type? && receiver.loc?(:dot) &&
|
|
429
|
+
receiver.loc.dot.line == current.loc.dot.line
|
|
430
|
+
current = receiver
|
|
431
431
|
end
|
|
432
432
|
current
|
|
433
433
|
end
|
|
@@ -91,7 +91,7 @@ module RuboCop
|
|
|
91
91
|
)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
# rubocop:disable Metrics/AbcSize, Metrics/MethodLength
|
|
94
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/MethodLength
|
|
95
95
|
def alignment_source(node, starting_loc)
|
|
96
96
|
ending_loc =
|
|
97
97
|
case node.type
|
|
@@ -111,7 +111,6 @@ module RuboCop
|
|
|
111
111
|
|
|
112
112
|
range_between(starting_loc.begin_pos, ending_loc.end_pos).source
|
|
113
113
|
end
|
|
114
|
-
# rubocop:enable Metrics/AbcSize, Metrics/MethodLength
|
|
115
114
|
|
|
116
115
|
# We will use ancestor or wrapper with access modifier.
|
|
117
116
|
|
|
@@ -5,7 +5,8 @@ module RuboCop
|
|
|
5
5
|
module Layout
|
|
6
6
|
# Checks the spacing inside and after block parameters pipes. Line breaks
|
|
7
7
|
# inside parameter pipes are checked by `Layout/MultilineBlockLayout` and
|
|
8
|
-
# not by this cop.
|
|
8
|
+
# not by this cop. Spaces inside a lambda's parameter parentheses are left
|
|
9
|
+
# to `Layout/SpaceInsideParens` when that cop enforces a conflicting style.
|
|
9
10
|
#
|
|
10
11
|
# @example EnforcedStyleInsidePipes: no_space (default)
|
|
11
12
|
# # bad
|
|
@@ -29,12 +30,12 @@ module RuboCop
|
|
|
29
30
|
include RangeHelp
|
|
30
31
|
extend AutoCorrector
|
|
31
32
|
|
|
32
|
-
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler
|
|
33
|
+
def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler, InternalAffairs/ItblockHandler -- checks spacing inside a block parameter list
|
|
33
34
|
arguments = node.arguments
|
|
34
35
|
|
|
35
36
|
return unless node.arguments? && pipes?(arguments)
|
|
36
37
|
|
|
37
|
-
check_inside_pipes(arguments)
|
|
38
|
+
check_inside_pipes(arguments) unless conflicting_space_inside_parens_style?(arguments)
|
|
38
39
|
check_after_closing_pipe(arguments) if node.body
|
|
39
40
|
check_each_arg(arguments)
|
|
40
41
|
end
|
|
@@ -53,6 +54,16 @@ module RuboCop
|
|
|
53
54
|
'EnforcedStyleInsidePipes'
|
|
54
55
|
end
|
|
55
56
|
|
|
57
|
+
def conflicting_space_inside_parens_style?(arguments)
|
|
58
|
+
return false unless arguments.loc.begin.source == '('
|
|
59
|
+
|
|
60
|
+
case config.for_enabled_cop('Layout/SpaceInsideParens')['EnforcedStyle']
|
|
61
|
+
when 'no_space' then style == :space
|
|
62
|
+
when 'space', 'compact' then style == :no_space
|
|
63
|
+
else false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
56
67
|
def check_inside_pipes(arguments)
|
|
57
68
|
case style
|
|
58
69
|
when :no_space
|
|
@@ -129,6 +129,12 @@ module RuboCop
|
|
|
129
129
|
check_operator(:class, node.loc.operator, rhs)
|
|
130
130
|
end
|
|
131
131
|
|
|
132
|
+
def on_def(node)
|
|
133
|
+
return unless node.endless?
|
|
134
|
+
|
|
135
|
+
check_operator(:assignment, node.loc.assignment, node.body)
|
|
136
|
+
end
|
|
137
|
+
|
|
132
138
|
def on_binary(node)
|
|
133
139
|
rhs = node.rhs
|
|
134
140
|
|
|
@@ -170,6 +176,7 @@ module RuboCop
|
|
|
170
176
|
alias on_or_asgn on_assignment
|
|
171
177
|
alias on_and_asgn on_assignment
|
|
172
178
|
alias on_op_asgn on_assignment
|
|
179
|
+
alias on_defs on_def
|
|
173
180
|
|
|
174
181
|
private
|
|
175
182
|
|
|
@@ -251,7 +258,7 @@ module RuboCop
|
|
|
251
258
|
return false unless allow_for_alignment?
|
|
252
259
|
return false unless with_space.source.start_with?(EXCESSIVE_SPACE)
|
|
253
260
|
|
|
254
|
-
return !aligned_with_operator?(operator) unless type
|
|
261
|
+
return !aligned_with_operator?(operator) unless grouped_alignment_type?(type)
|
|
255
262
|
|
|
256
263
|
token = Token.new(operator, nil, operator.source)
|
|
257
264
|
align_preceding = aligned_with_preceding_equals_operator(token)
|
|
@@ -262,6 +269,10 @@ module RuboCop
|
|
|
262
269
|
aligned_with_subsequent_equals_operator(token) != :yes
|
|
263
270
|
end
|
|
264
271
|
|
|
272
|
+
def grouped_alignment_type?(type)
|
|
273
|
+
type == :assignment || (type == :special_asgn && force_equal_sign_alignment?)
|
|
274
|
+
end
|
|
275
|
+
|
|
265
276
|
def excess_trailing_space?(right_operand, with_space)
|
|
266
277
|
with_space.source.end_with?(EXCESSIVE_SPACE) &&
|
|
267
278
|
(!allow_for_alignment? || !aligned_with_something?(right_operand))
|
|
@@ -153,7 +153,7 @@ module RuboCop
|
|
|
153
153
|
[token.line - 1, token.column - 1]
|
|
154
154
|
end
|
|
155
155
|
|
|
156
|
-
# rubocop:disable Metrics/ParameterLists
|
|
156
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
157
157
|
def issue_offenses(node, tokens, left, right, start_ok, end_ok)
|
|
158
158
|
case style
|
|
159
159
|
when :no_space
|
|
@@ -165,13 +165,12 @@ module RuboCop
|
|
|
165
165
|
compact_offenses(node, tokens, left, right, start_ok, end_ok)
|
|
166
166
|
end
|
|
167
167
|
end
|
|
168
|
-
# rubocop:enable Metrics/ParameterLists
|
|
169
168
|
|
|
170
169
|
def next_to_comment?(tokens, token)
|
|
171
170
|
tokens[index_for(tokens, token) + 1].comment?
|
|
172
171
|
end
|
|
173
172
|
|
|
174
|
-
# rubocop:disable Metrics/ParameterLists
|
|
173
|
+
# rubocop:disable-next Metrics/ParameterLists
|
|
175
174
|
def compact_offenses(node, tokens, left, right, start_ok, end_ok)
|
|
176
175
|
if qualifies_for_compact?(tokens, left, side: :left)
|
|
177
176
|
compact_offense(node, left, side: :left)
|
|
@@ -185,7 +184,6 @@ module RuboCop
|
|
|
185
184
|
space_offenses(node, nil, right, MSG, start_ok: true, end_ok: end_ok)
|
|
186
185
|
end
|
|
187
186
|
end
|
|
188
|
-
# rubocop:enable Metrics/ParameterLists
|
|
189
187
|
|
|
190
188
|
def qualifies_for_compact?(tokens, token, side: :right)
|
|
191
189
|
if side == :right
|
|
@@ -83,14 +83,13 @@ module RuboCop
|
|
|
83
83
|
yield range.end if range.end
|
|
84
84
|
end
|
|
85
85
|
|
|
86
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
86
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
87
87
|
def acceptable?(node)
|
|
88
88
|
node.begin_type? ||
|
|
89
89
|
node.literal? || rational_literal?(node) ||
|
|
90
90
|
node.variable? || node.const_type? || node.self_type? ||
|
|
91
91
|
(node.call_type? && acceptable_call?(node))
|
|
92
92
|
end
|
|
93
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
94
93
|
|
|
95
94
|
def acceptable_call?(node)
|
|
96
95
|
return true if node.unary_operation?
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module RuboCop
|
|
4
|
+
module Cop
|
|
5
|
+
module Lint
|
|
6
|
+
# Checks for calls that pass the wrong number of positional arguments to a
|
|
7
|
+
# method whose definition is known from the project index.
|
|
8
|
+
#
|
|
9
|
+
# The check is powered by the project-wide index, so it only runs when
|
|
10
|
+
# `AllCops/UseProjectIndex` is enabled and the `rubydex` gem is installed.
|
|
11
|
+
# Without the index the cop does nothing.
|
|
12
|
+
#
|
|
13
|
+
# Only calls on constant receivers (`Foo.bar`) are considered, and only
|
|
14
|
+
# when the receiver resolves in the index, its entire ancestry is resolved
|
|
15
|
+
# (so a method inherited from a gem or the standard library never produces
|
|
16
|
+
# an offense), and the called method resolves to a single, unambiguous
|
|
17
|
+
# signature. Calls that forward arguments (`*`, `**`, `...`) are skipped
|
|
18
|
+
# because their argument count is not statically known. Keyword arguments
|
|
19
|
+
# passed to a method that declares no keyword parameters are counted as the
|
|
20
|
+
# single positional hash they collapse into, matching Ruby's semantics.
|
|
21
|
+
#
|
|
22
|
+
# Constructor calls (`Foo.new`) and keyword-argument validation are out of
|
|
23
|
+
# scope: only positional arity of explicitly defined singleton methods is
|
|
24
|
+
# checked. `new` is skipped outright, because `Class#new` is not in the
|
|
25
|
+
# index: any `new` the index does find sits below it in the method
|
|
26
|
+
# resolution order, where Ruby would never reach it.
|
|
27
|
+
#
|
|
28
|
+
# @example
|
|
29
|
+
# # Given the project defines:
|
|
30
|
+
# # class Report
|
|
31
|
+
# # def self.generate(source, format); end
|
|
32
|
+
# # end
|
|
33
|
+
#
|
|
34
|
+
# # bad
|
|
35
|
+
# Report.generate(source)
|
|
36
|
+
#
|
|
37
|
+
# # bad
|
|
38
|
+
# Report.generate(source, :pdf, :extra)
|
|
39
|
+
#
|
|
40
|
+
# # good
|
|
41
|
+
# Report.generate(source, :pdf)
|
|
42
|
+
#
|
|
43
|
+
class ArgumentMismatch < Base
|
|
44
|
+
include ProjectIndexHelp
|
|
45
|
+
include IndexedMethodArity
|
|
46
|
+
|
|
47
|
+
MSG = 'Wrong number of arguments to `%<method>s` (given %<given>d, expected %<expected>s).'
|
|
48
|
+
|
|
49
|
+
def on_send(node)
|
|
50
|
+
return unless checkable_call?(node)
|
|
51
|
+
|
|
52
|
+
shape = resolved_signature_shape(node)
|
|
53
|
+
return unless shape
|
|
54
|
+
|
|
55
|
+
min, max, has_keywords = shape
|
|
56
|
+
given = positional_argument_count(node, has_keywords)
|
|
57
|
+
return if given.nil? || arity_satisfied?(given, min, max)
|
|
58
|
+
|
|
59
|
+
add_offense(node.loc.selector, message: message(node, given, min, max))
|
|
60
|
+
end
|
|
61
|
+
alias on_csend on_send
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def checkable_call?(node)
|
|
66
|
+
project_index && node.receiver&.const_type? && !node.method?(:new)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def message(node, given, min, max)
|
|
70
|
+
format(MSG, method: node.method_name, given: given, expected: expected_range(min, max))
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# `[min, max, has_keywords]` for the called singleton method, or nil when
|
|
74
|
+
# the receiver or its ancestry is not resolved.
|
|
75
|
+
def resolved_signature_shape(node)
|
|
76
|
+
declaration = resolve_constant_in_index(node.receiver)
|
|
77
|
+
return nil unless declaration.is_a?(Rubydex::Namespace)
|
|
78
|
+
return nil unless fully_resolved_index_ancestry?(declaration)
|
|
79
|
+
|
|
80
|
+
singleton_signature_shape(declaration, node.method_name)
|
|
81
|
+
rescue StandardError
|
|
82
|
+
nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# A call with an explicit receiver cannot reach a private method, so its
|
|
86
|
+
# signature says nothing about the call. This matters because `Object`
|
|
87
|
+
# closes the singleton ancestry of every constant, and a bare `def` at
|
|
88
|
+
# the top level of any file — what every block-based DSL produces — is a
|
|
89
|
+
# private instance method of `Object`.
|
|
90
|
+
def singleton_signature_shape(declaration, method_name)
|
|
91
|
+
member = indexed_singleton_member(declaration, "#{method_name}()")
|
|
92
|
+
return nil unless member&.public?
|
|
93
|
+
|
|
94
|
+
indexed_member_shape(member)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -106,6 +106,8 @@ module RuboCop
|
|
|
106
106
|
def traverse_node(node, &block)
|
|
107
107
|
# if the node is a block, any assignments are irrelevant
|
|
108
108
|
return if node.any_block_type?
|
|
109
|
+
# an assignment inside `defined?` is never executed
|
|
110
|
+
return if node.defined_type?
|
|
109
111
|
|
|
110
112
|
result = yield node if ASGN_TYPES.include?(node.type)
|
|
111
113
|
|