rubocop 1.39.0 → 1.41.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 (113) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +65 -9
  4. data/exe/rubocop +1 -1
  5. data/lib/rubocop/comment_config.rb +5 -0
  6. data/lib/rubocop/config.rb +33 -9
  7. data/lib/rubocop/config_loader.rb +14 -5
  8. data/lib/rubocop/config_loader_resolver.rb +1 -1
  9. data/lib/rubocop/config_validator.rb +1 -1
  10. data/lib/rubocop/cop/badge.rb +9 -4
  11. data/lib/rubocop/cop/base.rb +26 -17
  12. data/lib/rubocop/cop/commissioner.rb +8 -3
  13. data/lib/rubocop/cop/cop.rb +1 -1
  14. data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +22 -6
  15. data/lib/rubocop/cop/internal_affairs/cop_description.rb +3 -1
  16. data/lib/rubocop/cop/internal_affairs/lambda_or_proc.rb +46 -0
  17. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  18. data/lib/rubocop/cop/layout/empty_lines.rb +2 -0
  19. data/lib/rubocop/cop/layout/extra_spacing.rb +10 -6
  20. data/lib/rubocop/cop/layout/first_array_element_line_break.rb +38 -2
  21. data/lib/rubocop/cop/layout/first_hash_element_line_break.rb +49 -2
  22. data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +61 -2
  23. data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +52 -2
  24. data/lib/rubocop/cop/layout/indentation_style.rb +3 -1
  25. data/lib/rubocop/cop/layout/line_continuation_leading_space.rb +5 -0
  26. data/lib/rubocop/cop/layout/line_continuation_spacing.rb +7 -1
  27. data/lib/rubocop/cop/layout/line_length.rb +2 -0
  28. data/lib/rubocop/cop/layout/multiline_array_line_breaks.rb +51 -2
  29. data/lib/rubocop/cop/layout/multiline_hash_key_line_breaks.rb +49 -2
  30. data/lib/rubocop/cop/layout/multiline_method_argument_line_breaks.rb +53 -2
  31. data/lib/rubocop/cop/layout/multiline_method_parameter_line_breaks.rb +58 -2
  32. data/lib/rubocop/cop/layout/redundant_line_break.rb +2 -2
  33. data/lib/rubocop/cop/layout/trailing_empty_lines.rb +1 -1
  34. data/lib/rubocop/cop/layout/trailing_whitespace.rb +6 -2
  35. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  36. data/lib/rubocop/cop/lint/assignment_in_condition.rb +11 -1
  37. data/lib/rubocop/cop/lint/constant_resolution.rb +4 -0
  38. data/lib/rubocop/cop/lint/debugger.rb +3 -1
  39. data/lib/rubocop/cop/lint/deprecated_constants.rb +8 -1
  40. data/lib/rubocop/cop/lint/duplicate_branch.rb +0 -2
  41. data/lib/rubocop/cop/lint/duplicate_methods.rb +19 -8
  42. data/lib/rubocop/cop/lint/empty_block.rb +1 -5
  43. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  44. data/lib/rubocop/cop/lint/interpolation_check.rb +4 -3
  45. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +10 -5
  46. data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +5 -0
  47. data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +13 -3
  48. data/lib/rubocop/cop/lint/redundant_cop_enable_directive.rb +1 -1
  49. data/lib/rubocop/cop/lint/safe_navigation_chain.rb +10 -12
  50. data/lib/rubocop/cop/lint/send_with_mixin_argument.rb +5 -4
  51. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +4 -3
  52. data/lib/rubocop/cop/lint/void.rb +6 -6
  53. data/lib/rubocop/cop/metrics/block_length.rb +9 -4
  54. data/lib/rubocop/cop/metrics/class_length.rb +10 -5
  55. data/lib/rubocop/cop/metrics/method_length.rb +9 -4
  56. data/lib/rubocop/cop/metrics/module_length.rb +10 -5
  57. data/lib/rubocop/cop/metrics/utils/code_length_calculator.rb +6 -3
  58. data/lib/rubocop/cop/mixin/alignment.rb +1 -1
  59. data/lib/rubocop/cop/mixin/allowed_identifiers.rb +2 -2
  60. data/lib/rubocop/cop/mixin/annotation_comment.rb +13 -6
  61. data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +21 -9
  62. data/lib/rubocop/cop/mixin/first_element_line_break.rb +11 -7
  63. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +28 -5
  64. data/lib/rubocop/cop/mixin/line_length_help.rb +8 -1
  65. data/lib/rubocop/cop/mixin/method_complexity.rb +5 -3
  66. data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +5 -3
  67. data/lib/rubocop/cop/mixin/percent_array.rb +3 -5
  68. data/lib/rubocop/cop/mixin/require_library.rb +2 -0
  69. data/lib/rubocop/cop/mixin/rescue_node.rb +3 -3
  70. data/lib/rubocop/cop/mixin/statement_modifier.rb +15 -1
  71. data/lib/rubocop/cop/naming/class_and_module_camel_case.rb +2 -0
  72. data/lib/rubocop/cop/naming/inclusive_language.rb +4 -1
  73. data/lib/rubocop/cop/registry.rb +29 -14
  74. data/lib/rubocop/cop/style/array_intersect.rb +111 -0
  75. data/lib/rubocop/cop/style/concat_array_literals.rb +66 -0
  76. data/lib/rubocop/cop/style/documentation.rb +1 -1
  77. data/lib/rubocop/cop/style/guard_clause.rb +36 -5
  78. data/lib/rubocop/cop/style/if_with_semicolon.rb +4 -4
  79. data/lib/rubocop/cop/style/inverse_methods.rb +2 -0
  80. data/lib/rubocop/cop/style/line_end_concatenation.rb +4 -1
  81. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  82. data/lib/rubocop/cop/style/redundant_argument.rb +3 -0
  83. data/lib/rubocop/cop/style/redundant_constant_base.rb +85 -0
  84. data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +39 -0
  85. data/lib/rubocop/cop/style/redundant_return.rb +7 -0
  86. data/lib/rubocop/cop/style/redundant_sort.rb +1 -1
  87. data/lib/rubocop/cop/style/require_order.rb +140 -0
  88. data/lib/rubocop/cop/style/safe_navigation.rb +35 -6
  89. data/lib/rubocop/cop/style/select_by_regexp.rb +8 -4
  90. data/lib/rubocop/cop/style/semicolon.rb +2 -1
  91. data/lib/rubocop/cop/style/string_literals.rb +1 -5
  92. data/lib/rubocop/cop/style/symbol_proc.rb +2 -4
  93. data/lib/rubocop/cop/team.rb +1 -1
  94. data/lib/rubocop/cop/util.rb +32 -5
  95. data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
  96. data/lib/rubocop/cop/variable_force.rb +20 -29
  97. data/lib/rubocop/cops_documentation_generator.rb +22 -3
  98. data/lib/rubocop/directive_comment.rb +1 -1
  99. data/lib/rubocop/file_patterns.rb +43 -0
  100. data/lib/rubocop/formatter/disabled_config_formatter.rb +17 -6
  101. data/lib/rubocop/formatter/html_formatter.rb +1 -1
  102. data/lib/rubocop/formatter.rb +3 -1
  103. data/lib/rubocop/options.rb +8 -0
  104. data/lib/rubocop/path_util.rb +34 -16
  105. data/lib/rubocop/result_cache.rb +1 -1
  106. data/lib/rubocop/rspec/cop_helper.rb +4 -1
  107. data/lib/rubocop/rspec/support.rb +2 -2
  108. data/lib/rubocop/server/core.rb +1 -1
  109. data/lib/rubocop/target_finder.rb +1 -1
  110. data/lib/rubocop/target_ruby.rb +1 -1
  111. data/lib/rubocop/version.rb +1 -1
  112. data/lib/rubocop.rb +16 -6
  113. metadata +10 -3
