rubocop 1.36.0 → 1.40.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 (150) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +78 -12
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop/arguments_env.rb +17 -0
  6. data/lib/rubocop/arguments_file.rb +17 -0
  7. data/lib/rubocop/cli/command/execute_runner.rb +7 -7
  8. data/lib/rubocop/cli/command/suggest_extensions.rb +8 -1
  9. data/lib/rubocop/comment_config.rb +41 -1
  10. data/lib/rubocop/config.rb +5 -4
  11. data/lib/rubocop/config_loader.rb +5 -5
  12. data/lib/rubocop/config_loader_resolver.rb +1 -1
  13. data/lib/rubocop/cop/base.rb +2 -9
  14. data/lib/rubocop/cop/commissioner.rb +3 -1
  15. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  16. data/lib/rubocop/cop/generator.rb +1 -2
  17. data/lib/rubocop/cop/internal_affairs/create_empty_file.rb +37 -0
  18. data/lib/rubocop/cop/internal_affairs/example_heredoc_delimiter.rb +111 -0
  19. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  20. data/lib/rubocop/cop/internal_affairs.rb +3 -0
  21. data/lib/rubocop/cop/layout/first_argument_indentation.rb +1 -0
  22. data/lib/rubocop/cop/layout/indentation_style.rb +1 -1
  23. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  24. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +29 -8
  25. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +1 -1
  26. data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +13 -9
  27. data/lib/rubocop/cop/layout/space_inside_array_percent_literal.rb +3 -0
  28. data/lib/rubocop/cop/layout/space_inside_hash_literal_braces.rb +30 -3
  29. data/lib/rubocop/cop/layout/space_inside_percent_literal_delimiters.rb +34 -0
  30. data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +6 -2
  31. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +2 -2
  32. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  33. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  34. data/lib/rubocop/cop/lint/duplicate_magic_comment.rb +73 -0
  35. data/lib/rubocop/cop/lint/duplicate_methods.rb +28 -9
  36. data/lib/rubocop/cop/lint/duplicate_regexp_character_class_element.rb +25 -6
  37. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  38. data/lib/rubocop/cop/lint/empty_class.rb +3 -1
  39. data/lib/rubocop/cop/lint/empty_conditional_body.rb +21 -9
  40. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  41. data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
  42. data/lib/rubocop/cop/lint/nested_method_definition.rb +50 -1
  43. data/lib/rubocop/cop/lint/number_conversion.rb +1 -1
  44. data/lib/rubocop/cop/lint/ordered_magic_comments.rb +4 -5
  45. data/lib/rubocop/cop/lint/out_of_range_regexp_ref.rb +1 -1
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +36 -4
  48. data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +7 -0
  49. data/lib/rubocop/cop/lint/redundant_require_statement.rb +38 -10
  50. data/lib/rubocop/cop/lint/require_parentheses.rb +1 -1
  51. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +18 -8
  52. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  53. data/lib/rubocop/cop/lint/shadowed_exception.rb +0 -10
  54. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +7 -3
  55. data/lib/rubocop/cop/lint/suppressed_exception.rb +1 -1
  56. data/lib/rubocop/cop/lint/unreachable_loop.rb +1 -1
  57. data/lib/rubocop/cop/lint/unused_method_argument.rb +4 -0
  58. data/lib/rubocop/cop/lint/void.rb +6 -6
  59. data/lib/rubocop/cop/metrics/abc_size.rb +1 -1
  60. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  61. data/lib/rubocop/cop/metrics/class_length.rb +9 -4
  62. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  63. data/lib/rubocop/cop/metrics/module_length.rb +9 -4
  64. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +5 -2
  65. data/lib/rubocop/cop/mixin/comments_help.rb +12 -0
  66. data/lib/rubocop/cop/mixin/frozen_string_literal.rb +4 -0
  67. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +30 -8
  68. data/lib/rubocop/cop/mixin/range_help.rb +23 -0
  69. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -1
  70. data/lib/rubocop/cop/mixin/statement_modifier.rb +15 -1
  71. data/lib/rubocop/cop/mixin/surrounding_space.rb +10 -8
  72. data/lib/rubocop/cop/mixin/visibility_help.rb +40 -5
  73. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -1
  74. data/lib/rubocop/cop/registry.rb +32 -14
  75. data/lib/rubocop/cop/style/access_modifier_declarations.rb +5 -7
  76. data/lib/rubocop/cop/style/accessor_grouping.rb +7 -3
  77. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  78. data/lib/rubocop/cop/style/block_delimiters.rb +2 -2
  79. data/lib/rubocop/cop/style/character_literal.rb +1 -1
  80. data/lib/rubocop/cop/style/class_equality_comparison.rb +8 -6
  81. data/lib/rubocop/cop/style/collection_compact.rb +12 -3
  82. data/lib/rubocop/cop/style/empty_method.rb +1 -1
  83. data/lib/rubocop/cop/style/endless_method.rb +1 -1
  84. data/lib/rubocop/cop/style/explicit_block_argument.rb +4 -0
  85. data/lib/rubocop/cop/style/format_string_token.rb +1 -1
  86. data/lib/rubocop/cop/style/guard_clause.rb +90 -22
  87. data/lib/rubocop/cop/style/hash_as_last_array_item.rb +1 -0
  88. data/lib/rubocop/cop/style/hash_each_methods.rb +32 -10
  89. data/lib/rubocop/cop/style/hash_except.rb +4 -0
  90. data/lib/rubocop/cop/style/hash_syntax.rb +1 -1
  91. data/lib/rubocop/cop/style/if_with_boolean_literal_branches.rb +25 -2
  92. data/lib/rubocop/cop/style/if_with_semicolon.rb +2 -1
  93. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +13 -2
  94. data/lib/rubocop/cop/style/module_function.rb +28 -6
  95. data/lib/rubocop/cop/style/negated_if_else_condition.rb +7 -1
  96. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  97. data/lib/rubocop/cop/style/numeric_predicate.rb +1 -1
  98. data/lib/rubocop/cop/style/object_then.rb +3 -0
  99. data/lib/rubocop/cop/style/operator_method_call.rb +53 -0
  100. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  101. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  102. data/lib/rubocop/cop/style/redundant_begin.rb +1 -0
  103. data/lib/rubocop/cop/style/redundant_condition.rb +5 -2
  104. data/lib/rubocop/cop/style/redundant_constant_base.rb +72 -0
  105. data/lib/rubocop/cop/style/redundant_each.rb +116 -0
  106. data/lib/rubocop/cop/style/redundant_initialize.rb +3 -1
  107. data/lib/rubocop/cop/style/redundant_regexp_character_class.rb +8 -1
  108. data/lib/rubocop/cop/style/redundant_regexp_escape.rb +12 -3
  109. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  110. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  111. data/lib/rubocop/cop/style/redundant_string_escape.rb +181 -0
  112. data/lib/rubocop/cop/style/require_order.rb +88 -0
  113. data/lib/rubocop/cop/style/rescue_modifier.rb +1 -1
  114. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  115. data/lib/rubocop/cop/style/select_by_regexp.rb +8 -4
  116. data/lib/rubocop/cop/style/static_class.rb +32 -1
  117. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  118. data/lib/rubocop/cop/style/symbol_array.rb +2 -0
  119. data/lib/rubocop/cop/style/symbol_proc.rb +3 -5
  120. data/lib/rubocop/cop/style/word_array.rb +2 -0
  121. data/lib/rubocop/cop/team.rb +4 -5
  122. data/lib/rubocop/cop/util.rb +2 -2
  123. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  124. data/lib/rubocop/cop/variable_force/variable_table.rb +1 -1
  125. data/lib/rubocop/cop/variable_force.rb +20 -29
  126. data/lib/rubocop/cops_documentation_generator.rb +2 -1
  127. data/lib/rubocop/ext/processed_source.rb +2 -0
  128. data/lib/rubocop/formatter/disabled_config_formatter.rb +25 -8
  129. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  130. data/lib/rubocop/formatter/offense_count_formatter.rb +8 -5
  131. data/lib/rubocop/formatter/worst_offenders_formatter.rb +6 -3
  132. data/lib/rubocop/formatter.rb +3 -1
  133. data/lib/rubocop/optimized_patterns.rb +38 -0
  134. data/lib/rubocop/options.rb +28 -16
  135. data/lib/rubocop/path_util.rb +14 -2
  136. data/lib/rubocop/result_cache.rb +1 -1
  137. data/lib/rubocop/rspec/cop_helper.rb +24 -1
  138. data/lib/rubocop/rspec/shared_contexts.rb +14 -1
  139. data/lib/rubocop/rspec/support.rb +2 -2
  140. data/lib/rubocop/runner.rb +15 -11
  141. data/lib/rubocop/server/cache.rb +5 -1
  142. data/lib/rubocop/server/cli.rb +9 -2
  143. data/lib/rubocop/server/client_command/exec.rb +5 -0
  144. data/lib/rubocop/server/core.rb +19 -2
  145. data/lib/rubocop/server/socket_reader.rb +5 -1
  146. data/lib/rubocop/server.rb +1 -1
  147. data/lib/rubocop/target_ruby.rb +1 -1
  148. data/lib/rubocop/version.rb +8 -3
  149. data/lib/rubocop.rb +18 -6
  150. metadata +18 -5
