rubocop 1.36.0 → 1.40.0

Sign up to get free protection for your applications and to get access to all the features.
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