rubocop 1.75.1 → 1.77.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 (163) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -14
  3. data/config/default.yml +103 -25
  4. data/config/obsoletion.yml +6 -3
  5. data/lib/rubocop/config_validator.rb +6 -6
  6. data/lib/rubocop/cop/autocorrect_logic.rb +18 -10
  7. data/lib/rubocop/cop/bundler/ordered_gems.rb +1 -1
  8. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +5 -2
  9. data/lib/rubocop/cop/gemspec/attribute_assignment.rb +91 -0
  10. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +37 -15
  11. data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +1 -1
  12. data/lib/rubocop/cop/gemspec/require_mfa.rb +15 -1
  13. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -1
  14. data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +4 -4
  15. data/lib/rubocop/cop/internal_affairs/node_pattern_groups.rb +2 -0
  16. data/lib/rubocop/cop/internal_affairs/undefined_config.rb +6 -1
  17. data/lib/rubocop/cop/layout/block_alignment.rb +1 -2
  18. data/lib/rubocop/cop/layout/class_structure.rb +35 -0
  19. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +1 -1
  20. data/lib/rubocop/cop/layout/def_end_alignment.rb +1 -1
  21. data/lib/rubocop/cop/layout/empty_line_after_guard_clause.rb +1 -1
  22. data/lib/rubocop/cop/layout/empty_line_between_defs.rb +2 -2
  23. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +7 -3
  24. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -1
  25. data/lib/rubocop/cop/layout/hash_alignment.rb +2 -2
  26. data/lib/rubocop/cop/layout/leading_comment_space.rb +13 -1
  27. data/lib/rubocop/cop/layout/line_length.rb +26 -5
  28. data/lib/rubocop/cop/layout/multiline_operation_indentation.rb +1 -1
  29. data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +2 -4
  30. data/lib/rubocop/cop/layout/space_after_semicolon.rb +10 -0
  31. data/lib/rubocop/cop/layout/space_before_brackets.rb +5 -38
  32. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +12 -3
  33. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +3 -0
  34. data/lib/rubocop/cop/lint/ambiguous_range.rb +5 -0
  35. data/lib/rubocop/cop/lint/array_literal_in_regexp.rb +2 -3
  36. data/lib/rubocop/cop/lint/boolean_symbol.rb +1 -1
  37. data/lib/rubocop/cop/lint/circular_argument_reference.rb +2 -5
  38. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +1 -1
  39. data/lib/rubocop/cop/lint/deprecated_open_ssl_constant.rb +1 -1
  40. data/lib/rubocop/cop/lint/duplicate_methods.rb +86 -5
  41. data/lib/rubocop/cop/lint/empty_interpolation.rb +3 -1
  42. data/lib/rubocop/cop/lint/float_comparison.rb +31 -4
  43. data/lib/rubocop/cop/lint/identity_comparison.rb +19 -15
  44. data/lib/rubocop/cop/lint/literal_as_condition.rb +31 -25
  45. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +1 -1
  46. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  47. data/lib/rubocop/cop/lint/non_local_exit_from_iterator.rb +2 -2
  48. data/lib/rubocop/cop/lint/or_assignment_to_constant.rb +1 -1
  49. data/lib/rubocop/cop/lint/redundant_regexp_quantifiers.rb +1 -1
  50. data/lib/rubocop/cop/lint/redundant_type_conversion.rb +7 -4
  51. data/lib/rubocop/cop/lint/return_in_void_context.rb +7 -2
  52. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +4 -4
  53. data/lib/rubocop/cop/lint/self_assignment.rb +25 -0
  54. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +5 -0
  55. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  56. data/lib/rubocop/cop/lint/to_enum_arguments.rb +1 -1
  57. data/lib/rubocop/cop/lint/top_level_return_with_argument.rb +1 -1
  58. data/lib/rubocop/cop/lint/useless_access_modifier.rb +29 -4
  59. data/lib/rubocop/cop/lint/useless_assignment.rb +2 -0
  60. data/lib/rubocop/cop/lint/useless_default_value_argument.rb +90 -0
  61. data/lib/rubocop/cop/lint/useless_or.rb +98 -0
  62. data/lib/rubocop/cop/lint/useless_rescue.rb +1 -1
  63. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +3 -3
  64. data/lib/rubocop/cop/lint/void.rb +2 -2
  65. data/lib/rubocop/cop/message_annotator.rb +7 -3
  66. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  67. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  68. data/lib/rubocop/cop/mixin/check_line_breakable.rb +2 -2
  69. data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +1 -1
  70. data/lib/rubocop/cop/mixin/def_node.rb +1 -1
  71. data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +1 -1
  72. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +1 -2
  73. data/lib/rubocop/cop/mixin/gemspec_help.rb +22 -0
  74. data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +15 -14
  75. data/lib/rubocop/cop/mixin/line_length_help.rb +24 -8
  76. data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +2 -0
  77. data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
  78. data/lib/rubocop/cop/mixin/trailing_comma.rb +9 -5
  79. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  80. data/lib/rubocop/cop/naming/memoized_instance_variable_name.rb +1 -1
  81. data/lib/rubocop/cop/naming/method_name.rb +1 -1
  82. data/lib/rubocop/cop/naming/predicate_method.rb +281 -0
  83. data/lib/rubocop/cop/naming/{predicate_name.rb → predicate_prefix.rb} +4 -4
  84. data/lib/rubocop/cop/style/access_modifier_declarations.rb +32 -10
  85. data/lib/rubocop/cop/style/arguments_forwarding.rb +8 -5
  86. data/lib/rubocop/cop/style/case_like_if.rb +1 -1
  87. data/lib/rubocop/cop/style/class_and_module_children.rb +19 -3
  88. data/lib/rubocop/cop/style/class_equality_comparison.rb +1 -1
  89. data/lib/rubocop/cop/style/collection_querying.rb +167 -0
  90. data/lib/rubocop/cop/style/command_literal.rb +1 -1
  91. data/lib/rubocop/cop/style/commented_keyword.rb +2 -2
  92. data/lib/rubocop/cop/style/comparable_between.rb +5 -2
  93. data/lib/rubocop/cop/style/conditional_assignment.rb +18 -4
  94. data/lib/rubocop/cop/style/data_inheritance.rb +7 -0
  95. data/lib/rubocop/cop/style/def_with_parentheses.rb +18 -5
  96. data/lib/rubocop/cop/style/double_negation.rb +1 -1
  97. data/lib/rubocop/cop/style/empty_literal.rb +4 -0
  98. data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +100 -0
  99. data/lib/rubocop/cop/style/eval_with_location.rb +3 -3
  100. data/lib/rubocop/cop/style/explicit_block_argument.rb +2 -2
  101. data/lib/rubocop/cop/style/exponential_notation.rb +2 -2
  102. data/lib/rubocop/cop/style/fetch_env_var.rb +32 -6
  103. data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +3 -2
  104. data/lib/rubocop/cop/style/global_std_stream.rb +3 -0
  105. data/lib/rubocop/cop/style/hash_conversion.rb +12 -3
  106. data/lib/rubocop/cop/style/hash_fetch_chain.rb +0 -1
  107. data/lib/rubocop/cop/style/hash_syntax.rb +3 -0
  108. data/lib/rubocop/cop/style/hash_transform_keys.rb +2 -2
  109. data/lib/rubocop/cop/style/hash_transform_values.rb +2 -2
  110. data/lib/rubocop/cop/style/identical_conditional_branches.rb +3 -3
  111. data/lib/rubocop/cop/style/if_unless_modifier.rb +33 -6
  112. data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -7
  113. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +1 -1
  114. data/lib/rubocop/cop/style/it_block_parameter.rb +33 -14
  115. data/lib/rubocop/cop/style/keyword_parameters_order.rb +1 -1
  116. data/lib/rubocop/cop/style/lambda_call.rb +7 -2
  117. data/lib/rubocop/cop/style/map_into_array.rb +3 -1
  118. data/lib/rubocop/cop/style/map_to_hash.rb +11 -0
  119. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +6 -3
  120. data/lib/rubocop/cop/style/min_max_comparison.rb +13 -5
  121. data/lib/rubocop/cop/style/multiline_if_modifier.rb +2 -0
  122. data/lib/rubocop/cop/style/percent_q_literals.rb +1 -1
  123. data/lib/rubocop/cop/style/redundant_array_flatten.rb +50 -0
  124. data/lib/rubocop/cop/style/redundant_condition.rb +13 -1
  125. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  126. data/lib/rubocop/cop/style/redundant_format.rb +6 -1
  127. data/lib/rubocop/cop/style/redundant_interpolation.rb +1 -1
  128. data/lib/rubocop/cop/style/redundant_line_continuation.rb +0 -3
  129. data/lib/rubocop/cop/style/redundant_parentheses.rb +41 -3
  130. data/lib/rubocop/cop/style/redundant_self.rb +8 -5
  131. data/lib/rubocop/cop/style/regexp_literal.rb +1 -1
  132. data/lib/rubocop/cop/style/return_nil.rb +2 -2
  133. data/lib/rubocop/cop/style/safe_navigation.rb +42 -14
  134. data/lib/rubocop/cop/style/sole_nested_conditional.rb +6 -3
  135. data/lib/rubocop/cop/style/string_concatenation.rb +1 -2
  136. data/lib/rubocop/cop/style/struct_inheritance.rb +8 -1
  137. data/lib/rubocop/cop/style/super_arguments.rb +1 -2
  138. data/lib/rubocop/cop/style/symbol_proc.rb +1 -1
  139. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +7 -1
  140. data/lib/rubocop/cop/style/trailing_comma_in_array_literal.rb +1 -1
  141. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  142. data/lib/rubocop/cop/style/trailing_comma_in_hash_literal.rb +1 -1
  143. data/lib/rubocop/cop/team.rb +1 -1
  144. data/lib/rubocop/cop/util.rb +1 -1
  145. data/lib/rubocop/cop/variable_force/assignment.rb +7 -3
  146. data/lib/rubocop/cop/variable_force/variable.rb +1 -1
  147. data/lib/rubocop/cops_documentation_generator.rb +6 -2
  148. data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
  149. data/lib/rubocop/formatter/fuubar_style_formatter.rb +1 -1
  150. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  151. data/lib/rubocop/formatter/offense_count_formatter.rb +1 -1
  152. data/lib/rubocop/formatter/pacman_formatter.rb +1 -1
  153. data/lib/rubocop/lsp/diagnostic.rb +4 -4
  154. data/lib/rubocop/magic_comment.rb +8 -0
  155. data/lib/rubocop/rspec/cop_helper.rb +2 -2
  156. data/lib/rubocop/rspec/expect_offense.rb +9 -3
  157. data/lib/rubocop/rspec/shared_contexts.rb +1 -2
  158. data/lib/rubocop/server/cache.rb +13 -10
  159. data/lib/rubocop/target_finder.rb +6 -2
  160. data/lib/rubocop/version.rb +1 -1
  161. data/lib/rubocop.rb +8 -1
  162. data/lib/ruby_lsp/rubocop/addon.rb +2 -2
  163. metadata +14 -7
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gemspec
6
+ # Use consistent style for Gemspec attributes assignment.
7
+ #
8
+ # @example
9
+ #
10
+ # # bad
11
+ # # This example uses two styles for assignment of metadata attribute.
12
+ # Gem::Specification.new do |spec|
13
+ # spec.metadata = { 'key' => 'value' }
14
+ # spec.metadata['another-key'] = 'another-value'
15
+ # end
16
+ #
17
+ # # good
18
+ # Gem::Specification.new do |spec|
19
+ # spec.metadata['key'] = 'value'
20
+ # spec.metadata['another-key'] = 'another-value'
21
+ # end
22
+ #
23
+ # # good
24
+ # Gem::Specification.new do |spec|
25
+ # spec.metadata = { 'key' => 'value', 'another-key' => 'another-value' }
26
+ # end
27
+ #
28
+ # # bad
29
+ # # This example uses two styles for assignment of authors attribute.
30
+ # Gem::Specification.new do |spec|
31
+ # spec.authors = %w[author-0 author-1]
32
+ # spec.authors[2] = 'author-2'
33
+ # end
34
+ #
35
+ # # good
36
+ # Gem::Specification.new do |spec|
37
+ # spec.authors = %w[author-0 author-1 author-2]
38
+ # end
39
+ #
40
+ # # good
41
+ # Gem::Specification.new do |spec|
42
+ # spec.authors[0] = 'author-0'
43
+ # spec.authors[1] = 'author-1'
44
+ # spec.authors[2] = 'author-2'
45
+ # end
46
+ #
47
+ # # good
48
+ # # This example uses consistent assignment per attribute,
49
+ # # even though two different styles are used overall.
50
+ # Gem::Specification.new do |spec|
51
+ # spec.metadata = { 'key' => 'value' }
52
+ # spec.authors[0] = 'author-0'
53
+ # spec.authors[1] = 'author-1'
54
+ # spec.authors[2] = 'author-2'
55
+ # end
56
+ #
57
+ class AttributeAssignment < Base
58
+ include GemspecHelp
59
+
60
+ MSG = 'Use consistent style for Gemspec attributes assignment.'
61
+
62
+ def on_new_investigation
63
+ return if processed_source.blank?
64
+
65
+ assignments = source_assignments(processed_source.ast)
66
+ indexed_assignments = source_indexed_assignments(processed_source.ast)
67
+
68
+ assignments.keys.intersection(indexed_assignments.keys).each do |attribute|
69
+ indexed_assignments[attribute].each do |node|
70
+ add_offense(node)
71
+ end
72
+ end
73
+ end
74
+
75
+ private
76
+
77
+ def source_assignments(ast)
78
+ assignment_method_declarations(ast)
79
+ .select(&:assignment_method?)
80
+ .group_by(&:method_name)
81
+ .transform_keys { |method_name| method_name.to_s.delete_suffix('=').to_sym }
82
+ end
83
+
84
+ def source_indexed_assignments(ast)
85
+ indexed_assignment_method_declarations(ast)
86
+ .group_by { |node| node.children.first.method_name }
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -6,10 +6,11 @@ module RuboCop
6
6
  # An attribute assignment method calls should be listed only once