@@ -31,6 +31,7 @@ module RuboCop
31
31
  minimum_target_ruby_version 2.3
32
32
 
33
33
  MSG = 'Do not chain ordinary method call after safe navigation operator.'
34
+ PLUS_MINUS_METHODS = %i[+@ -@].freeze
34
35
 
35
36
  # @!method bad_method?(node)
36
37
  def_node_matcher :bad_method?, <<~PATTERN
@@ -42,7 +43,7 @@ module RuboCop
42
43
 
43
44
  def on_send(node)
44
45
  bad_method?(node) do |safe_nav, method|
45
- return if nil_methods.include?(method)
46
+ return if nil_methods.include?(method) || PLUS_MINUS_METHODS.include?(node.method_name)
46
47
 
47
48
  method_chain = method_chain(node)
48
49
  location =
@@ -61,17 +62,18 @@ module RuboCop
61
62
  # @param [RuboCop::AST::SendNode] send_node
62
63
  # @return [String]
63
64
  def add_safe_navigation_operator(offense_range:, send_node:)
64
- source = \
65
- if send_node.method?(:[]) || send_node.method?(:[]=)
65
+ source =
66
+ if (brackets = find_brackets(send_node))
66
67
  format(
67
- '%<method_name>s(%<arguments>s)',
68
- arguments: send_node.arguments.map(&:source).join(', '),
69
- method_name: send_node.method_name
68
+ '%<method_name>s(%<arguments>s)%<method_chain>s',
69
+ arguments: brackets.arguments.map(&:source).join(', '),
70
+ method_name: brackets.method_name,
71
+ method_chain: brackets.source_range.end.join(send_node.source_range.end).source
70
72
  )
