rubocop 1.88.2 → 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.
Files changed (103) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +67 -11
  3. data/lib/rubocop/comment_config.rb +35 -19
  4. data/lib/rubocop/config.rb +53 -0
  5. data/lib/rubocop/config_validator.rb +38 -1
  6. data/lib/rubocop/cop/badge.rb +8 -0
  7. data/lib/rubocop/cop/base.rb +6 -4
  8. data/lib/rubocop/cop/bundler.rb +19 -0
  9. data/lib/rubocop/cop/corrector.rb +1 -1
  10. data/lib/rubocop/cop/correctors/alignment_corrector.rb +55 -5
  11. data/lib/rubocop/cop/gemspec.rb +22 -0
  12. data/lib/rubocop/cop/generator/registration_injector.rb +188 -0
  13. data/lib/rubocop/cop/generator.rb +4 -0
  14. data/lib/rubocop/cop/layout/class_structure.rb +8 -2
  15. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +1 -1
  16. data/lib/rubocop/cop/layout/indentation_width.rb +3 -1
  17. data/lib/rubocop/cop/layout/leading_comment_space.rb +27 -0
  18. data/lib/rubocop/cop/layout/line_length.rb +85 -12
  19. data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +12 -2
  20. data/lib/rubocop/cop/layout/redundant_line_break.rb +77 -0
  21. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +33 -34
  22. data/lib/rubocop/cop/layout.rb +114 -0
  23. data/lib/rubocop/cop/lazy_loader.rb +41 -0
  24. data/lib/rubocop/cop/lint/ambiguous_assignment.rb +9 -7
  25. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +71 -0
  26. data/lib/rubocop/cop/lint/constant_reassignment.rb +1 -10
  27. data/lib/rubocop/cop/lint/constant_resolution.rb +25 -0
  28. data/lib/rubocop/cop/lint/deprecated_reference.rb +185 -0
  29. data/lib/rubocop/cop/lint/duplicate_methods.rb +202 -22
  30. data/lib/rubocop/cop/lint/inherit_exception.rb +53 -7
  31. data/lib/rubocop/cop/lint/literal_as_condition.rb +5 -0
  32. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +1 -1
  33. data/lib/rubocop/cop/lint/missing_super.rb +31 -1
  34. data/lib/rubocop/cop/lint/name_typo.rb +223 -0
  35. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +0 -4
  36. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +7 -0
  37. data/lib/rubocop/cop/lint/to_enum_arguments.rb +37 -1
  38. data/lib/rubocop/cop/lint/unused_private_method.rb +157 -0
  39. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +2 -0
  40. data/lib/rubocop/cop/lint/void.rb +17 -6
  41. data/lib/rubocop/cop/lint.rb +176 -0
  42. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +1 -1
  43. data/lib/rubocop/cop/metrics.rb +31 -0
  44. data/lib/rubocop/cop/migration/department_name.rb +0 -4
  45. data/lib/rubocop/cop/migration.rb +13 -0
  46. data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -5
  47. data/lib/rubocop/cop/mixin/endless_method_rewriter.rb +13 -5
  48. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +23 -6
  49. data/lib/rubocop/cop/mixin/project_index_help.rb +140 -0
  50. data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +171 -0
  51. data/lib/rubocop/cop/mixin/statement_modifier.rb +42 -6
  52. data/lib/rubocop/cop/mixin.rb +78 -77
  53. data/lib/rubocop/cop/naming/accessor_method_name.rb +24 -0
  54. data/lib/rubocop/cop/naming/predicate_prefix.rb +33 -5
  55. data/lib/rubocop/cop/naming/variable_number.rb +3 -1
  56. data/lib/rubocop/cop/naming.rb +31 -0
  57. data/lib/rubocop/cop/offense.rb +10 -6
  58. data/lib/rubocop/cop/registry.rb +153 -43
  59. data/lib/rubocop/cop/security.rb +19 -0
  60. data/lib/rubocop/cop/style/array_intersect.rb +24 -5
  61. data/lib/rubocop/cop/style/class_and_module_children.rb +78 -18
  62. data/lib/rubocop/cop/style/conditional_assignment.rb +22 -0
  63. data/lib/rubocop/cop/style/copyright.rb +6 -1
  64. data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +41 -3
  65. data/lib/rubocop/cop/style/documentation.rb +47 -0
  66. data/lib/rubocop/cop/style/endless_method.rb +0 -10
  67. data/lib/rubocop/cop/style/if_unless_modifier.rb +7 -48
  68. data/lib/rubocop/cop/style/lambda.rb +4 -0
  69. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +20 -3
  70. data/lib/rubocop/cop/style/missing_respond_to_missing.rb +30 -0
  71. data/lib/rubocop/cop/style/multiline_if_then.rb +7 -1
  72. data/lib/rubocop/cop/style/multiple_comparison.rb +33 -22
  73. data/lib/rubocop/cop/style/mutable_constant.rb +5 -0
  74. data/lib/rubocop/cop/style/negative_array_index.rb +3 -1
  75. data/lib/rubocop/cop/style/redundant_assignment.rb +11 -0
  76. data/lib/rubocop/cop/style/redundant_constant_base.rb +37 -1
  77. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +9 -0
  78. data/lib/rubocop/cop/style/redundant_format.rb +12 -3
  79. data/lib/rubocop/cop/style/redundant_line_continuation.rb +78 -135
  80. data/lib/rubocop/cop/style/redundant_parentheses.rb +63 -63
  81. data/lib/rubocop/cop/style/safe_navigation.rb +10 -0
  82. data/lib/rubocop/cop/style/sole_nested_conditional.rb +20 -3
  83. data/lib/rubocop/cop/style/static_class.rb +21 -0
  84. data/lib/rubocop/cop/style.rb +310 -0
  85. data/lib/rubocop/cop/team.rb +48 -3
  86. data/lib/rubocop/cop/variable_force/variable.rb +18 -6
  87. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  88. data/lib/rubocop/directive_comment.rb +2 -0
  89. data/lib/rubocop/feature_loader.rb +0 -6
  90. data/lib/rubocop/lsp/routes.rb +41 -3
  91. data/lib/rubocop/lsp/runtime.rb +4 -0
  92. data/lib/rubocop/lsp/server.rb +4 -0
  93. data/lib/rubocop/lsp/stdin_runner.rb +20 -0
  94. data/lib/rubocop/pending_cops_reporter.rb +1 -6
  95. data/lib/rubocop/project_index_loader.rb +16 -0
  96. data/lib/rubocop/result_cache.rb +6 -4
  97. data/lib/rubocop/rspec/shared_contexts.rb +14 -2
  98. data/lib/rubocop/runner.rb +45 -20
  99. data/lib/rubocop/server/cache.rb +10 -5
  100. data/lib/rubocop/target_ruby.rb +1 -1
  101. data/lib/rubocop/version.rb +1 -1
  102. data/lib/rubocop.rb +17 -626
  103. metadata +18 -3