7
7
  # in a gemspec.
8
8
  #
9
- # Assigning to an attribute with the same name using `spec.foo =` will be
10
- # an unintended usage. On the other hand, duplication of methods such
11
- # as `spec.requirements`, `spec.add_runtime_dependency`, and others are
12
- # permitted because it is the intended use of appending values.
9
+ # Assigning to an attribute with the same name using `spec.foo =` or
10
+ # `spec.attribute#[]=` will be an unintended usage. On the other hand,
11
+ # duplication of methods such # as `spec.requirements`,
12
+ # `spec.add_runtime_dependency`, and others are permitted because it is
13
+ # the intended use of appending values.
13
14
  #
14
15
  # @example
15
16
  # # bad
@@ -34,6 +35,18 @@ module RuboCop
34
35
  # spec.add_dependency('parallel', '~> 1.10')
35
36
  # spec.add_dependency('parser', '>= 2.3.3.1', '< 3.0')
36
37
  # end
38
+ #
39
+ # # bad
40
+ # Gem::Specification.new do |spec|
41
+ # spec.metadata["key"] = "value"
42
+ # spec.metadata["key"] = "value"
43
+ # end
44
+ #
45
+ # # good
46
+ # Gem::Specification.new do |spec|
47
+ # spec.metadata["key"] = "value"
48
+ # end
49
+ #
37
50
  class DuplicatedAssignment < Base