71
73
  else
72
- offense_range.source.dup
74
+ offense_range.source
73
75
  end
74
- source.prepend('.') unless send_node.dot?
76
+ source.prepend('.') unless source.start_with?('.')
75
77
  source.prepend('&')
76
78
  end
77
79
 
@@ -93,6 +95,14 @@ module RuboCop
93
95
  chain = chain.parent if chain.send_type? && chain.parent&.call_type?
94
96
  chain
95
97
  end
98
+
99
+ def find_brackets(send_node)
100
+ return send_node if send_node.method?(:[]) || send_node.method?(:[]=)
101
+
102
+ send_node.descendants.detect do |node|
103
+ node.send_type? && (node.method?(:[]) || node.method?(:[]=))
104
+ end
105
+ end
96
106
  end
97
107
  end
98
108
  end
@@ -48,16 +48,17 @@ module RuboCop
48
48
  (send
49
49
  (const _ _) {:#{SEND_METHODS.join(' :')}}
50
50
  ({sym str} $#mixin_method?)
51
- $(const _ _))
51
+ $(const _ _)+)
52
52
  PATTERN
53
53
 
54
54
  def on_send(node)
55
- send_with_mixin_argument?(node) do |method, module_name|
56
- message = message(method, module_name.source, bad_location(node).source)
55
+ send_with_mixin_argument?(node) do |method, module_names|
56
+ module_names_source = module_names.map(&:source).join(', ')
57
+ message = message(method, module_names_source, bad_location(node).source)
57
58
 
58
59
  bad_location = bad_location(node)
59
60
  add_offense(bad_location, message: message) do |corrector|
60
- corrector.replace(bad_location, "#{method} #{module_name.source}")
61
+ corrector.replace(bad_location, "#{method} #{module_names_source}")
61
62
  end
62
63
  end
63
64
  end
@@ -155,16 +155,6 @@ module RuboCop
155
155
  end
156
156
  end
157
157
 
158
- # @param [RuboCop::AST::Node] rescue_group is a node of array_type
159
- def rescued_exceptions(rescue_group)
160
- klasses = *rescue_group
161
- klasses.map do |klass|
162
- next unless klass.const_type?
163
-
164
- klass.source
165
- end.compact
166
- end
167
-
168
158
  def find_shadowing_rescue(rescues)
169
159
  rescued_groups = rescued_groups_for(rescues)
170
160
  rescued_groups.zip(rescues).each do |group, res|
@@ -12,9 +12,12 @@ module RuboCop
12
12
  # because `Ractor` should not access outer variables.
13
13
  # eg. following style is encouraged:
14
14
  #
15
+ # [source,ruby]
16
+ # ----
15
17
  # worker_id, pipe = env
16
18
  # Ractor.new(worker_id, pipe) do |worker_id, pipe|
17
19
  # end
20
+ # ----
18
21
  #
19
22
  # @example
20
23
  #
@@ -70,10 +73,11 @@ module RuboCop
70
73
  outer_local_variable_node =
71
74
  find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
72
75
  return true unless outer_local_variable_node
76
+ return false unless outer_local_variable_node.conditional?
77
+ return true if variable_node == outer_local_variable_node
73
78
 
74
- outer_local_variable_node.conditional? &&
75
- (variable_node == outer_local_variable_node ||
76
- variable_node == outer_local_variable_node.else_branch)
79
+ outer_local_variable_node.if_type? &&
80
+ variable_node == outer_local_variable_node.else_branch
77
81
  end
78
82
 
79
83
  def variable_node(variable)
@@ -116,7 +116,7 @@ module RuboCop
116
116
  private
117
117
 
118
118
  def comment_between_rescue_and_end?(node)
119
- ancestor = node.each_ancestor(:kwbegin, :def, :defs, :block).first
119
+ ancestor = node.each_ancestor(:kwbegin, :def, :defs, :block, :numblock).first
120
120
  return unless ancestor
121
121
 
122
122
  end_line = ancestor.loc.end.line
@@ -79,7 +79,7 @@ module RuboCop
79
79
  # # bad
80
80
  # 2.times { raise ArgumentError }
81
81
  #
82
- # @example AllowedPatterns: [/(exactly|at_least|at_most)\(\d+\)\.times/] (default)
82
+ # @example AllowedPatterns: ['(exactly|at_least|at_most)\(\d+\)\.times'] (default)
83
83
  #
84
84
  # # good
85
85
  # exactly(2).times { raise StandardError }
@@ -68,6 +68,10 @@ module RuboCop
68
68
  (send nil? :fail ...)}