@@ -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
@@ -73,10 +73,11 @@ module RuboCop
73
73
  outer_local_variable_node =
74
74
  find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
75
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
76
78
 
77
- outer_local_variable_node.conditional? &&
78
- (variable_node == outer_local_variable_node ||
79
- variable_node == outer_local_variable_node.else_branch)
79
+ outer_local_variable_node.if_type? &&
80
+ variable_node == outer_local_variable_node.else_branch
80
81
  end
81
82
 
82
83
  def variable_node(variable)
@@ -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)
@@ -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
@@ -3,15 +3,15 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Metrics
6
- # Checks if the length a class exceeds some maximum value.
6
+ # Checks if the length of a class exceeds some maximum value.
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
@@ -3,15 +3,15 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Metrics
6
- # Checks if the length a module exceeds some maximum value.
6
+ # Checks if the length of a module exceeds some maximum value.
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(', ')}."
@@ -56,7 +58,8 @@ module RuboCop
56
58
  end
57
59
 
58
60
  def normalize_foldable_types(types)
59
- types.concat(%i[str dstr]) if types.delete(:heredoc)
61
+ types.push(:str, :dstr) if types.delete(:heredoc)
62
+ types.push(:send, :csend) if types.delete(:method_call)
60
63
  types
61
64
  end