38
51
  include RangeHelp
39
52
  include GemspecHelp
@@ -41,15 +54,16 @@ module RuboCop
41
54
  MSG = '`%<assignment>s` method calls already given on line ' \
42
55
  '%<line_of_first_occurrence>d of the gemspec.'
43
56
 
44
- # @!method assignment_method_declarations(node)
45
- def_node_search :assignment_method_declarations, <<~PATTERN
46
- (send
47
- (lvar #match_block_variable_name?) _ ...)
48
- PATTERN
49
-
50
57
  def on_new_investigation
51
58
  return if processed_source.blank?
52
59
 
60
+ process_assignment_method_nodes
61
+ process_indexed_assignment_method_nodes
62
+ end
63
+
64
+ private
65
+
66
+ def process_assignment_method_nodes
53
67
  duplicated_assignment_method_nodes.each do |nodes|
54
68
  nodes[1..].each do |node|
55
69
  register_offense(node, node.method_name, nodes.first.first_line)
@@ -57,11 +71,12 @@ module RuboCop
57
71
  end
58
72
  end
59
73
 
60
- private
61
-
62
- def match_block_variable_name?(receiver_name)
63
- gem_specification(processed_source.ast) do |block_variable_name|
64
- return block_variable_name == receiver_name
74
+ def process_indexed_assignment_method_nodes
75
+ duplicated_indexed_assignment_method_nodes.each do |nodes|
76
+ nodes[1..].each do |node|
77
+ assignment = "#{node.children.first.method_name}[#{node.first_argument.source}]="
78
+ register_offense(node, assignment, nodes.first.first_line)
79
+ end
65
80
  end
66
81
  end
67
82
 
@@ -73,6 +88,13 @@ module RuboCop
73
88
  .select { |nodes| nodes.size > 1 }
74
89
  end
75
90
 
91
+ def duplicated_indexed_assignment_method_nodes
92
+ indexed_assignment_method_declarations(processed_source.ast)
93
+ .group_by { |node| [node.children.first.method_name, node.first_argument] }
94
+ .values
95
+ .select { |nodes| nodes.size > 1 }
96
+ end
97
+
76
98
  def register_offense(node, assignment, line_of_first_occurrence)
77
99
  line_range = node.loc.column...node.loc.last_column
78
100
  offense_location = source_range(processed_source.buffer, node.first_line, line_range)
@@ -71,7 +71,7 @@ module RuboCop
71
71
 
72
72
  dependency_declarations(processed_source.ast)
73
73
  .each_cons(2) do |previous, current|
74
- next unless consecutive_lines(previous, current)
74
+ next unless consecutive_lines?(previous, current)
75
75
  next unless case_insensitive_out_of_order?(gem_name(current), gem_name(previous))
76
76
  next unless get_dependency_name(previous) == get_dependency_name(current)
77
77
 
@@ -74,6 +74,14 @@ module RuboCop
74
74
  }
75
75
  PATTERN
76
76
 
77
+ # @!method metadata_assignment(node)
78
+ def_node_search :metadata_assignment, <<~PATTERN
79
+ `{
80
+ (send _ :metadata= _)
81
+ (send (send _ :metadata) :[]= (str _) _)
82
+ }
83
+ PATTERN
84
+
77
85
  # @!method rubygems_mfa_required(node)
78
86
  def_node_search :rubygems_mfa_required, <<~PATTERN
79
87
  (pair (str "rubygems_mfa_required") $_)
@@ -131,9 +139,15 @@ module RuboCop
131
139
  end
132
140
 
133
141
  def insert_mfa_required(corrector, node, block_var)
134
- corrector.insert_before(node.loc.end, <<~RUBY)
142
+ require_mfa_directive = <<~RUBY.strip
135
143
  #{block_var}.metadata['rubygems_mfa_required'] = 'true'
136
144
  RUBY
145
+
146
+ if (last_assignment = metadata_assignment(processed_source.ast).to_a.last)
147
+ corrector.insert_after(last_assignment, "\n#{require_mfa_directive}")
148
+ else
149
+ corrector.insert_before(node.loc.end, "#{require_mfa_directive}\n")
150
+ end
137
151
  end
138
152
 
139
153
  def change_value(corrector, value)
@@ -69,7 +69,7 @@ module RuboCop
69
69
  # @!method offense_example(node)
70
70
  def_node_matcher :offense_example, <<~PATTERN
71
71
  (block
72
- (send _ {:it :specify} $...)
72
+ (send _ {:it :specify :xit :fit} $...)
73
73
  _args
74
74
  `(send nil? %RESTRICT_ON_SEND ...)
75
75
  )