69
69
  PATTERN
70
70
 
71
+ def self.autocorrect_incompatible_with
72
+ [Style::ExplicitBlockArgument]
73
+ end
74
+
71
75
  def self.joining_forces
72
76
  VariableForce
73
77
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Lint
6
- # Checks for operators, variables, literals, and nonmutating
6
+ # Checks for operators, variables, literals, lambda, proc and nonmutating
7
7
  # methods used in void context.
8
8
  #
9
9
  # @example CheckForMethodsWithNoSideEffects: false (default)
@@ -45,7 +45,7 @@ module RuboCop
45
45
  VAR_MSG = 'Variable `%<var>s` used in void context.'
46
46
  LIT_MSG = 'Literal `%<lit>s` used in void context.'
47
47
  SELF_MSG = '`self` used in void context.'
48
- DEFINED_MSG = '`%<defined>s` used in void context.'
48
+ EXPRESSION_MSG = '`%<expression>s` used in void context.'
49
49
  NONMUTATING_MSG = 'Method `#%<method>s` used in void context. Did you mean `#%<method>s!`?'
50
50
 
51
51
  BINARY_OPERATORS = %i[* / % + - == === != < > <= >= <=>].freeze
@@ -87,7 +87,7 @@ module RuboCop
87
87
  check_literal(expr)
88
88
  check_var(expr)
89
89
  check_self(expr)
90
- check_defined(expr)
90
+ check_void_expression(expr)
91
91
  return unless cop_config['CheckForMethodsWithNoSideEffects']
92
92
 
93
93
  check_nonmutating(expr)
@@ -117,10 +117,10 @@ module RuboCop
117
117
  add_offense(node, message: SELF_MSG)
118
118
  end
119
119
 
120
- def check_defined(node)
121
- return unless node.defined_type?
120
+ def check_void_expression(node)
121
+ return unless node.defined_type? || node.lambda_or_proc?
122
122
 
123
- add_offense(node, message: format(DEFINED_MSG, defined: node.source))
123
+ add_offense(node, message: format(EXPRESSION_MSG, expression: node.source))
124
124
  end
125
125
 
126
126
  def check_nonmutating(node)
@@ -20,7 +20,7 @@ module RuboCop
20
20
  #
21
21
  # @example CountRepeatedAttributes: false (default is true)
22
22
  #
23
- # # `model` and `current_user`, refenced 3 times each,
23
+ # # `model` and `current_user`, referenced 3 times each,
24
24
  # # are each counted as only 1 branch each if
