rubocop 1.39.0 → 1.44.1

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 (188) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.txt +1 -1
  3. data/README.md +2 -2
  4. data/config/default.yml +149 -11
  5. data/exe/rubocop +1 -1
  6. data/lib/rubocop/cli.rb +1 -1
  7. data/lib/rubocop/comment_config.rb +5 -0
  8. data/lib/rubocop/config.rb +39 -15
  9. data/lib/rubocop/config_loader.rb +26 -20
  10. data/lib/rubocop/config_loader_resolver.rb +6 -2
  11. data/lib/rubocop/config_validator.rb +1 -1
  12. data/lib/rubocop/cop/badge.rb +9 -4
  13. data/lib/rubocop/cop/base.rb +84 -74
  14. data/lib/rubocop/cop/commissioner.rb +8 -3
  15. data/lib/rubocop/cop/cop.rb +29 -29
  16. data/lib/rubocop/cop/corrector.rb +30 -10
  17. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  18. data/lib/rubocop/cop/correctors/ordered_gem_corrector.rb +1 -6
  19. data/lib/rubocop/cop/gemspec/dependency_version.rb +16 -18
  20. data/lib/rubocop/cop/gemspec/development_dependencies.rb +107 -0
  21. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  22. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  23. data/lib/rubocop/cop/internal_affairs/redundant_let_rubocop_config_new.rb +11 -3
  24. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  25. data/lib/rubocop/cop/layout/block_end_newline.rb +7 -1
  26. data/lib/rubocop/cop/layout/class_structure.rb +32 -11
  27. data/lib/rubocop/cop/layout/closing_parenthesis_indentation.rb +2 -6
  28. data/lib/rubocop/cop/layout/comment_indentation.rb +3 -1
  29. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  30. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  31. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  32. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  33. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  34. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  35. data/lib/rubocop/cop/layout/heredoc_indentation.rb +6 -9
  36. data/lib/rubocop/cop/layout/indentation_style.rb +7 -2
  37. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  38. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +11 -5
  39. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  40. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  41. data/lib/rubocop/cop/layout/multiline_block_layout.rb +1 -1
  42. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  43. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  44. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  45. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  46. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  47. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +0 -2
  48. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  49. data/lib/rubocop/cop/layout/trailing_whitespace.rb +11 -4
  50. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  51. data/lib/rubocop/cop/lint/ambiguous_operator.rb +4 -0
  52. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  53. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  54. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  55. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +62 -112
  56. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  57. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  58. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  59. data/lib/rubocop/cop/lint/else_layout.rb +2 -6
  60. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  61. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  62. data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +11 -7
  63. data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +15 -17
  64. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  65. data/lib/rubocop/cop/lint/mixed_regexp_capture_types.rb +1 -0
  66. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  67. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +19 -0
  68. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  69. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +15 -3
  70. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  71. data/lib/rubocop/cop/lint/redundant_require_statement.rb +11 -1
  72. data/lib/rubocop/cop/lint/regexp_as_condition.rb +6 -0
  73. data/lib/rubocop/cop/lint/require_parentheses.rb +3 -1
  74. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +10 -12
  75. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  76. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
  77. data/lib/rubocop/cop/lint/unused_method_argument.rb +2 -1
  78. data/lib/rubocop/cop/lint/useless_method_definition.rb +3 -3
  79. data/lib/rubocop/cop/lint/useless_rescue.rb +85 -0
  80. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +14 -4
  81. data/lib/rubocop/cop/lint/void.rb +25 -16
  82. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  83. data/lib/rubocop/cop/metrics/block_nesting.rb +1 -1
  84. data/lib/rubocop/cop/metrics/class_length.rb +10 -5
  85. data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +1 -1
  86. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  87. data/lib/rubocop/cop/metrics/module_length.rb +10 -5
  88. data/lib/rubocop/cop/metrics/parameter_lists.rb +27 -0
  89. data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -1
  90. data/lib/rubocop/cop/metrics/utils/abc_size_calculator.rb +3 -6
  91. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -3
  92. data/lib/rubocop/cop/mixin/alignment.rb +2 -2
  93. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  94. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  95. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  96. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  97. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +59 -6
  98. data/lib/rubocop/cop/mixin/line_length_help.rb +11 -2
  99. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  100. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  101. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  102. data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +1 -1
  103. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  104. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  105. data/lib/rubocop/cop/mixin/statement_modifier.rb +16 -1
  106. data/lib/rubocop/cop/naming/block_forwarding.rb +5 -1
  107. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  108. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  109. data/lib/rubocop/cop/registry.rb +63 -43
  110. data/lib/rubocop/cop/security/compound_hash.rb +2 -1
  111. data/lib/rubocop/cop/style/access_modifier_declarations.rb +18 -10
  112. data/lib/rubocop/cop/style/alias.rb +9 -1
  113. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  114. data/lib/rubocop/cop/style/block_comments.rb +1 -1
  115. data/lib/rubocop/cop/style/block_delimiters.rb +8 -2
  116. data/lib/rubocop/cop/style/class_and_module_children.rb +2 -9
  117. data/lib/rubocop/cop/style/comparable_clamp.rb +125 -0
  118. data/lib/rubocop/cop/style/concat_array_literals.rb +86 -0
  119. data/lib/rubocop/cop/style/conditional_assignment.rb +0 -6
  120. data/lib/rubocop/cop/style/documentation.rb +11 -5
  121. data/lib/rubocop/cop/style/guard_clause.rb +44 -9
  122. data/lib/rubocop/cop/style/hash_each_methods.rb +13 -1
  123. data/lib/rubocop/cop/style/hash_syntax.rb +11 -7
  124. data/lib/rubocop/cop/style/identical_conditional_branches.rb +15 -0
  125. data/lib/rubocop/cop/style/if_with_semicolon.rb +4 -4
  126. data/lib/rubocop/cop/style/infinite_loop.rb +2 -5
  127. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  128. data/lib/rubocop/cop/style/invertible_unless_condition.rb +114 -0
  129. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  130. data/lib/rubocop/cop/style/map_to_set.rb +61 -0
  131. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +23 -14
  132. data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +2 -0
  133. data/lib/rubocop/cop/style/method_def_parentheses.rb +11 -4
  134. data/lib/rubocop/cop/style/min_max_comparison.rb +83 -0
  135. data/lib/rubocop/cop/style/missing_else.rb +13 -1
  136. data/lib/rubocop/cop/style/multiline_if_modifier.rb +0 -4
  137. data/lib/rubocop/cop/style/multiline_memoization.rb +2 -2
  138. data/lib/rubocop/cop/style/negated_if_else_condition.rb +1 -5
  139. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  140. data/lib/rubocop/cop/style/one_line_conditional.rb +3 -6
  141. data/lib/rubocop/cop/style/operator_method_call.rb +15 -1
  142. data/lib/rubocop/cop/style/parallel_assignment.rb +3 -1
  143. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  144. data/lib/rubocop/cop/style/redundant_conditional.rb +0 -4
  145. data/lib/rubocop/cop/style/redundant_constant_base.rb +85 -0
  146. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +45 -0
  147. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +2 -1
  148. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  149. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  150. data/lib/rubocop/cop/style/redundant_string_escape.rb +6 -3
  151. data/lib/rubocop/cop/style/require_order.rb +135 -0
  152. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  153. data/lib/rubocop/cop/style/select_by_regexp.rb +13 -5
  154. data/lib/rubocop/cop/style/self_assignment.rb +2 -2
  155. data/lib/rubocop/cop/style/semicolon.rb +26 -3
  156. data/lib/rubocop/cop/style/signal_exception.rb +8 -6
  157. data/lib/rubocop/cop/style/string_hash_keys.rb +4 -1
  158. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  159. data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
  160. data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +4 -4
  161. data/lib/rubocop/cop/style/word_array.rb +41 -0
  162. data/lib/rubocop/cop/style/yoda_expression.rb +81 -0
  163. data/lib/rubocop/cop/style/zero_length_predicate.rb +31 -14
  164. data/lib/rubocop/cop/team.rb +30 -30
  165. data/lib/rubocop/cop/util.rb +32 -5
  166. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  167. data/lib/rubocop/cop/variable_force/variable_table.rb +3 -1
  168. data/lib/rubocop/cop/variable_force.rb +18 -30
  169. data/lib/rubocop/cops_documentation_generator.rb +33 -11
  170. data/lib/rubocop/directive_comment.rb +1 -1
  171. data/lib/rubocop/file_patterns.rb +43 -0
  172. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  173. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  174. data/lib/rubocop/formatter.rb +4 -1
  175. data/lib/rubocop/options.rb +8 -0
  176. data/lib/rubocop/path_util.rb +50 -22
  177. data/lib/rubocop/result_cache.rb +2 -2
  178. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  179. data/lib/rubocop/rspec/expect_offense.rb +6 -4
  180. data/lib/rubocop/rspec/support.rb +2 -2
  181. data/lib/rubocop/runner.rb +10 -3
  182. data/lib/rubocop/server/cache.rb +3 -1
  183. data/lib/rubocop/server/core.rb +1 -1
  184. data/lib/rubocop/target_finder.rb +1 -1
  185. data/lib/rubocop/target_ruby.rb +1 -2
  186. data/lib/rubocop/version.rb +1 -1
  187. data/lib/rubocop.rb +23 -6
  188. metadata +23 -9
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Gemspec
6
+ # Enforce that development dependencies for a gem are specified in
7
+ # `Gemfile`, rather than in the `gemspec` using
8
+ # `add_development_dependency`. Alternatively, using `EnforcedStyle:
9
+ # gemspec`, enforce that all dependencies are specified in `gemspec`,
10
+ # rather than in `Gemfile`.
11
+ #
12
+ # @example EnforcedStyle: Gemfile (default)
13
+ # # Specify runtime dependencies in your gemspec,
14
+ # # but all other dependencies in your Gemfile.
15
+ #
16
+ # # bad
17
+ # # example.gemspec
18
+ # s.add_development_dependency "foo"
19
+ #
20
+ # # good
21
+ # # Gemfile
22
+ # gem "foo"
23
+ #
24
+ # # good
25
+ # # gems.rb
26
+ # gem "foo"
27
+ #
28
+ # # good (with AllowedGems: ["bar"])
29
+ # # example.gemspec
30
+ # s.add_development_dependency "bar"
31
+ #
32
+ # @example EnforcedStyle: gems.rb
33
+ # # Specify runtime dependencies in your gemspec,
34
+ # # but all other dependencies in your Gemfile.
35
+ # #
36
+ # # Identical to `EnforcedStyle: Gemfile`, but with a different error message.
37
+ # # Rely on Bundler/GemFilename to enforce the use of `Gemfile` vs `gems.rb`.
38
+ #
39
+ # # bad
40
+ # # example.gemspec
41
+ # s.add_development_dependency "foo"
42
+ #
43
+ # # good
44
+ # # Gemfile
45
+ # gem "foo"
46
+ #
47
+ # # good
48
+ # # gems.rb
49
+ # gem "foo"
50
+ #
51
+ # # good (with AllowedGems: ["bar"])
52
+ # # example.gemspec
53
+ # s.add_development_dependency "bar"
54
+ #
55
+ # @example EnforcedStyle: gemspec
56
+ # # Specify all dependencies in your gemspec.
57
+ #
58
+ # # bad
59
+ # # Gemfile
60
+ # gem "foo"
61
+ #
62
+ # # good
63
+ # # example.gemspec
64
+ # s.add_development_dependency "foo"
65
+ #
66
+ # # good (with AllowedGems: ["bar"])
67
+ # # Gemfile
68
+ # gem "bar"
69
+ #
70
+ class DevelopmentDependencies < Base
71
+ include ConfigurableEnforcedStyle
72
+
73
+ MSG = 'Specify development dependencies in %<preferred>s.'
74
+ RESTRICT_ON_SEND = %i[add_development_dependency gem].freeze
75
+
76
+ # @!method add_development_dependency?(node)
77
+ def_node_matcher :add_development_dependency?, <<~PATTERN
78
+ (send _ :add_development_dependency (str #forbidden_gem? ...))
79
+ PATTERN
80
+
81
+ # @!method gem?(node)
82
+ def_node_matcher :gem?, <<~PATTERN
83
+ (send _ :gem (str #forbidden_gem? ...))
84
+ PATTERN
85
+
86
+ def on_send(node)
87
+ case style
88
+ when :Gemfile, :'gems.rb'
89
+ add_offense(node) if add_development_dependency?(node)
90
+ when :gemspec
91
+ add_offense(node) if gem?(node)
92
+ end
93
+ end
94
+
95
+ private
96
+
97
+ def forbidden_gem?(gem_name)
98
+ !cop_config['AllowedGems'].include?(gem_name)
99
+ end
100
+
101
+ def message(_range)
102
+ format(MSG, preferred: style)
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -28,8 +28,9 @@ module RuboCop
28
28
  /^\s+# This cop (?<special>#{SPECIAL_WORDS.join('|')})?\s*(?<word>.+?) .*/.freeze
29
29
  REPLACEMENT_REGEX = /^\s+# This cop (#{SPECIAL_WORDS.join('|')})?\s*(.+?) /.freeze
30
30
 
31
+ # rubocop:disable Metrics/CyclomaticComplexity
31
32
  def on_class(node)
32
- return unless (module_node = node.parent)
33
+ return unless (module_node = node.parent) && node.parent_class
33
34
 
34
35
  description_beginning = first_comment_line(module_node)
35
36
  return unless description_beginning
@@ -48,6 +49,7 @@ module RuboCop
48
49
  end
49
50
  end
50
51
  end
52
+ # rubocop:enable Metrics/CyclomaticComplexity
51
53
 
52
54
  private
53
55
 
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module InternalAffairs
6
+ # Enforces the use of `node.lambda_or_proc?` instead of `node.lambda? || node.proc?`.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # node.lambda? || node.proc?
11
+ # node.proc? || node.lambda?
12
+ #
13
+ # # good
14
+ # node.lambda_or_proc?
15
+ #
16
+ class LambdaOrProc < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = 'Use `%<prefer>s`.'
20
+
21
+ # @!method lambda_or_proc(node)
22
+ def_node_matcher :lambda_or_proc, <<~PATTERN
23
+ {
24
+ (or $(send _node :lambda?) $(send _node :proc?))
25
+ (or $(send _node :proc?) $(send _node :lambda?))
26
+ (or
27
+ (or _ $(send _node :lambda?)) $(send _node :proc?))
28
+ (or
29
+ (or _ $(send _node :proc?)) $(send _node :lambda?))
30
+ }
31
+ PATTERN
32
+
33
+ def on_or(node)
34
+ return unless (lhs, rhs = lambda_or_proc(node))
35
+
36
+ offense = lhs.receiver.source_range.join(rhs.source_range.end)
37
+ prefer = "#{lhs.receiver.source}.lambda_or_proc?"
38
+
39
+ add_offense(offense, message: format(MSG, prefer: prefer)) do |corrector|
40
+ corrector.replace(offense, prefer)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -31,9 +31,17 @@ module RuboCop
31
31
  (send nil? :let
32
32
  (sym :config))
33
33
  (args)
34
- (send
35
- (const
36
- (const nil? :RuboCop) :Config) :new))
34
+ {
35
+ (send
36
+ (const
37
+ (const nil? :RuboCop) :Config) :new)
38
+ (send
39
+ (const
40
+ (const nil? :RuboCop) :Config) :new
41
+ (hash (pair (send (send (send nil? :described_class) :badge) :to_s)
42
+ (send nil? :cop_config))))
43
+ }
44
+ )
37
45
  PATTERN
38
46
 
39
47
  def on_block(node)
@@ -6,6 +6,7 @@ require_relative 'internal_affairs/empty_line_between_expect_offense_and_correct
6
6
  require_relative 'internal_affairs/example_description'
7
7
  require_relative 'internal_affairs/example_heredoc_delimiter'
8
8
  require_relative 'internal_affairs/inherit_deprecated_cop_class'
9
+ require_relative 'internal_affairs/lambda_or_proc'
9
10
  require_relative 'internal_affairs/location_line_equality_comparison'
10
11
  require_relative 'internal_affairs/method_name_end_with'
11
12
  require_relative 'internal_affairs/method_name_equal'
@@ -46,7 +46,7 @@ module RuboCop
46
46
  def register_offense(node)
47
47
  add_offense(node.loc.end, message: message(node)) do |corrector|
48
48
  offense_range = offense_range(node)
49
- replacement = "\n#{offense_range.source.strip}"
49
+ replacement = replacement(node)
50
50
 
51
51
  if (heredoc = last_heredoc_argument(node.body))
52
52
  corrector.remove(offense_range)
@@ -79,6 +79,12 @@ module RuboCop
79
79
  )
80
80
  end
81
81
 
82
+ def replacement(node)
83
+ end_with_method_chain = node.loc.end.join(end_of_method_chain(node).loc.expression.end)
84
+
85
+ "\n#{end_with_method_chain.source.strip}"
86
+ end
87
+
82
88
  def end_of_method_chain(node)
83
89
  return node unless node.parent&.call_type?
84
90
 
@@ -132,25 +132,19 @@ module RuboCop
132
132
  # end
133
133
  # end
134
134
  #
135
- # @see https://rubystyle.guide#consistent-classes
136
135
  class ClassStructure < Base
137
136
  include VisibilityHelp
138
137
  extend AutoCorrector
139
138
 
140
139
  HUMANIZED_NODE_TYPE = {
141
140
  casgn: :constants,
142
- defs: :class_methods,
141
+ defs: :public_class_methods,
143
142
  def: :public_methods,
144
143
  sclass: :class_singleton
145
144
  }.freeze
146
145
 
147
146
  MSG = '`%<category>s` is supposed to appear before `%<previous>s`.'
148
147
 
149
- # @!method dynamic_constant?(node)
150
- def_node_matcher :dynamic_constant?, <<~PATTERN
151
- (casgn nil? _ (send ...))
152
- PATTERN
153
-
154
148
  # Validates code style on class declaration.
155
149
  # Add offense when find a node out of expected order.
156
150
  def on_class(class_node)
@@ -222,7 +216,7 @@ module RuboCop
222
216
  def walk_over_nested_class_definition(class_node)
223
217
  class_elements(class_node).each do |node|
224
218
  classification = classify(node)
225
- next if ignore?(classification)
219
+ next if ignore?(node, classification)
226
220
 
227
221
  yield node, classification
228
222
  end
@@ -240,17 +234,20 @@ module RuboCop
240
234
  end
241
235
  end
242
236
 
243
- def ignore?(classification)
237
+ def ignore?(node, classification)
244
238
  classification.nil? ||
245
239
  classification.to_s.end_with?('=') ||
246
- expected_order.index(classification).nil?
240
+ expected_order.index(classification).nil? ||
241
+ private_constant?(node)
247
242
  end
248
243
 
249
244
  def ignore_for_autocorrect?(node, sibling)
250
245
  classification = classify(node)
251
246
  sibling_class = classify(sibling)
252
247
 
253
- ignore?(sibling_class) || classification == sibling_class || dynamic_constant?(node)
248
+ ignore?(sibling, sibling_class) ||
249
+ classification == sibling_class ||
250
+ dynamic_constant?(node)
254
251
  end
255
252
 
256
253
  def humanize_node(node)
@@ -262,6 +259,30 @@ module RuboCop
262
259
  HUMANIZED_NODE_TYPE[node.type] || node.type
263
260
  end
264
261
 
262
+ def dynamic_constant?(node)
263
+ return false unless node.casgn_type? && node.namespace.nil?
264
+
265
+ expression = node.expression
266
+ expression.send_type? &&
267
+ !(expression.method?(:freeze) && expression.receiver&.recursive_basic_literal?)
268
+ end
269
+
270
+ def private_constant?(node)
271
+ return false unless node.casgn_type? && node.namespace.nil?
272
+ return false unless (parent = node.parent)
273
+
274
+ parent.each_child_node(:send) do |child_node|
275
+ return true if marked_as_private_constant?(child_node, node.name)
276
+ end
277
+ false
278
+ end
279
+
280
+ def marked_as_private_constant?(node, name)
281
+ return false unless node.method?(:private_constant)
282
+
283
+ node.arguments.any? { |arg| (arg.sym_type? || arg.str_type?) && arg.value == name }
284
+ end
285
+
265
286
  def source_range_with_comment(node)
266
287
  begin_pos, end_pos =
267
288
  if (node.def_type? && !node.method?(:initialize)) ||
@@ -144,7 +144,7 @@ module RuboCop
144
144
  def expected_column(left_paren, elements)
145
145
  if line_break_after_left_paren?(left_paren, elements)
146
146
  source_indent = processed_source.line_indentation(first_argument_line(elements))
147
- new_indent = source_indent - indentation_width
147
+ new_indent = source_indent - configured_indentation_width
148
148
 
149
149
  new_indent.negative? ? 0 : new_indent
150
150
  elsif all_elements_aligned?(elements)
@@ -157,7 +157,7 @@ module RuboCop
157
157
  def all_elements_aligned?(elements)
158
158
  elements.flat_map do |e|
159
159
  if e.hash_type?
160
- e.each_pair.map { |pair| pair.loc.column }
160
+ e.each_child_node.map { |child| child.loc.column }
161
161
  else
162
162
  e.loc.column
163
163
  end
@@ -184,10 +184,6 @@ module RuboCop
184
184
  end
185
185
  end
186
186
 
187
- def indentation_width
188
- @config.for_cop('Layout/IndentationWidth')['Width'] || 2
189
- end
190
-
191
187
  def line_break_after_left_paren?(left_paren, elements)
192
188
  elements.first && elements.first.loc.line > left_paren.line
193
189
  end
@@ -154,7 +154,9 @@ module RuboCop
154
154
  end
155
155
 
156
156
  def less_indented?(line)
157
- /^\s*(end\b|[)}\]])/.match?(line)
157
+ rule = config.for_cop('Layout/AccessModifierIndentation')['EnforcedStyle'] == 'outdent'
158
+ access_modifier = 'private|protected|public'
159
+ /\A\s*(end\b|[)}\]])/.match?(line) || (rule && /\A\s*(#{access_modifier})\b/.match?(line))
158
160
  end
159
161
 
160
162
  def two_alternatives?(line)
@@ -27,6 +27,8 @@ module RuboCop
27
27
 
28
28
  def on_new_investigation
29
29
  return if processed_source.tokens.empty?
30
+ # Quick check if we possibly have consecutive blank lines.
31
+ return unless processed_source.raw_source.include?("\n\n\n")
30
32
 
31
33
  lines = Set.new
32
34
  processed_source.each_token { |token| lines << token.line }
@@ -119,12 +119,16 @@ module RuboCop
119
119
  def ignored_ranges(ast)
120
120
  return [] unless ast
121
121
 
122
- @ignored_ranges ||= on_node(:pair, ast).map do |pair|
123
- next if pair.parent.single_line?
124
-
125
- key, value = *pair
126
- key.source_range.end_pos...value.source_range.begin_pos
127
- end.compact
122
+ @ignored_ranges ||= begin
123
+ ranges = []
124
+ on_node(:pair, ast) do |pair|
125
+ next if pair.parent.single_line?
126
+
127
+ key, value = *pair
128
+ ranges << (key.source_range.end_pos...value.source_range.begin_pos)
129
+ end
130
+ ranges
131
+ end
128
132
  end
129
133
 
130
134
  def force_equal_sign_alignment?
@@ -6,17 +6,49 @@ module RuboCop
6
6
  # Checks for a line break before the first element in a
7
7
  # multi-line array.
8
8
  #
9
- # @example
9
+ # @example AllowMultilineFinalElement: false (default)
10
+ #
11
+ # # bad
12
+ # [ :a,
13
+ # :b]
14
+ #
15
+ # # bad
16
+ # [ :a, {
17
+ # :b => :c
18
+ # }]
19
+ #
20
+ # # good
21
+ # [:a, :b]
22
+ #
23
+ # # good
24
+ # [
25
+ # :a,
26
+ # :b]
27
+ #
28
+ # # good
29
+ # [
30
+ # :a, {
31
+ # :b => :c
32
+ # }]
33
+ #
34
+ # @example AllowMultilineFinalElement: true
10
35
  #
11
36
  # # bad
12
37
  # [ :a,
13
38
  # :b]
14
39
  #
15
40
  # # good
41
+ # [ :a, {
42
+ # :b => :c
43
+ # }]
44
+ #
45
+ # # good
16
46
  # [
17
47
  # :a,
18
48
  # :b]
19
49
  #
50
+ # # good
51
+ # [:a, :b]
20
52
  class FirstArrayElementLineBreak < Base
21
53
  include FirstElementLineBreak
22
54
  extend AutoCorrector
@@ -26,7 +58,7 @@ module RuboCop
26
58
  def on_array(node)
27
59
  return if !node.loc.begin && !assignment_on_same_line?(node)
28
60
 
29
- check_children_line_break(node, node.children)
61
+ check_children_line_break(node, node.children, ignore_last: ignore_last_element?)
30
62
  end
31
63
 
32
64
  private
@@ -35,6 +67,10 @@ module RuboCop
35
67
  source = node.source_range.source_line[0...node.loc.column]
36
68
  /\s*=\s*$/.match?(source)
37
69
  end
70
+
71
+ def ignore_last_element?
72
+ !!cop_config['AllowMultilineFinalElement']
73
+ end
38
74
  end
39
75
  end
40
76
  end
@@ -6,16 +6,55 @@ module RuboCop
6
6
  # Checks for a line break before the first element in a
7
7
  # multi-line hash.
8
8
  #
9
- # @example
9
+ # @example AllowMultilineFinalElement: false (default)
10
10
  #
11
11
  # # bad
12
12
  # { a: 1,
13
13
  # b: 2}
14
14
  #
15
+ # # bad
16
+ # { a: 1, b: {
17
+ # c: 3
18
+ # }}
19
+ #
15
20
  # # good
16
21
  # {
17
22
  # a: 1,
18
23
  # b: 2 }
24
+ #
25
+ # # good
26
+ # {
27
+ # a: 1, b: {
28
+ # c: 3
29
+ # }}
30
+ #
31
+ # @example AllowMultilineFinalElement: true
32
+ #
33
+ # # bad
34
+ # { a: 1,
35
+ # b: 2}
36
+ #
37
+ # # bad
38
+ # { a: 1,
39
+ # b: {
40
+ # c: 3
41
+ # }}
42
+ #
43
+ # # good
44
+ # { a: 1, b: {
45
+ # c: 3
46
+ # }}
47
+ #
48
+ # # good
49
+ # {
50
+ # a: 1,
51
+ # b: 2 }
52
+ #
53
+ # # good
54
+ # {
55
+ # a: 1, b: {
56
+ # c: 3
57
+ # }}
19
58
  class FirstHashElementLineBreak < Base
20
59
  include FirstElementLineBreak
21
60
  extend AutoCorrector
@@ -25,7 +64,15 @@ module RuboCop
25
64
  def on_hash(node)
26
65
  # node.loc.begin tells us whether the hash opens with a {
27
66
  # If it doesn't, Style/FirstMethodArgumentLineBreak will handle it
28
- check_children_line_break(node, node.children) if node.loc.begin
67
+ return unless node.loc.begin
68
+
69
+ check_children_line_break(node, node.children, ignore_last: ignore_last_element?)
70
+ end
71
+
72
+ private
73
+
74
+ def ignore_last_element?
75
+ !!cop_config['AllowMultilineFinalElement']
29
76
  end
30
77
  end
31
78
  end
@@ -6,17 +6,70 @@ module RuboCop
6
6
  # Checks for a line break before the first argument in a
7
7
  # multi-line method call.
8
8
  #
9
- # @example
9
+ # @example AllowMultilineFinalElement: false (default)
10
10
  #
11
11
  # # bad
12
12
  # method(foo, bar,
13
13
  # baz)
14
14
  #
15
+ # # bad
16
+ # method(foo, bar, {
17
+ # baz: "a",
18
+ # qux: "b",
19
+ # })
20
+ #
15
21
  # # good
16
22
  # method(
17
23
  # foo, bar,
18
24
  # baz)
19
25
  #
26
+ # # good
27
+ # method(
28
+ # foo, bar, {
29
+ # baz: "a",
30
+ # qux: "b",
31
+ # })
32
+ #
33
+ # # ignored
34
+ # method foo, bar,
35
+ # baz
36
+ #
37
+ # @example AllowMultilineFinalElement: true
38
+ #
39
+ # # bad
40
+ # method(foo, bar,
41
+ # baz)
42
+ #
43
+ # # bad
44
+ # method(foo,
45
+ # bar,
46
+ # {
47
+ # baz: "a",
48
+ # qux: "b",
49
+ # }
50
+ # )
51
+ #
52
+ # # good
53
+ # method(foo, bar, {
54
+ # baz: "a",
55
+ # qux: "b",
56
+ # })
57
+ #
58
+ # # good
59
+ # method(
60
+ # foo, bar,
61
+ # baz)
62
+ #
63
+ # # good
64
+ # method(
65
+ # foo,
66
+ # bar,
67
+ # {
68
+ # baz: "a",
69
+ # qux: "b",
70
+ # }
71
+ # )
72
+ #
20
73
  # # ignored
21
74
  # method foo, bar,
22
75
  # baz
@@ -38,10 +91,16 @@ module RuboCop
38
91
  last_arg = args.last
39
92
  args.concat(args.pop.children) if last_arg&.hash_type? && !last_arg&.braces?
40
93
 
41
- check_method_line_break(node, args)
94
+ check_method_line_break(node, args, ignore_last: ignore_last_element?)
42
95
  end
43
96
  alias on_csend on_send
44
97
  alias on_super on_send
98
+
99
+ private
100
+
101
+ def ignore_last_element?
102
+ !!cop_config['AllowMultilineFinalElement']
103
+ end
45
104
  end
46
105
  end
47
106
  end