@@ -110,8 +110,8 @@ module RuboCop
110
110
  def directive_offense_type(directive, actual_name)
111
111
  return :missing_directive unless directive
112
112
 
113
- return :wrong_scope if wrong_scope(directive, actual_name)
114
- return :no_scope if no_scope(directive, actual_name)
113
+ return :wrong_scope if wrong_scope?(directive, actual_name)
114
+ return :no_scope if no_scope?(directive, actual_name)
115
115
 
116
116
  # The method directive being prefixed by 'self.' is always an offense.
117
117
  # The matched method_name does not contain the receiver but the
@@ -121,11 +121,11 @@ module RuboCop
121
121
  end
122
122
  end
123
123
 
124
- def wrong_scope(directive, actual_name)
124
+ def wrong_scope?(directive, actual_name)
125
125
  !actual_name.start_with?('self.') && directive[:has_scope_directive]
126
126
  end
127
127
 
128
- def no_scope(directive, actual_name)
128
+ def no_scope?(directive, actual_name)
129
129
  actual_name.start_with?('self.') && !directive[:has_scope_directive]
130
130
  end
131
131
 
@@ -28,6 +28,8 @@ module RuboCop
28
28
  RESTRICT_ON_SEND = %i[def_node_matcher def_node_search].freeze
29
29
  NODE_GROUPS = {
30
30
  any_block: %i[block numblock itblock],
31
+ any_def: %i[def defs],
32
+ any_match_pattern: %i[match_pattern match_pattern_p],
31
33
  argument: %i[arg optarg restarg kwarg kwoptarg kwrestarg blockarg forward_arg shadowarg],
32
34
  boolean: %i[true false],
33
35
  call: %i[send csend],
@@ -10,7 +10,12 @@ module RuboCop
10
10
  extend FileFinder
11
11
 
12
12
  ALLOWED_CONFIGURATIONS = %w[
13
- Safe SafeAutoCorrect AutoCorrect Severity StyleGuide Details Reference Include Exclude
13
+ Safe SafeAutoCorrect AutoCorrect
14
+ Severity
15
+ StyleGuide
16
+ Details
17
+ Reference References
18
+ Include Exclude
14
19
  ].freeze
15
20
  RESTRICT_ON_SEND = %i[[] fetch].freeze
16
21
  MSG = '`%<name>s` is not defined in the configuration for `%<cop>s` ' \
@@ -73,8 +73,7 @@ module RuboCop
73
73
  # @!method block_end_align_target?(node, child)
74
74
  def_node_matcher :block_end_align_target?, <<~PATTERN
75
75
  {assignment?
76
- def
77
- defs
76
+ any_def
78
77
  splat
79
78
  and
80
79
  or
@@ -22,6 +22,11 @@ module RuboCop
22
22
  # * Private attribute macros (`attr_accessor`, `attr_writer`, `attr_reader`)
23
23
  # * Private instance methods
24
24
  #
25
+ # NOTE: Simply enabling the cop with `Enabled: true` will not use
26
+ # the example order shown below.
27
+ # To enforce the order of macros like `attr_reader`,
28
+ # you must define both `ExpectedOrder` *and* `Categories`.
29
+ #
25
30
  # You can configure the following order:
26
31
  #
27
32
  # [source,yaml]
@@ -68,6 +73,36 @@ module RuboCop
68
73
  # - extend
69
74
  # ----
70
75
  #
76
+ # If you only set `ExpectedOrder`
77
+ # without defining `Categories`,
78
+ # macros such as `attr_reader` or `has_many`
79
+ # will not be recognized as part of a category, and their order will not be validated.
80
+ # For example, the following will NOT raise any offenses, even if the order is incorrect:
81
+ #
82
+ # [source,yaml]
83
+ # ----
84
+ # Layout/ClassStructure:
85
+ # Enabled: true
86
+ # ExpectedOrder:
87
+ # - public_attribute_macros
88
+ # - initializer
89
+ # ----
90
+ #
91
+ # To make it work as expected, you must also specify `Categories` like this:
92
+ #
93
+ # [source,yaml]
94
+ # ----
95
+ # Layout/ClassStructure:
96
+ # ExpectedOrder:
97
+ # - public_attribute_macros
98
+ # - initializer
99
+ # Categories:
100
+ # attribute_macros:
101
+ # - attr_reader
102
+ # - attr_writer
103
+ # - attr_accessor
104
+ # ----
105
+ #
71
106
  # @safety
72
107
  # Autocorrection is unsafe because class methods and module inclusion
73
108
  # can behave differently, based on which methods or constants have
@@ -161,7 +161,7 @@ module RuboCop
161
161
  elements.flat_map do |e|
162
162
  e.loc.column
163
163
  end
164
- end.uniq.count == 1
164
+ end.uniq.one?
165
165
  end
166
166
 
167
167
  def first_argument_line(elements)
@@ -48,7 +48,7 @@ module RuboCop
48
48
  def on_send(node)
49
49
  return unless node.def_modifier?
50
50
 
51
- method_def = node.each_descendant(:def, :defs).first
51
+ method_def = node.each_descendant(:any_def).first
52
52
  expr = node.source_range
53
53
 
54
54
  line_start = range_between(expr.begin_pos, method_def.loc.keyword.end_pos)
@@ -131,7 +131,7 @@ module RuboCop
131
131
 
132
132
  def next_sibling_parent_empty_or_else?(node)
133
133
  next_sibling = node.right_sibling
134
- return true if next_sibling.nil?
134
+ return true unless next_sibling.is_a?(AST::Node)
135
135
 
136
136
  parent = next_sibling.parent
137
137
 
@@ -180,7 +180,7 @@ module RuboCop
180
180
  end
181
181
 
182
182
  def method_candidate?(node)
183
- cop_config['EmptyLineBetweenMethodDefs'] && node.type?(:def, :defs)
183
+ cop_config['EmptyLineBetweenMethodDefs'] && node.any_def_type?
184
184
  end
185
185
 
186
186
  def class_candidate?(node)
@@ -252,7 +252,7 @@ module RuboCop
252
252
  end
253
253
 
254
254
  def end_loc(node)
255
- if node.type?(:def, :defs) && node.endless?
255
+ if node.any_def_type? && node.endless?
256
256
  node.source_range.end
257
257
  else
258
258
  node.loc.end
@@ -116,7 +116,7 @@ module RuboCop
116
116
  def allowed_only_before_style?(node)
117
117
  if node.special_modifier?
118
118
  return true if processed_source[node.last_line] == 'end'
119
- return false if next_line_empty?(node.last_line)
119
+ return false if next_line_empty_and_exists?(node.last_line)
120
120
  end
121
121
 
122
122
  previous_line_empty?(node.first_line)
@@ -129,7 +129,7 @@ module RuboCop
129
129
  when :around
130
130
  corrector.insert_after(line, "\n") unless next_line_empty?(node.last_line)
131
131
  when :only_before
132
- if next_line_empty?(node.last_line)
132
+ if next_line_empty_and_exists?(node.last_line)
133
133
  range = next_empty_line_range(node)
134
134
 
135
135
  corrector.remove(range)
@@ -138,7 +138,7 @@ module RuboCop
138
138
  end
139
139
 
140
140
  def previous_line_ignoring_comments(processed_source, send_line)
141
- processed_source[0..send_line - 2].reverse.find { |line| !comment_line?(line) }
141
+ processed_source[0..(send_line - 2)].reverse.find { |line| !comment_line?(line) }
142
142
  end
143
143
 
144
144
  def previous_line_empty?(send_line)
@@ -154,6 +154,10 @@ module RuboCop
154
154
  body_end?(last_send_line) || next_line.blank?
155
155
  end
156
156
 
157
+ def next_line_empty_and_exists?(last_send_line)
158
+ next_line_empty?(last_send_line) && last_send_line.next != processed_source.lines.size
159
+ end
160
+
157
161
  def empty_lines_around?(node)
158
162
  previous_line_empty?(node.first_line) && next_line_empty?(node.last_line)
159
163
  end
@@ -155,7 +155,7 @@ module RuboCop
155
155
  def on_send(node)
156
156
  return unless should_check?(node)
157
157
  return if same_line?(node, node.first_argument)
158
- return if style != :consistent && enforce_first_argument_with_fixed_indentation? &&
158
+ return if enforce_first_argument_with_fixed_indentation? &&
159
159
  !enable_layout_first_method_argument_line_break?
160
160
 
161
161
  indent = base_indentation(node) + configured_indentation_width
@@ -19,7 +19,7 @@ module RuboCop
19
19
  # * ignore_implicit (without curly braces)
20
20
  #
21
21
  # Alternatively you can specify multiple allowed styles. That's done by
22
- # passing a list of styles to EnforcedStyles.
22
+ # passing a list of styles to EnforcedHashRocketStyle and EnforcedColonStyle.
23
23
  #
24
24
  # @example EnforcedHashRocketStyle: key (default)
25
25
  # # bad
@@ -250,7 +250,7 @@ module RuboCop
250
250
  reset!
251
251
 
252
252
  alignment_for(first_pair).each do |alignment|
253
- delta = alignment.deltas_for_first_pair(first_pair, node)
253
+ delta = alignment.deltas_for_first_pair(first_pair)
254
254
  check_delta delta, node: first_pair, alignment: alignment
255
255
  end
256
256
 
@@ -58,6 +58,12 @@ module RuboCop
58
58
  # attr_reader :name #: String
59
59
  # attr_reader :age #: Integer?
60
60
  #
61
+ # #: (
62
+ # #| Integer,
63
+ # #| String
64
+ # #| ) -> void
65
+ # def foo; end
66
+ #
61
67
  # @example AllowRBSInlineAnnotation: true
62
68
  #
63
69
  # # good
@@ -67,6 +73,12 @@ module RuboCop
67
73
  # attr_reader :name #: String
68
74
  # attr_reader :age #: Integer?
69
75
  #
76
+ # #: (
77
+ # #| Integer,
78
+ # #| String
79
+ # #| ) -> void
80
+ # def foo; end
81
+ #
70
82
  # @example AllowSteepAnnotation: false (default)
71
83
  #
72
84
  # # bad
@@ -175,7 +187,7 @@ module RuboCop
175
187
  end
176
188
 
177
189
  def rbs_inline_annotation?(comment)
178
- allow_rbs_inline_annotation? && comment.text.start_with?(/#:|#\[.+\]/)
190
+ allow_rbs_inline_annotation? && comment.text.start_with?(/#:|#\[.+\]|#\|/)
179
191
  end
180
192
 
181
193
  def allow_steep_annotation?
@@ -258,7 +258,7 @@ module RuboCop
258
258
  if ignore_cop_directives? && directive_on_source_line?(line_index)
259
259
  return check_directive_line(line, line_index)
260
260
  end
261
- return check_uri_line(line, line_index) if allow_uri?
261
+ return check_line_for_exemptions(line, line_index) if allow_uri? || allow_qualified_name?
262
262
 
263
263
  register_offense(excess_range(nil, line, line_index), line, line_index)
264
264
  end
@@ -358,11 +358,32 @@ module RuboCop
358
358
  )
359
359
  end
360
360
 
361
- def check_uri_line(line, line_index)
362
- uri_range = find_excessive_uri_range(line)
363
- return if uri_range && allowed_uri_position?(line, uri_range)
361
+ def check_line_for_exemptions(line, line_index)
362
+ uri_range = range_if_applicable(line, :uri)
363
+ qualified_name_range = range_if_applicable(line, :qualified_name)
364
364
 
365
- register_offense(excess_range(uri_range, line, line_index), line, line_index)
365
+ return if allowed_combination?(line, uri_range, qualified_name_range)
366
+
367
+ range = uri_range || qualified_name_range
368
+ register_offense(excess_range(range, line, line_index), line, line_index)
369
+ end
370
+
371
+ def range_if_applicable(line, type)
372
+ return unless type == :uri ? allow_uri? : allow_qualified_name?
373
+
374
+ find_excessive_range(line, type)
375
+ end
376
+
377
+ def allowed_combination?(line, uri_range, qualified_name_range)
378
+ if uri_range && qualified_name_range
379
+ allowed_position?(line, uri_range) && allowed_position?(line, qualified_name_range)
380
+ elsif uri_range
381
+ allowed_position?(line, uri_range)
382
+ elsif qualified_name_range
383
+ allowed_position?(line, qualified_name_range)
384
+ else
385
+ false
386
+ end
366
387
  end
367
388
 
368
389
  def breakable_dstr?(node)
@@ -118,7 +118,7 @@ module RuboCop
118
118
  end
119
119
 
120
120
  def right_hand_side(send_node)
121
- send_node.first_argument.source_range
121
+ send_node.first_argument&.source_range
122
122
  end
123
123
  end
124
124
  end
@@ -29,8 +29,7 @@ module RuboCop
29
29
  MSG = '`%<kw_loc>s` at %<kw_loc_line>d, %<kw_loc_column>d is not ' \
30
30
  'aligned with `%<beginning>s` at ' \
31
31
  '%<begin_loc_line>d, %<begin_loc_column>d.'
32
- ANCESTOR_TYPES = %i[kwbegin def defs class module any_block].freeze
33
- ANCESTOR_TYPES_WITH_ACCESS_MODIFIERS = %i[def defs].freeze
32
+ ANCESTOR_TYPES = %i[kwbegin any_def class module any_block].freeze
34
33
  ALTERNATIVE_ACCESS_MODIFIERS = %i[public_class_method private_class_method].freeze
35
34
 
36
35
  def on_resbody(node)
@@ -162,8 +161,7 @@ module RuboCop
162
161
  end
163
162
 
164
163
  def access_modifier_node(node)
165
- return nil unless
166
- ANCESTOR_TYPES_WITH_ACCESS_MODIFIERS.include?(node.type)
164
+ return nil unless node.any_def_type?
167
165
 
168
166
  access_modifier_node = node.ancestors.first
169
167
  return nil unless access_modifier?(access_modifier_node)
@@ -23,6 +23,16 @@ module RuboCop
23
23
  def kind(token)
24
24
  'semicolon' if token.semicolon?
25
25
  end
26
+
27
+ def space_missing?(token1, token2)
28
+ super && !semicolon_sequence?(token1, token2)
29
+ end
30
+
31
+ private
32
+
33
+ def semicolon_sequence?(token, next_token)
34
+ token.semicolon? && next_token.semicolon?
35
+ end
26
36
  end
27
37
  end
28
38
  end
@@ -22,50 +22,17 @@ module RuboCop
22
22
  RESTRICT_ON_SEND = %i[[] []=].freeze
23
23
 
24
24
  def on_send(node)
25
- return unless (first_argument = node.first_argument)
25
+ return if node.loc.dot
26
26
 
27
- begin_pos = first_argument.source_range.begin_pos
28
- return unless (range = offense_range(node, begin_pos))
29
-
30
- register_offense(range)
31
- end
32
-
33
- private
34
-
35
- def offense_range(node, begin_pos)
36
27
  receiver_end_pos = node.receiver.source_range.end_pos
37
28
  selector_begin_pos = node.loc.selector.begin_pos
38
29
  return if receiver_end_pos >= selector_begin_pos
39
- return if dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
40
-
41
- if reference_variable_with_brackets?(node)
42
- range_between(receiver_end_pos, selector_begin_pos)
43
- elsif node.method?(:[]=)
44
- offense_range_for_assignment(node, begin_pos)
45
- end
46
- end
47
-
48
- def dot_before_brackets?(node, receiver_end_pos, selector_begin_pos)
49
- return false unless node.loc.respond_to?(:dot) && (dot = node.loc.dot)
50
30
 
51
- dot.begin_pos == receiver_end_pos && dot.end_pos == selector_begin_pos
52
- end
53
-
54
- def offense_range_for_assignment(node, begin_pos)
55
- end_pos = node.receiver.source_range.end_pos
56
-
57
- return if begin_pos - end_pos == 1 ||
58
- (range = range_between(end_pos, begin_pos - 1)).source.start_with?('[')
59
-
60
- range
61
- end
62
-
63
- def register_offense(range)
64
- add_offense(range) { |corrector| corrector.remove(range) }
65
- end
31
+ range = range_between(receiver_end_pos, selector_begin_pos)
66
32
 
67
- def reference_variable_with_brackets?(node)
68
- node.receiver&.variable? && node.method?(:[]) && node.arguments.size == 1
33
+ add_offense(range) do |corrector|
34
+ corrector.remove(range)
35
+ end
69
36
  end
70
37
  end
71
38
  end