25
25
  # # `CountRepeatedAttributes` is set to 'false'
26
26
  #
@@ -8,8 +8,8 @@ module RuboCop
8
8
  # The maximum allowed length is configurable.
9
9
  # The cop can be configured to ignore blocks passed to certain methods.
10
10
  #
11
- # You can set literals you want to fold with `CountAsOne`.
12
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
11
+ # You can set constructs you want to fold with `CountAsOne`.
12
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
13
13
  # will be counted as one line regardless of its actual size.
14
14
  #
15
15
  #
@@ -17,7 +17,7 @@ module RuboCop
17
17
  # for backwards compatibility. Please use `AllowedMethods` and `AllowedPatterns`
18
18
  # instead. By default, there are no methods to allowed.
19
19
  #
20
- # @example CountAsOne: ['array', 'heredoc']
20
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
21
21
  #
22
22
  # something do
23
23
  # array = [ # +1
@@ -33,7 +33,12 @@ module RuboCop
33
33
  # Heredoc
34
34
  # content.
35
35
  # HEREDOC
36
- # end # 5 points
36
+ #
37
+ # foo( # +1
38
+ # 1,
39
+ # 2
40
+ # )
41
+ # end # 6 points
37
42
  #
38
43
  # NOTE: This cop does not apply for `Struct` definitions.
39
44
  class BlockLength < Base
@@ -7,11 +7,11 @@ module RuboCop
7
7
  # Comment lines can optionally be ignored.
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
- # You can set literals you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
10
+ # You can set constructs you want to fold with `CountAsOne`.
11
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
- # @example CountAsOne: ['array', 'heredoc']
14
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
15
  #
16
16
  # class Foo
17
17
  # ARRAY = [ # +1
@@ -27,7 +27,12 @@ module RuboCop
27
27
  # Heredoc
28
28
  # content.
29
29
  # HEREDOC
30
- # end # 5 points
30
+ #
31
+ # foo( # +1
32
+ # 1,
33
+ # 2
34
+ # )
35
+ # end # 6 points
31
36
  #
32
37
  #
33
38
  # NOTE: This cop also applies for `Struct` definitions.
@@ -7,8 +7,8 @@ module RuboCop
7
7
  # Comment lines can optionally be allowed.
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
- # You can set literals you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
10
+ # You can set constructs you want to fold with `CountAsOne`.
11
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
14
  # NOTE: The `ExcludedMethods` and `IgnoredMethods` configuration is
@@ -16,7 +16,7 @@ module RuboCop
16
16
  # Please use `AllowedMethods` and `AllowedPatterns` instead.
17
17
  # By default, there are no methods to allowed.
18
18
  #
19
- # @example CountAsOne: ['array', 'heredoc']
19
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
20
20
  #
21
21
  # def m
22
22
  # array = [ # +1
@@ -32,7 +32,12 @@ module RuboCop
32
32
  # Heredoc
33
33
  # content.
34
34
  # HEREDOC
35
- # end # 5 points
35
+ #
36
+ # foo( # +1
37
+ # 1,
38
+ # 2
39
+ # )
40
+ # end # 6 points
36
41
  #
37
42
  class MethodLength < Base
38
43
  include CodeLength
@@ -7,11 +7,11 @@ module RuboCop
7
7
  # Comment lines can optionally be ignored.
8
8
  # The maximum allowed length is configurable.
9
9
  #
10
- # You can set literals you want to fold with `CountAsOne`.
11
- # Available are: 'array', 'hash', and 'heredoc'. Each literal
10
+ # You can set constructs you want to fold with `CountAsOne`.
11
+ # Available are: 'array', 'hash', 'heredoc', and 'method_call'. Each construct
12
12
  # will be counted as one line regardless of its actual size.
13
13
  #
14
- # @example CountAsOne: ['array', 'heredoc']
14
+ # @example CountAsOne: ['array', 'heredoc', 'method_call']
15
15
  #
16
16
  # module M
17
17
  # ARRAY = [ # +1
@@ -27,7 +27,12 @@ module RuboCop
27
27
  # Heredoc
28
28
  # content.
29
29
  # HEREDOC
30
- # end # 5 points
30
+ #
31
+ # foo( # +1
32
+ # 1,
33
+ # 2
34
+ # )
35
+ # end # 6 points
31
36
  #
32
37
  class ModuleLength < Base
33
38
  include CodeLength
@@ -9,7 +9,7 @@ module RuboCop
9
9
  extend NodePattern::Macros