62
65
 
@@ -28,7 +28,7 @@ module RuboCop
28
28
 
29
29
  each_bad_alignment(items, base_column) do |current|
30
30
  expr = current.source_range
31
- if @current_offenses.any? { |o| within?(expr, o.location) }
31
+ if @current_offenses&.any? { |o| within?(expr, o.location) }
32
32
  # If this offense is within a line range that is already being
33
33
  # realigned by autocorrect, we report the offense without
34
34
  # autocorrecting it. Two rewrites in the same area by the same
@@ -7,11 +7,11 @@ module RuboCop
7
7
  SIGILS = '@$' # if a variable starts with a sigil it will be removed
8
8
 
9
9
  def allowed_identifier?(name)
10
- allowed_identifiers.include?(name.to_s.delete(SIGILS))
10
+ !allowed_identifiers.empty? && allowed_identifiers.include?(name.to_s.delete(SIGILS))
11
11
  end
12
12
 
13
13
  def allowed_identifiers
14
- cop_config.fetch('AllowedIdentifiers', [])
14
+ cop_config.fetch('AllowedIdentifiers') { [] }
15
15
  end
16
16
  end
17
17
  end
@@ -41,18 +41,25 @@ module RuboCop
41
41
  def split_comment(comment)
42
42
  # Sort keywords by reverse length so that if a keyword is in a phrase
43
43
  # but also on its own, both will match properly.
