rubocop 1.89.0 → 1.90.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 +74 -7
- data/exe/rubocop +1 -1
- data/lib/rubocop/cached_data.rb +7 -2
- data/lib/rubocop/cli/command/execute_runner.rb +31 -0
- data/lib/rubocop/cli.rb +2 -4
- data/lib/rubocop/comment_config/directive_range.rb +32 -0
- data/lib/rubocop/comment_config/disable_next.rb +97 -0
- data/lib/rubocop/comment_config.rb +46 -30
- data/lib/rubocop/config.rb +23 -5
- data/lib/rubocop/config_loader.rb +1 -2
- data/lib/rubocop/config_loader_resolver.rb +1 -2
- data/lib/rubocop/config_validator.rb +1 -2
- data/lib/rubocop/cop/autocorrect_logic.rb +8 -0
- data/lib/rubocop/cop/base.rb +34 -2
- data/lib/rubocop/cop/corrector.rb +3 -3
- 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/layout/block_alignment.rb +1 -2
- data/lib/rubocop/cop/layout/class_structure.rb +102 -26
- 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/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/line_continuation_spacing.rb +1 -2
- data/lib/rubocop/cop/layout/line_length.rb +1 -2
- 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_operators.rb +7 -0
- 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 +89 -0
- data/lib/rubocop/cop/lint/constant_reassignment.rb +17 -11
- data/lib/rubocop/cop/lint/cop_directive_syntax.rb +84 -8
- data/lib/rubocop/cop/lint/deprecated_reference.rb +11 -2
- data/lib/rubocop/cop/lint/duplicate_methods.rb +112 -14
- 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 +3 -6
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +3 -6
- 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/out_of_range_regexp_ref.rb +15 -0
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +98 -19
- 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 +2 -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/comments_help.rb +1 -2
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +1 -2
- data/lib/rubocop/cop/mixin/hash_subset.rb +1 -2
- 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 +51 -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 +4 -0
- data/lib/rubocop/cop/naming/binary_operator_parameter_name.rb +16 -0
- 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 +34 -10
- data/lib/rubocop/cop/style/accessor_grouping.rb +1 -2
- data/lib/rubocop/cop/style/arguments_forwarding.rb +3 -6
- 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 +2 -4
- 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 +1 -2
- data/lib/rubocop/cop/style/copyright.rb +1 -2
- data/lib/rubocop/cop/style/directive_scope.rb +171 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +36 -10
- data/lib/rubocop/cop/style/empty_case_condition.rb +1 -2
- data/lib/rubocop/cop/style/format_string_token.rb +1 -2
- data/lib/rubocop/cop/style/guard_clause.rb +1 -2
- data/lib/rubocop/cop/style/hash_each_methods.rb +1 -2
- 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/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_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/nil_comparison.rb +1 -2
- 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/redundant_assignment.rb +1 -2
- data/lib/rubocop/cop/style/redundant_condition.rb +2 -4
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +1 -2
- data/lib/rubocop/cop/style/redundant_each.rb +1 -2
- data/lib/rubocop/cop/style/redundant_format.rb +2 -4
- data/lib/rubocop/cop/style/redundant_parentheses.rb +2 -4
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +1 -2
- 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_do_end_block.rb +1 -2
- data/lib/rubocop/cop/style/sole_nested_conditional.rb +1 -2
- 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/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/yoda_condition.rb +1 -2
- data/lib/rubocop/cop/style.rb +2 -0
- data/lib/rubocop/cop/team.rb +11 -3
- data/lib/rubocop/cop/util.rb +2 -4
- data/lib/rubocop/cop/variable_force/variable.rb +1 -2
- data/lib/rubocop/cops_documentation_generator.rb +3 -6
- data/lib/rubocop/directive_comment.rb +37 -3
- data/lib/rubocop/formatter/emacs_style_formatter.rb +3 -1
- 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/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/lockfile.rb +18 -0
- data/lib/rubocop/lsp/diagnostic.rb +4 -8
- data/lib/rubocop/lsp/routes.rb +48 -19
- data/lib/rubocop/mcp/server.rb +1 -2
- data/lib/rubocop/options.rb +10 -6
- data/lib/rubocop/path_util.rb +1 -2
- data/lib/rubocop/plugin/configuration_integrator.rb +6 -3
- data/lib/rubocop/rake_task.rb +1 -2
- data/lib/rubocop/rspec/expect_offense.rb +2 -4
- data/lib/rubocop/rspec/shared_contexts.rb +4 -2
- data/lib/rubocop/runner.rb +26 -23
- 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/version.rb +3 -5
- data/lib/rubocop.rb +3 -0
- data/lib/ruby_lsp/rubocop/addon.rb +1 -2
- metadata +13 -5
|
@@ -5,6 +5,14 @@ module RuboCop
|
|
|
5
5
|
module Layout
|
|
6
6
|
# Checks for a newline after the final magic comment.
|
|
7
7
|
#
|
|
8
|
+
# `NumberOfEmptyLines` configures the minimum number of empty lines required. Set it
|
|
9
|
+
# to `2` when using YARD, which otherwise treats the magic comments as documentation
|
|
10
|
+
# for the first module or class in the file.
|
|
11
|
+
#
|
|
12
|
+
# NOTE: `Layout/EmptyLines` has to be disabled for values greater than `1`, as it
|
|
13
|
+
# removes the extra empty lines this cop adds, and autocorrecting with both enabled
|
|
14
|
+
# loops between them.
|
|
15
|
+
#
|
|
8
16
|
# @example
|
|
9
17
|
# # good
|
|
10
18
|
# # frozen_string_literal: true
|
|
@@ -20,30 +28,80 @@ module RuboCop
|
|
|
20
28
|
# class Person
|
|
21
29
|
# # Some code
|
|
22
30
|
# end
|
|
31
|
+
#
|
|
32
|
+
# @example NumberOfEmptyLines: 1 (default)
|
|
33
|
+
# # good
|
|
34
|
+
# # frozen_string_literal: true
|
|
35
|
+
#
|
|
36
|
+
# class Person
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# @example NumberOfEmptyLines: 2
|
|
40
|
+
# # bad
|
|
41
|
+
# # frozen_string_literal: true
|
|
42
|
+
#
|
|
43
|
+
# class Person
|
|
44
|
+
# end
|
|
45
|
+
#
|
|
46
|
+
# # good
|
|
47
|
+
# # frozen_string_literal: true
|
|
48
|
+
#
|
|
49
|
+
#
|
|
50
|
+
# class Person
|
|
51
|
+
# end
|
|
23
52
|
class EmptyLineAfterMagicComment < Base
|
|
24
53
|
include RangeHelp
|
|
25
54
|
extend AutoCorrector
|
|
26
55
|
|
|
27
|
-
MSG = '
|
|
56
|
+
MSG = 'Expected at least %<expected>d empty %<lines>s after magic comments; found ' \
|
|
57
|
+
'%<actual>d.'
|
|
58
|
+
|
|
59
|
+
def validate_config
|
|
60
|
+
return if number_of_empty_lines.is_a?(Integer) && number_of_empty_lines.positive?
|
|
61
|
+
|
|
62
|
+
raise ValidationError,
|
|
63
|
+
'The `Layout/EmptyLineAfterMagicComment` cop only accepts a positive ' \
|
|
64
|
+
'integer for its `NumberOfEmptyLines` configuration parameter, but ' \
|
|
65
|
+
"`#{number_of_empty_lines.inspect}` was given."
|
|
66
|
+
end
|
|
28
67
|
|
|
29
68
|
def on_new_investigation
|
|
30
69
|
return unless (last_magic_comment = last_magic_comment(processed_source))
|
|
31
|
-
|
|
32
|
-
|
|
70
|
+
|
|
71
|
+
actual = empty_lines_after(last_magic_comment)
|
|
72
|
+
# Trailing empty lines are `Layout/TrailingEmptyLines`' responsibility.
|
|
73
|
+
return unless processed_source[last_magic_comment.loc.line + actual]
|
|
74
|
+
return if actual >= number_of_empty_lines
|
|
33
75
|
|
|
34
76
|
offending_range = offending_range(last_magic_comment)
|
|
35
77
|
|
|
36
|
-
add_offense(offending_range) do |corrector|
|
|
37
|
-
corrector.insert_before(offending_range, "\n")
|
|
78
|
+
add_offense(offending_range, message: message(actual)) do |corrector|
|
|
79
|
+
corrector.insert_before(offending_range, "\n" * (number_of_empty_lines - actual))
|
|
38
80
|
end
|
|
39
81
|
end
|
|
40
82
|
|
|
41
83
|
private
|
|
42
84
|
|
|
85
|
+
def message(actual)
|
|
86
|
+
format(MSG, expected: number_of_empty_lines, actual: actual,
|
|
87
|
+
lines: number_of_empty_lines == 1 ? 'line' : 'lines')
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def empty_lines_after(last_magic_comment)
|
|
91
|
+
first_line = last_magic_comment.loc.line
|
|
92
|
+
count = 0
|
|
93
|
+
count += 1 while (line = processed_source[first_line + count]) && line.strip.empty?
|
|
94
|
+
count
|
|
95
|
+
end
|
|
96
|
+
|
|
43
97
|
def offending_range(last_magic_comment)
|
|
44
98
|
source_range(processed_source.buffer, last_magic_comment.loc.line + 1, 0)
|
|
45
99
|
end
|
|
46
100
|
|
|
101
|
+
def number_of_empty_lines
|
|
102
|
+
cop_config.fetch('NumberOfEmptyLines', 1)
|
|
103
|
+
end
|
|
104
|
+
|
|
47
105
|
# Find the last magic comment in the source file.
|
|
48
106
|
#
|
|
49
107
|
# Take all comments that precede the first line of code (or just take
|
|
@@ -123,7 +123,7 @@ module RuboCop
|
|
|
123
123
|
AlignmentCorrector.align_end(corrector, processed_source, node, alignment_node(node))
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
-
# rubocop:disable Metrics/CyclomaticComplexity
|
|
126
|
+
# rubocop:disable-next Metrics/CyclomaticComplexity
|
|
127
127
|
def check_assignment(node, rhs)
|
|
128
128
|
# If there are method calls chained to the right hand side of the
|
|
129
129
|
# assignment, we let rhs be the receiver of those method calls before
|
|
@@ -139,7 +139,6 @@ module RuboCop
|
|
|
139
139
|
|
|
140
140
|
check_asgn_alignment(node, rhs)
|
|
141
141
|
end
|
|
142
|
-
# rubocop:enable Metrics/CyclomaticComplexity
|
|
143
142
|
|
|
144
143
|
def check_asgn_alignment(outer_node, inner_node)
|
|
145
144
|
align_with = {
|
|
@@ -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
|
|
@@ -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)
|
|
@@ -328,7 +328,7 @@ module RuboCop
|
|
|
328
328
|
[max - indentation_difference(line), 0].max
|
|
329
329
|
end
|
|
330
330
|
|
|
331
|
-
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
331
|
+
# rubocop:disable-next Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
332
332
|
def check_line(line, line_index)
|
|
333
333
|
return if line_length(line) <= max
|
|
334
334
|
return if allowed_line?(line, line_index)
|
|
@@ -348,7 +348,6 @@ module RuboCop
|
|
|
348
348
|
|
|
349
349
|
register_offense(excess_range(nil, line, line_index), line, line_index)
|
|
350
350
|
end
|
|
351
|
-
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
352
351
|
|
|
353
352
|
def allowed_line?(line, line_index)
|
|
354
353
|
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
|
|
|
@@ -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
|
|
|
@@ -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,89 @@
|
|
|
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.
|
|
25
|
+
#
|
|
26
|
+
# @example
|
|
27
|
+
# # Given the project defines:
|
|
28
|
+
# # class Report
|
|
29
|
+
# # def self.generate(source, format); end
|
|
30
|
+
# # end
|
|
31
|
+
#
|
|
32
|
+
# # bad
|
|
33
|
+
# Report.generate(source)
|
|
34
|
+
#
|
|
35
|
+
# # bad
|
|
36
|
+
# Report.generate(source, :pdf, :extra)
|
|
37
|
+
#
|
|
38
|
+
# # good
|
|
39
|
+
# Report.generate(source, :pdf)
|
|
40
|
+
#
|
|
41
|
+
class ArgumentMismatch < Base
|
|
42
|
+
include ProjectIndexHelp
|
|
43
|
+
include IndexedMethodArity
|
|
44
|
+
|
|
45
|
+
MSG = 'Wrong number of arguments to `%<method>s` (given %<given>d, expected %<expected>s).'
|
|
46
|
+
|
|
47
|
+
def on_send(node)
|
|
48
|
+
return unless project_index
|
|
49
|
+
return unless node.receiver&.const_type?
|
|
50
|
+
|
|
51
|
+
shape = resolved_signature_shape(node)
|
|
52
|
+
return unless shape
|
|
53
|
+
|
|
54
|
+
min, max, has_keywords = shape
|
|
55
|
+
given = positional_argument_count(node, has_keywords)
|
|
56
|
+
return if given.nil? || arity_satisfied?(given, min, max)
|
|
57
|
+
|
|
58
|
+
add_offense(node.loc.selector, message: message(node, given, min, max))
|
|
59
|
+
end
|
|
60
|
+
alias on_csend on_send
|
|
61
|
+
|
|
62
|
+
private
|
|
63
|
+
|
|
64
|
+
def message(node, given, min, max)
|
|
65
|
+
format(MSG, method: node.method_name, given: given, expected: expected_range(min, max))
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# `[min, max, has_keywords]` for the called singleton method, or nil when
|
|
69
|
+
# the receiver or its ancestry is not resolved.
|
|
70
|
+
def resolved_signature_shape(node)
|
|
71
|
+
declaration = resolve_constant_in_index(node.receiver)
|
|
72
|
+
return nil unless declaration.is_a?(Rubydex::Namespace)
|
|
73
|
+
return nil unless fully_resolved_index_ancestry?(declaration)
|
|
74
|
+
|
|
75
|
+
singleton_signature_shape(declaration, node.method_name)
|
|
76
|
+
rescue StandardError
|
|
77
|
+
nil
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def singleton_signature_shape(declaration, method_name)
|
|
81
|
+
member = indexed_singleton_member(declaration, "#{method_name}()")
|
|
82
|
+
return nil unless member
|
|
83
|
+
|
|
84
|
+
indexed_member_shape(member)
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -175,10 +175,19 @@ module RuboCop
|
|
|
175
175
|
end
|
|
176
176
|
|
|
177
177
|
def ancestor_namespaces(node)
|
|
178
|
-
node
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
.
|
|
178
|
+
ancestors = node.each_ancestor(:class, :module).reverse_each
|
|
179
|
+
|
|
180
|
+
ancestors.with_object([]) do |ancestor, namespaces|
|
|
181
|
+
append_namespaces(namespaces, ancestor.identifier)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Compact definitions (e.g. `module A::B`) contribute every path segment,
|
|
186
|
+
# and an absolute one (e.g. `module ::A::B`) discards the enclosing namespaces.
|
|
187
|
+
def append_namespaces(namespaces, identifier)
|
|
188
|
+
namespaces.clear if identifier.absolute?
|
|
189
|
+
namespaces.concat(identifier_namespaces(identifier))
|
|
190
|
+
namespaces << identifier.short_name
|
|
182
191
|
end
|
|
183
192
|
|
|
184
193
|
def unconditional_definition?(node)
|
|
@@ -188,14 +197,11 @@ module RuboCop
|
|
|
188
197
|
end
|
|
189
198
|
|
|
190
199
|
def definition_name(node)
|
|
191
|
-
|
|
200
|
+
namespaces = ancestor_namespaces(node)
|
|
201
|
+
append_namespaces(namespaces, node.identifier)
|
|
202
|
+
constant = namespaces.pop
|
|
192
203
|
|
|
193
|
-
|
|
194
|
-
fully_qualified_name_for([], identifier.short_name)
|
|
195
|
-
else
|
|
196
|
-
namespaces = ancestor_namespaces(node) + identifier_namespaces(identifier)
|
|
197
|
-
fully_qualified_name_for(namespaces, identifier.short_name)
|
|
198
|
-
end
|
|
204
|
+
fully_qualified_name_for(namespaces, constant)
|
|
199
205
|
end
|
|
200
206
|
|
|
201
207
|
def identifier_namespaces(identifier)
|