10
10
  include Util
11
11
 
12
- FOLDABLE_TYPES = %i[array hash heredoc].freeze
12
+ FOLDABLE_TYPES = %i[array hash heredoc send csend].freeze
13
13
  CLASSLIKE_TYPES = %i[class module].freeze
14
14
  private_constant :FOLDABLE_TYPES, :CLASSLIKE_TYPES
15
15
 
@@ -39,7 +39,7 @@ module RuboCop
39
39
 
40
40
  private
41
41
 
42
- def build_foldable_checks(types)
42
+ def build_foldable_checks(types) # rubocop:disable Metrics/MethodLength
43
43
  types.map do |type|
44
44
  case type
45
45
  when :array
@@ -48,6 +48,8 @@ module RuboCop
48
48
  ->(node) { node.hash_type? }
49
49
  when :heredoc
50
50
  ->(node) { heredoc_node?(node) }
51
+ when :method_call
52
+ ->(node) { node.call_type? }
51
53
  else
52
54
  raise ArgumentError, "Unknown foldable type: #{type.inspect}. " \
53
55
  "Valid foldable types are: #{FOLDABLE_TYPES.join(', ')}."
@@ -57,6 +59,7 @@ module RuboCop
57
59
 
58
60
  def normalize_foldable_types(types)
59
61
  types.concat(%i[str dstr]) if types.delete(:heredoc)
62
+ types.concat(%i[send csend]) if types.delete(:method_call)
60
63
  types
61
64
  end
62
65
 
@@ -22,6 +22,18 @@ module RuboCop
22
22
  processed_source.each_comment_in_lines(start_line...end_line)
23
23
  end
24
24
 
25
+ def comments_contain_disables?(node, cop_name)
26
+ disabled_ranges = processed_source.disabled_line_ranges[cop_name]
27
+
28
+ return unless disabled_ranges
29
+
30
+ node_range = node.source_range.line...find_end_line(node)
31
+
32
+ disabled_ranges.any? do |disable_range|
33
+ disable_range.cover?(node_range) || node_range.cover?(disable_range)
34
+ end
35
+ end
36
+
25
37
  private
26
38
 
27
39
  def end_position_for(node)
@@ -69,6 +69,10 @@ module RuboCop
69
69
  end
70
70
  end
71
71
 
72
+ def leading_magic_comments
73
+ leading_comment_lines.map { |line| MagicComment.parse(line) }
74
+ end
75
+
72
76
  def leading_comment_lines
73
77
  first_non_comment_token = processed_source.tokens.find { |token| !token.comment? }
74
78
 
@@ -47,6 +47,12 @@ module RuboCop
47
47
 
48
48
  def register_offense(node, message, replacement)
49
49
  add_offense(node.value, message: message) do |corrector|
50
+ if (def_node = def_node_that_require_parentheses(node))
51
+ white_spaces = range_between(def_node.loc.selector.end_pos,
52
+ def_node.first_argument.source_range.begin_pos)
53
+ corrector.replace(white_spaces, '(')
54
+ corrector.insert_after(def_node.arguments.last, ')')
55
+ end
50
56
  corrector.replace(node, replacement)
51
57
  end
52
58
  end
@@ -76,12 +82,25 @@ module RuboCop
76
82
  end
77
83
 
78
84
  def require_hash_value_for_around_hash_literal?(node)
79
- return false unless (ancestor = node.parent.parent)
80
- return false if ancestor.send_type? && ancestor.method?(:[])
85
+ return false unless (send_node = find_ancestor_send_node(node))
86
+
87
+ !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(send_node, node.parent) &&
88
+ use_modifier_form_without_parenthesized_method_call?(send_node)
89
+ end
90
+
91
+ def def_node_that_require_parentheses(node)
92
+ return unless (send_node = find_ancestor_send_node(node))
93
+ return unless without_parentheses_call_expr_follows?(send_node)
94
+
95
+ def_node = node.each_ancestor(:send, :csend).first
81
96
 
82
- !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(ancestor, node.parent) &&
83
- (use_modifier_form_without_parenthesized_method_call?(ancestor) ||
84
- without_parentheses_call_expr_follows?(ancestor))
97
+ def_node unless def_node && def_node.arguments.empty?
98
+ end
99
+
100
+ def find_ancestor_send_node(node)
101
+ ancestor = node.parent.parent
102
+
103
+ ancestor if ancestor&.call_type? && !ancestor&.method?(:[])
85
104
  end
86
105
 
87
106
  def use_element_of_hash_literal_as_receiver?(ancestor, parent)