@@ -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?(node, left)
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, node, left, right)
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?(node, token)
138
- processed_source.tokens_within(node)[index_for(node, token) + 1].line != token.line
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(node, token)
149
- processed_source.tokens_within(node).index(token)
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
- def issue_offenses(node, left, right, start_ok, end_ok)
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?(node, left)
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?(node, token)
169
- processed_source.tokens_within(node)[index_for(node, token) + 1].comment?
170
+ def next_to_comment?(tokens, token)
171
+ tokens[index_for(tokens, token) + 1].comment?
170
172
  end
171
173
 
172
- def compact_offenses(node, left, right, start_ok, end_ok)
173
- if qualifies_for_compact?(node, left, side: :left)
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?(node, left, side: :left)
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
- if qualifies_for_compact?(node, right)
181
+
182
+ if qualifies_for_compact?(tokens, right)
179
183
  compact_offense(node, right)
180
- elsif !multi_dimensional_array?(node, right)
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?(node, token, side: :right)
190
+ def qualifies_for_compact?(tokens, token, side: :right)
186
191
  if side == :right
187
- multi_dimensional_array?(node, token) && token.space_before?
192
+ multi_dimensional_array?(tokens, token) && token.space_before?
188
193
  else
189
- multi_dimensional_array?(node, token, side: :left) && token.space_after?
194
+ multi_dimensional_array?(tokens, token, side: :left) && token.space_after?
190
195
  end
191
196
  end
192
197
 
193
- def multi_dimensional_array?(node, token, side: :right)
198
+ def multi_dimensional_array?(tokens, token, side: :right)
194
199
  offset = side == :right ? -1 : +1
195
- i = index_for(node, token) + offset
196
- i += offset while processed_source.tokens_within(node)[i].new_line?
200
+ i = index_for(tokens, token) + offset
201
+ i += offset while tokens[i].new_line?
197
202
  if side == :right
198
- processed_source.tokens_within(node)[i].right_bracket?
203
+ tokens[i].right_bracket?
199
204
  else
200
- processed_source.tokens_within(node)[i].left_bracket?
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, node, left, right)
219
- if multi_dimensional_array?(node, left, side: :left)
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?(node, right)
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
- def on_asgn(node)
31
- return unless (rhs = node.expression)
29
+ alias on_csend on_send
30
+
31
+ private
32
32
 
33
- range = range_between(node.loc.operator.end_pos - 1, rhs.source_range.begin_pos + 1)
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)