44
- keywords_regex = Regexp.new(
45
- Regexp.union(keywords.sort_by { |w| -w.length }).source,
46
- Regexp::IGNORECASE
47
- )
48
- regex = /^(# ?)(\b#{keywords_regex}\b)(\s*:)?(\s+)?(\S+)?/i
49
-
50
44
  match = comment.text.match(regex)
51
45
  return false unless match
52
46
 
53
47
  match.captures
54
48
  end
55
49
 
50
+ KEYWORDS_REGEX_CACHE = {} # rubocop:disable Layout/ClassStructure, Style/MutableConstant
51
+ private_constant :KEYWORDS_REGEX_CACHE
52
+
53
+ def regex
54
+ KEYWORDS_REGEX_CACHE[keywords] ||= begin
55
+ keywords_regex = Regexp.new(
56
+ Regexp.union(keywords.sort_by { |w| -w.length }).source,
57
+ Regexp::IGNORECASE
58
+ )
59
+ /^(# ?)(\b#{keywords_regex}\b)(\s*:)?(\s+)?(\S+)?/i
60
+ end
61
+ end
62
+
56
63
  def keyword_appearance?
57
64
  keyword && (colon || space)
58
65
  end
@@ -20,19 +20,30 @@ module RuboCop
20
20
  style_detected(possibilities)
21
21
  end
22
22
 
23
+ SYMBOL_TO_STRING_CACHE = Hash.new do |hash, key|
24
+ hash[key] = key.to_s if key.is_a?(Symbol)
25
+ end
26
+ private_constant :SYMBOL_TO_STRING_CACHE
27
+
28
+ # rubocop:disable Metrics
23
29
  def style_detected(detected)
24
30
  return if no_acceptable_style?
25
31
 
26
- # `detected` can be a single style or an Array of possible styles
27
- # (if there is more than one which matches the observed code)
28
- detected_as_strings = Array(detected).map(&:to_s)
32
+ # This logic is more complex than it needs to be
33
+ # to avoid allocating Arrays in the hot code path.
34
+ updated_list =
35
+ if detected_style
36
+ if detected_style.size == 1 && detected_style.include?(SYMBOL_TO_STRING_CACHE[detected])
37
+ detected_style
38
+ else
39
+ detected_as_strings = SYMBOL_TO_STRING_CACHE.values_at(*detected)
40
+ detected_style & detected_as_strings
41
+ end
42
+ else
43
+ # We haven't observed any specific style yet.
44
+ SYMBOL_TO_STRING_CACHE.values_at(*detected)
45
+ end
29
46
 
30
- updated_list = if detected_style
31
- detected_style & detected_as_strings
32
- else
33
- # We haven't observed any specific style yet.
34
- detected_as_strings
35
- end
36
47
  if updated_list.empty?
37
48
  no_acceptable_style!
38
49
  else
@@ -40,6 +51,7 @@ module RuboCop
40
51
  config_to_allow_offenses[style_parameter_name] = updated_list.first
41
52
  end
42
53
  end
54
+ # rubocop:enable Metrics
43
55
 
44
56
  def no_acceptable_style?
45
57
  config_to_allow_offenses['Enabled'] == false
@@ -7,12 +7,12 @@ module RuboCop
7
7
  module FirstElementLineBreak
8
8
  private
9
9
 
10
- def check_method_line_break(node, children)
10
+ def check_method_line_break(node, children, ignore_last: false)
11
11
  return if children.empty?
12
12
 
13
13
  return unless method_uses_parens?(node, children.first)
14
14
 
15
- check_children_line_break(node, children)
15
+ check_children_line_break(node, children, ignore_last: ignore_last)
16
16
  end
17
17
 
18
18
  def method_uses_parens?(node, limit)
@@ -20,7 +20,7 @@ module RuboCop
20
20
  /\s*\(\s*$/.match?(source)
21
21
  end
22
22
 
23
- def check_children_line_break(node, children, start = node)
23
+ def check_children_line_break(node, children, start = node, ignore_last: false)
24
24
  return if children.empty?
25
25
 
26
26
  line = start.first_line
@@ -28,8 +28,8 @@ module RuboCop
28
28
  min = first_by_line(children)
29
29
  return if line != min.first_line
30
30
 
31
- max = last_by_line(children)
32
- return if line == max.last_line
31
+ max_line = last_line(children, ignore_last: ignore_last)
32
+ return if line == max_line
33
33
 
34
34
  add_offense(min) { |corrector| EmptyLineCorrector.insert_before(corrector, min) }
35
35
  end
@@ -38,8 +38,12 @@ module RuboCop
38
38
  nodes.min_by(&:first_line)
39
39
  end
40
40
 
41
- def last_by_line(nodes)
42
- nodes.max_by(&:last_line)
41
+ def last_line(nodes, ignore_last:)
42
+ if ignore_last
43
+ nodes.map(&:first_line)
44
+ else
45
+ nodes.map(&:last_line)
46
+ end.max
43
47
  end
44
48
  end
45
49
  end
@@ -45,11 +45,21 @@ module RuboCop
45
45
 
46
46
  private
47
47
 
48
+ # rubocop:disable Metrics/AbcSize
48
49
  def register_offense(node, message, replacement)
49
50
  add_offense(node.value, message: message) do |corrector|
51
+ if (def_node = def_node_that_require_parentheses(node))
52
+ white_spaces = range_between(def_node.loc.selector.end_pos,
53
+ def_node.first_argument.source_range.begin_pos)
54
+ corrector.replace(white_spaces, '(')
55
+
56
+ last_argument = def_node.arguments.last
57
+ corrector.insert_after(last_argument, ')') if node == last_argument.pairs.last
58
+ end
50
59
  corrector.replace(node, replacement)
51
60
  end
52
61
  end
62
+ # rubocop:enable Metrics/AbcSize
53
63
 
54
64
  def ignore_mixed_hash_shorthand_syntax?(hash_node)
55
65
  target_ruby_version <= 3.0 || enforced_shorthand_syntax != 'consistent' ||
@@ -76,12 +86,25 @@ module RuboCop
76
86
  end
77
87
 
78
88
  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?(:[])
89
+ return false unless (send_node = find_ancestor_send_node(node))
90
+
91
+ !node.parent.braces? && !use_element_of_hash_literal_as_receiver?(send_node, node.parent) &&
92
+ use_modifier_form_without_parenthesized_method_call?(send_node)
93
+ end
94
+
95
+ def def_node_that_require_parentheses(node)
96
+ return unless (send_node = find_ancestor_send_node(node))
97
+ return unless without_parentheses_call_expr_follows?(send_node)
98
+
99
+ def_node = node.each_ancestor(:send, :csend).first
100
+
101
+ def_node unless def_node && def_node.arguments.empty?
102
+ end
103
+
104
+ def find_ancestor_send_node(node)
105
+ ancestor = node.parent.parent
81
106
 
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))
107
+ ancestor if ancestor&.call_type? && !ancestor&.method?(:[])
85
108
  end
86
109
 
87
110
  def use_element_of_hash_literal_as_receiver?(ancestor, parent)
@@ -57,7 +57,14 @@ module RuboCop
57
57
  def indentation_difference(line)
58
58
  return 0 unless tab_indentation_width
59
59
 
60
- (line.index(/[^\t]/) || 0) * (tab_indentation_width - 1)
60
+ index =
61
+ if line.match?(/^[^\t]/)
62
+ 0
63
+ else
64
+ line.index(/[^\t]/) || 0
65
+ end
66
+
67
+ index * (tab_indentation_width - 1)
61
68
  end
62
69
 
63
70
  def extend_uri_end_position(line, end_position)
@@ -59,13 +59,15 @@ module RuboCop
59
59
  end
60
60
 
61
61
  def complexity(body)
62
- body.each_node(:lvasgn, *self.class::COUNTED_NODES).reduce(1) do |score, node|
62
+ score = 1
63
+ body.each_node(:lvasgn, *self.class::COUNTED_NODES) do |node|
63
64
  if node.lvasgn_type?
64
65
  reset_on_lvasgn(node)
65
- next score
66
+ else
67
+ score += complexity_score_for(node)
66
68
  end
67
- score + complexity_score_for(node)
68
69
  end
70
+ score
69
71
  end
70
72
  end
71
73
  end
@@ -10,8 +10,8 @@ module RuboCop
10
10
  module MultilineElementLineBreaks
11
11
  private
12
12
 
13
- def check_line_breaks(_node, children)
14
- return if all_on_same_line?(children)
13
+ def check_line_breaks(_node, children, ignore_last: false)
14
+ return if all_on_same_line?(children, ignore_last: ignore_last)
15
15
 
16
16
  last_seen_line = -1
17
17
  children.each do |child|
@@ -23,9 +23,11 @@ module RuboCop
23
23
  end
24
24
  end
25
25
 
26
- def all_on_same_line?(nodes)
26
+ def all_on_same_line?(nodes, ignore_last: false)
27
27
  return true if nodes.empty?
28
28
 
29
+ return same_line?(nodes.first, nodes.last) if ignore_last
30
+
29
31
  nodes.first.first_line == nodes.last.last_line
30
32
  end
31
33
  end
@@ -97,9 +97,7 @@ module RuboCop
97
97
  # @return [String]
98
98
  def whitespace_between(node)
99
99
  if node.children.length >= 2
100
- node.source[
101
- node.children[0].loc.expression.end_pos...node.children[1].loc.expression.begin_pos
102
- ]
100
+ node.children[0].source_range.end.join(node.children[1].source_range.begin).source
103
101
  else
104
102
  ' '
105
103
  end
@@ -111,7 +109,7 @@ module RuboCop
111
109
  # @param [RuboCop::AST::ArrayNode] node
112
110
  # @return [String]
113
111
  def whitespace_leading(node)
114
- node.source[node.loc.begin.end_pos...node.children[0].loc.expression.begin_pos]
112
+ node.loc.begin.end.join(node.children[0].source_range.begin).source
115
113
  end
116
114
 
117
115
  # Provides trailing whitespace for building a bracketed array.
@@ -120,7 +118,7 @@ module RuboCop
120
118
  # @param [RuboCop::AST::ArrayNode] node
121
119
  # @return [String]
122
120
  def whitespace_trailing(node)
123
- node.source[node.children[-1].loc.expression.end_pos...node.loc.end.begin_pos]
121
+ node.children[-1].source_range.end.join(node.loc.end.begin).source
124
122
  end
125
123
  end
126
124
  end
@@ -7,6 +7,8 @@ module RuboCop
7
7
  module RequireLibrary
8
8
  extend NodePattern::Macros
9
9
 
10
+ RESTRICT_ON_SEND = [:require].freeze
11
+
10
12
  def ensure_required(corrector, node, library_name)
11
13
  node = node.parent while node.parent&.parent?
12
14
 
@@ -4,8 +4,8 @@ module RuboCop
4
4
  module Cop
5
5
  # Common functionality for checking `rescue` nodes.
6
6
  module RescueNode
7
- def on_new_investigation
8
- @modifier_locations = processed_source.tokens.select(&:rescue_modifier?).map(&:pos)
7
+ def modifier_locations
8
+ @modifier_locations ||= processed_source.tokens.select(&:rescue_modifier?).map!(&:pos)
9
9
  end
10
10
 
11
11
  private
@@ -13,7 +13,7 @@ module RuboCop
13
13
  def rescue_modifier?(node)
14
14
  return false unless node.respond_to?(:resbody_type?)
15
15
 
16
- node.resbody_type? && @modifier_locations.include?(node.loc.keyword)
16
+ node.resbody_type? && modifier_locations.include?(node.loc.keyword)
17
17
  end
18
18
 
19
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
@@ -30,6 +30,8 @@ module RuboCop
30
30
  MSG = 'Use CamelCase for classes and modules.'
31
31
 
32
32
  def on_class(node)
33
+ return unless node.loc.name.source.include?('_')
34
+
33
35
  allowed = /#{cop_config['AllowedNames'].join('|')}/
34
36
  name = node.loc.name.source.gsub(allowed, '')
35
37
  return unless /_/.match?(name)
@@ -207,7 +207,10 @@ module RuboCop
207
207
  end
208
208
 
209
209
  def scan_for_words(input)
210
- mask_input(input).enum_for(:scan, @flagged_terms_regex).map do
210
+ masked_input = mask_input(input)
211
+ return EMPTY_ARRAY unless masked_input.match?(@flagged_terms_regex)
212
+
213
+ masked_input.enum_for(:scan, @flagged_terms_regex).map do
211
214
  match = Regexp.last_match
212
215
  WordLocation.new(match.to_s, match.offset(0).first)
213
216
  end