@@ -96,11 +115,14 @@ module RuboCop
96
115
  end
97
116
 
98
117
  def without_parentheses_call_expr_follows?(ancestor)
118
+ return false unless ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized?
119
+
99
120
  right_sibling = ancestor.right_sibling
100
- right_sibling ||= ancestor.each_ancestor.find(&:assignment?)&.right_sibling
101
- return false unless right_sibling
121
+ right_sibling ||= ancestor.each_ancestor.find do |node|
122
+ node.assignment? || node.send_type?
123
+ end&.right_sibling
102
124
 
103
- ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized? && !!right_sibling
125
+ !!right_sibling
104
126
  end
105
127
 
106
128
  def breakdown_value_types_of_hash(hash_node)
@@ -126,6 +126,29 @@ module RuboCop
126
126
  pos += size * step while condition && src[pos + offset, size] == needle
127
127
  pos.negative? ? 0 : pos
128
128
  end
129
+
130
+ def range_with_comments_and_lines(node)
131
+ range_by_whole_lines(range_with_comments(node), include_final_newline: true)
132
+ end
133
+
134
+ def range_with_comments(node)
135
+ ranges = [
136
+ node,
137
+ *@processed_source.ast_with_comments[node]
138
+ ].map do |element|
139
+ element.location.expression
140
+ end
141
+ ranges.reduce do |result, range|
142
+ add_range(result, range)
143
+ end
144
+ end
145
+
146
+ def add_range(range1, range2)
147
+ range1.with(
148
+ begin_pos: [range1.begin_pos, range2.begin_pos].min,
149
+ end_pos: [range1.end_pos, range2.end_pos].max
150
+ )
151
+ end
129
152
  end
130
153
  end
131
154
  end
@@ -11,7 +11,9 @@ module RuboCop
11
11
  private
12
12
 
13
13
  def rescue_modifier?(node)
14
- node&.resbody_type? && @modifier_locations.include?(node.loc.keyword)
14
+ return false unless node.respond_to?(:resbody_type?)
15
+
16
+ node.resbody_type? && @modifier_locations.include?(node.loc.keyword)
15
17
  end
16
18
 
17
19
  # @deprecated Use ResbodyNode#exceptions instead
@@ -48,11 +48,25 @@ module RuboCop
48
48
  end
49
49
 
50
50
  def to_modifier_form(node)
51
- expression = [node.body.source, node.keyword, node.condition.source].compact.join(' ')
51
+ body = if_body_source(node.body)
52
+ expression = [body, node.keyword, node.condition.source].compact.join(' ')
52
53
  parenthesized = parenthesize?(node) ? "(#{expression})" : expression
53
54
  [parenthesized, first_line_comment(node)].compact.join(' ')
54
55
  end
55
56
 
57
+ def if_body_source(if_body)
58
+ if if_body.call_type? &&
59
+ if_body.last_argument&.hash_type? && if_body.last_argument.pairs.last.value_omission?
60
+ "#{method_source(if_body)}(#{if_body.arguments.map(&:source).join(', ')})"
61
+ else
62
+ if_body.source
63
+ end
64
+ end
65
+
66
+ def method_source(if_body)
67
+ range_between(if_body.loc.expression.begin_pos, if_body.loc.selector.end_pos).source
68
+ end
69
+
56
70
  def first_line_comment(node)
57
71
  comment = processed_source.find_comment { |c| same_line?(c, node) }
58
72
  return unless comment
@@ -13,7 +13,7 @@ module RuboCop
13
13
 
14
14
  private
15
15
 
16
- def side_space_range(range:, side:)
16
+ def side_space_range(range:, side:, include_newlines: false)
17
17
  buffer = processed_source.buffer
18
18
  src = buffer.source
19
19
 
@@ -21,11 +21,11 @@ module RuboCop
21
21
  end_pos = range.end_pos
22
22
  if side == :left
23
23
  end_pos = begin_pos
24
- begin_pos = reposition(src, begin_pos, -1)
24
+ begin_pos = reposition(src, begin_pos, -1, include_newlines: include_newlines)
25
25
  end
26
26
  if side == :right
27
27
  begin_pos = end_pos
28
- end_pos = reposition(src, end_pos, 1)
28
+ end_pos = reposition(src, end_pos, 1, include_newlines: include_newlines)
29
29
  end
30
30
  Parser::Source::Range.new(buffer, begin_pos, end_pos)
31
31
  end
@@ -75,9 +75,10 @@ module RuboCop
75
75
  end
76
76
  end
77
77
 
78
- def reposition(src, pos, step)
78
+ def reposition(src, pos, step, include_newlines: false)
79
79
  offset = step == -1 ? -1 : 0
80
- pos += step while SINGLE_SPACE_REGEXP.match?(src[pos + offset])
80
+ pos += step while SINGLE_SPACE_REGEXP.match?(src[pos + offset]) ||
81
+ (include_newlines && src[pos + offset] == "\n")
81
82
  pos.negative? ? 0 : pos
82
83
  end
83
84
 
@@ -117,14 +118,15 @@ module RuboCop
117
118
  end
118
119
 
119
120
  def offending_empty_no_space?(config, left_token, right_token)
120
- config == 'no_space' && !no_space_between?(left_token, right_token)
121
+ config == 'no_space' && !no_character_between?(left_token, right_token)
121
122
  end
122
123
 
123
124
  def space_between?(left_bracket_token, right_bracket_token)
124
- left_bracket_token.end_pos + 1 == right_bracket_token.begin_pos
125
+ left_bracket_token.end_pos + 1 == right_bracket_token.begin_pos &&
126
+ processed_source.buffer.source[left_bracket_token.end_pos] == ' '
125
127
  end
126
128
 
127
- def no_space_between?(left_bracket_token, right_bracket_token)
129
+ def no_character_between?(left_bracket_token, right_bracket_token)
128
130
  left_bracket_token.end_pos == right_bracket_token.begin_pos
129
131
  end
130
132
  end
@@ -1,18 +1,43 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'set'
4
+
3
5
  module RuboCop
4
6
  module Cop
5
7
  # Help methods for determining node visibility.
6
8
  module VisibilityHelp
7
9
  extend NodePattern::Macros
8
10
 
9
- VISIBILITY_SCOPES = %i[private protected public].freeze
11
+ VISIBILITY_SCOPES = ::Set[:private, :protected, :public].freeze
10
12
 
11
13
  private
12
14
 
13
15
  def node_visibility(node)
14
- scope = find_visibility_start(node)
15
- scope&.method_name || :public
16
+ node_visibility_from_visibility_inline(node) ||
17
+ node_visibility_from_visibility_block(node) ||
18
+ :public
19
+ end
20
+
21
+ def node_visibility_from_visibility_inline(node)
22
+ return unless node.def_type?
23
+
24
+ node_visibility_from_visibility_inline_on_def(node) ||
25
+ node_visibility_from_visibility_inline_on_method_name(node)
26
+ end
27
+
28
+ def node_visibility_from_visibility_inline_on_def(node)
29
+ parent = node.parent
30
+ parent.method_name if visibility_inline_on_def?(parent)
31
+ end
32
+
33
+ def node_visibility_from_visibility_inline_on_method_name(node)
34
+ node.right_siblings.reverse.find do |sibling|
35
+ visibility_inline_on_method_name?(sibling, method_name: node.method_name)
36
+ end&.method_name
37
+ end
38
+
39
+ def node_visibility_from_visibility_block(node)
40
+ find_visibility_start(node)&.method_name
16
41
  end
17
42
 
18
43
  def find_visibility_start(node)
@@ -21,7 +46,7 @@ module RuboCop
21
46
 
22
47
  # Navigate to find the last protected method
23
48
  def find_visibility_end(node)
24
- possible_visibilities = VISIBILITY_SCOPES - [node_visibility(node)]
49
+ possible_visibilities = VISIBILITY_SCOPES - ::Set[node_visibility(node)]
25
50
  right = node.right_siblings
26
51
  right.find do |child_node|
27
52
  possible_visibilities.include?(node_visibility(child_node))
@@ -30,7 +55,17 @@ module RuboCop
30
55
 
31
56
  # @!method visibility_block?(node)
32
57
  def_node_matcher :visibility_block?, <<~PATTERN
33
- (send nil? { :private :protected :public })
58
+ (send nil? VISIBILITY_SCOPES)
59
+ PATTERN
60
+
61
+ # @!method visibility_inline_on_def?(node)
62
+ def_node_matcher :visibility_inline_on_def?, <<~PATTERN
63
+ (send nil? VISIBILITY_SCOPES def)
64
+ PATTERN
65
+
66
+ # @!method visibility_inline_on_method_name?(node, method_name:)
67
+ def_node_matcher :visibility_inline_on_method_name?, <<~PATTERN
68
+ (send nil? VISIBILITY_SCOPES (sym %method_name))
34
69
  PATTERN
35
70
  end
36
71
  end