rubocop 1.34.1 → 1.36.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +33 -0
  4. data/lib/rubocop/cli/command/{auto_genenerate_config.rb → auto_generate_config.rb} +0 -0
  5. data/lib/rubocop/config.rb +1 -1
  6. data/lib/rubocop/cop/correctors/parentheses_corrector.rb +32 -2
  7. data/lib/rubocop/cop/gemspec/require_mfa.rb +1 -1
  8. data/lib/rubocop/cop/generator/require_file_injector.rb +2 -2
  9. data/lib/rubocop/cop/internal_affairs/numblock_handler.rb +69 -0
  10. data/lib/rubocop/cop/internal_affairs/single_line_comparison.rb +5 -4
  11. data/lib/rubocop/cop/internal_affairs.rb +1 -0
  12. data/lib/rubocop/cop/layout/block_alignment.rb +16 -12
  13. data/lib/rubocop/cop/layout/block_end_newline.rb +2 -0
  14. data/lib/rubocop/cop/layout/empty_lines_around_access_modifier.rb +5 -2
  15. data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +2 -0
  16. data/lib/rubocop/cop/layout/end_of_line.rb +4 -4
  17. data/lib/rubocop/cop/layout/first_array_element_indentation.rb +2 -2
  18. data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +2 -2
  19. data/lib/rubocop/cop/layout/indentation_width.rb +5 -1
  20. data/lib/rubocop/cop/layout/line_length.rb +4 -1
  21. data/lib/rubocop/cop/layout/multiline_block_layout.rb +2 -0
  22. data/lib/rubocop/cop/layout/redundant_line_break.rb +1 -1
  23. data/lib/rubocop/cop/layout/space_around_block_parameters.rb +1 -1
  24. data/lib/rubocop/cop/layout/space_around_keyword.rb +1 -1
  25. data/lib/rubocop/cop/layout/space_before_block_braces.rb +2 -0
  26. data/lib/rubocop/cop/layout/space_inside_block_braces.rb +25 -9
  27. data/lib/rubocop/cop/lint/ambiguous_block_association.rb +1 -1
  28. data/lib/rubocop/cop/lint/deprecated_class_methods.rb +4 -4
  29. data/lib/rubocop/cop/lint/duplicate_require.rb +1 -1
  30. data/lib/rubocop/cop/lint/empty_block.rb +1 -1
  31. data/lib/rubocop/cop/lint/empty_conditional_body.rb +31 -1
  32. data/lib/rubocop/cop/lint/erb_new_arguments.rb +9 -9
  33. data/lib/rubocop/cop/lint/literal_in_interpolation.rb +4 -0
  34. data/lib/rubocop/cop/lint/next_without_accumulator.rb +25 -6
  35. data/lib/rubocop/cop/lint/non_atomic_file_operation.rb +6 -6
  36. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +12 -0
  37. data/lib/rubocop/cop/lint/redundant_safe_navigation.rb +9 -3
  38. data/lib/rubocop/cop/lint/redundant_with_index.rb +13 -10
  39. data/lib/rubocop/cop/lint/redundant_with_object.rb +12 -11
  40. data/lib/rubocop/cop/lint/shadowed_exception.rb +1 -1
  41. data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +15 -2
  42. data/lib/rubocop/cop/lint/unreachable_loop.rb +8 -2
  43. data/lib/rubocop/cop/lint/useless_access_modifier.rb +8 -6
  44. data/lib/rubocop/cop/lint/useless_ruby2_keywords.rb +1 -1
  45. data/lib/rubocop/cop/lint/void.rb +2 -0
  46. data/lib/rubocop/cop/mixin/allowed_methods.rb +10 -5
  47. data/lib/rubocop/cop/mixin/allowed_pattern.rb +13 -5
  48. data/lib/rubocop/cop/mixin/check_line_breakable.rb +1 -1
  49. data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +76 -1
  50. data/lib/rubocop/cop/mixin/hash_transform_method.rb +10 -6
  51. data/lib/rubocop/cop/mixin/method_complexity.rb +4 -4
  52. data/lib/rubocop/cop/mixin/multiline_element_indentation.rb +1 -1
  53. data/lib/rubocop/cop/mixin/range_help.rb +2 -3
  54. data/lib/rubocop/cop/naming/block_parameter_name.rb +1 -1
  55. data/lib/rubocop/cop/naming/constant_name.rb +2 -2
  56. data/lib/rubocop/cop/style/access_modifier_declarations.rb +77 -1
  57. data/lib/rubocop/cop/style/arguments_forwarding.rb +2 -2
  58. data/lib/rubocop/cop/style/case_equality.rb +40 -10
  59. data/lib/rubocop/cop/style/class_methods_definitions.rb +2 -1
  60. data/lib/rubocop/cop/style/collection_methods.rb +2 -0
  61. data/lib/rubocop/cop/style/combinable_loops.rb +3 -1
  62. data/lib/rubocop/cop/style/each_for_simple_loop.rb +41 -6
  63. data/lib/rubocop/cop/style/each_with_object.rb +39 -8
  64. data/lib/rubocop/cop/style/empty_block_parameter.rb +1 -1
  65. data/lib/rubocop/cop/style/empty_lambda_parameter.rb +1 -1
  66. data/lib/rubocop/cop/style/for.rb +2 -0
  67. data/lib/rubocop/cop/style/guard_clause.rb +27 -16
  68. data/lib/rubocop/cop/style/hash_each_methods.rb +3 -1
  69. data/lib/rubocop/cop/style/hash_syntax.rb +17 -0
  70. data/lib/rubocop/cop/style/inverse_methods.rb +8 -6
  71. data/lib/rubocop/cop/style/magic_comment_format.rb +307 -0
  72. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +2 -2
  73. data/lib/rubocop/cop/style/method_called_on_do_end_block.rb +4 -1
  74. data/lib/rubocop/cop/style/multiline_block_chain.rb +3 -1
  75. data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +1 -1
  76. data/lib/rubocop/cop/style/next.rb +3 -5
  77. data/lib/rubocop/cop/style/nil_lambda.rb +1 -1
  78. data/lib/rubocop/cop/style/object_then.rb +2 -0
  79. data/lib/rubocop/cop/style/perl_backrefs.rb +22 -1
  80. data/lib/rubocop/cop/style/proc.rb +4 -1
  81. data/lib/rubocop/cop/style/redundant_begin.rb +2 -0
  82. data/lib/rubocop/cop/style/redundant_fetch_block.rb +1 -1
  83. data/lib/rubocop/cop/style/redundant_parentheses.rb +4 -0
  84. data/lib/rubocop/cop/style/redundant_self.rb +2 -0
  85. data/lib/rubocop/cop/style/redundant_sort_by.rb +24 -8
  86. data/lib/rubocop/cop/style/safe_navigation.rb +4 -2
  87. data/lib/rubocop/cop/style/single_line_block_params.rb +1 -1
  88. data/lib/rubocop/cop/style/sole_nested_conditional.rb +0 -2
  89. data/lib/rubocop/cop/style/symbol_array.rb +1 -1
  90. data/lib/rubocop/cop/style/symbol_proc.rb +5 -4
  91. data/lib/rubocop/cop/style/top_level_method_definition.rb +3 -1
  92. data/lib/rubocop/cop/style/trailing_comma_in_block_args.rb +1 -1
  93. data/lib/rubocop/cop/style/word_array.rb +1 -1
  94. data/lib/rubocop/cop/util.rb +1 -1
  95. data/lib/rubocop/feature_loader.rb +6 -2
  96. data/lib/rubocop/formatter/html_formatter.rb +2 -2
  97. data/lib/rubocop/runner.rb +4 -0
  98. data/lib/rubocop/server/cache.rb +11 -8
  99. data/lib/rubocop/version.rb +3 -2
  100. data/lib/rubocop.rb +3 -2
  101. metadata +9 -7
@@ -63,7 +63,7 @@ module RuboCop
63
63
  class EmptyBlock < Base
64
64
  MSG = 'Empty block detected.'
65
65
 
66
- def on_block(node)
66
+ def on_block(node) # rubocop:disable InternalAffairs/NumblockHandler
67
67
  return if node.body
68
68
  return if allow_empty_lambdas? && lambda_or_proc?(node)
69
69
  return if cop_config['AllowComments'] && allow_comment?(node)
@@ -96,7 +96,7 @@ module RuboCop
96
96
  end
97
97
 
98
98
  def correct_other_branches(corrector, node)
99
- return unless (node.if? || node.unless?) && node.else_branch
99
+ return unless require_other_branches_correction?(node)
100
100
 
101
101
  if node.else_branch.if_type?
102
102
  # Replace an orphaned `elsif` with `if`
@@ -107,13 +107,43 @@ module RuboCop
107
107
  end
108
108
  end
109
109
 
110
+ def require_other_branches_correction?(node)
111
+ return false unless node.if_type? && node.else_branch
112
+ return false if !empty_if_branch?(node) && node.elsif?
113
+
114
+ !empty_else_branch?(node)
115
+ end
116
+
117
+ def empty_if_branch?(node)
118
+ return false unless (parent = node.parent)
119
+ return true unless parent.if_type?
120
+ return true unless (if_branch = parent.if_branch)
121
+
122
+ if_branch.if_type? && !if_branch.body
123
+ end
124
+
125
+ def empty_else_branch?(node)
126
+ node.else_branch.if_type? && !node.else_branch.body
127
+ end
128
+
129
+ # rubocop:disable Metrics/AbcSize
110
130
  def branch_range(node)
111
131
  if node.loc.else
112
132
  node.source_range.with(end_pos: node.loc.else.begin_pos - 1)
133
+ elsif all_branches_body_missing?(node)
134
+ if_node = node.ancestors.detect(&:if?)
135
+ node.source_range.with(end_pos: if_node.loc.end.end_pos)
113
136
  else
114
137
  node.source_range
115
138
  end
116
139
  end
140
+ # rubocop:enable Metrics/AbcSize
141
+
142
+ def all_branches_body_missing?(node)
143
+ return false unless node.parent&.if_type?
144
+
145
+ node.parent.branches.compact.empty?
146
+ end
117
147
 
118
148
  def deletion_range(range)
119
149
  # Collect a range between the start of the `if` node and the next relevant node,
@@ -6,18 +6,18 @@ module RuboCop
6
6
  #
7
7
  # This cop emulates the following Ruby warnings in Ruby 2.6.
8
8
  #
9
+ # [source,console]
10
+ # ----
9
11
  # % cat example.rb
10
12
  # ERB.new('hi', nil, '-', '@output_buffer')
11
13
  # % ruby -rerb example.rb
12
- # example.rb:1: warning: Passing safe_level with the 2nd argument of
13
- # ERB.new is deprecated. Do not use it, and specify other arguments as
14
- # keyword arguments.
15
- # example.rb:1: warning: Passing trim_mode with the 3rd argument of
16
- # ERB.new is deprecated. Use keyword argument like
17
- # ERB.new(str, trim_mode:...) instead.
18
- # example.rb:1: warning: Passing eoutvar with the 4th argument of ERB.new
19
- # is deprecated. Use keyword argument like ERB.new(str, eoutvar: ...)
20
- # instead.
14
+ # example.rb:1: warning: Passing safe_level with the 2nd argument of ERB.new is
15
+ # deprecated. Do not use it, and specify other arguments as keyword arguments.
16
+ # example.rb:1: warning: Passing trim_mode with the 3rd argument of ERB.new is
17
+ # deprecated. Use keyword argument like ERB.new(str, trim_mode:...) instead.
18
+ # example.rb:1: warning: Passing eoutvar with the 4th argument of ERB.new is
19
+ # deprecated. Use keyword argument like ERB.new(str, eoutvar: ...) instead.
20
+ # ----
21
21
  #
22
22
  # Now non-keyword arguments other than first one are softly deprecated
23
23
  # and will be removed when Ruby 2.5 becomes EOL.
@@ -58,6 +58,7 @@ module RuboCop
58
58
  (node.str_type? && !node.loc.respond_to?(:begin)) || node.source_range.is?('__LINE__')
59
59
  end
60
60
 
61
+ # rubocop:disable Metrics/MethodLength
61
62
  def autocorrected_value(node)
62
63
  case node.type
63
64
  when :int
@@ -70,10 +71,13 @@ module RuboCop
70
71
  autocorrected_value_for_symbol(node)
71
72
  when :array
72
73
  autocorrected_value_for_array(node)
74
+ when :nil
75
+ ''
73
76
  else
74
77
  node.source.gsub('"', '\"')
75
78
  end
76
79
  end
80
+ # rubocop:enable Metrics/MethodLength
77
81
 
78
82
  def autocorrected_value_for_string(node)
79
83
  if node.source.start_with?("'", '%q')
@@ -25,13 +25,8 @@ module RuboCop
25
25
  class NextWithoutAccumulator < Base
26
26
  MSG = 'Use `next` with an accumulator argument in a `reduce`.'
27
27
 
28
- # @!method on_body_of_reduce(node)
29
- def_node_matcher :on_body_of_reduce, <<~PATTERN
30
- (block (send _recv {:reduce :inject} !sym) _blockargs $(begin ...))
31
- PATTERN
32
-
33
28
  def on_block(node)
34
- on_body_of_reduce(node) do |body|
29
+ on_block_body_of_reduce(node) do |body|
35
30
  void_next = body.each_node(:next).find do |n|
36
31
  n.children.empty? && parent_block_node(n) == node
37
32
  end
@@ -40,11 +35,35 @@ module RuboCop
40
35
  end
41
36
  end
42
37
 
38
+ def on_numblock(node)
39
+ on_numblock_body_of_reduce(node) do |body|
40
+ void_next = body.each_node(:next).find do |n|
41
+ n.children.empty? && parent_numblock_node(n) == node
42
+ end
43
+
44
+ add_offense(void_next) if void_next
45
+ end
46
+ end
47
+
43
48
  private
44
49
 
50
+ # @!method on_block_body_of_reduce(node)
51
+ def_node_matcher :on_block_body_of_reduce, <<~PATTERN
52
+ (block (send _recv {:reduce :inject} !sym) _blockargs $(begin ...))
53
+ PATTERN
54
+
55
+ # @!method on_numblock_body_of_reduce(node)
56
+ def_node_matcher :on_numblock_body_of_reduce, <<~PATTERN
57
+ (numblock (send _recv {:reduce :inject} !sym) _argscount $(begin ...))
58
+ PATTERN
59
+
45
60
  def parent_block_node(node)
46
61
  node.each_ancestor(:block).first
47
62
  end
63
+
64
+ def parent_numblock_node(node)
65
+ node.each_ancestor(:numblock).first
66
+ end
48
67
  end
49
68
  end
50
69
  end
@@ -99,19 +99,19 @@ module RuboCop
99
99
  end
100
100
 
101
101
  def register_offense(node, exist_node)
102
- unless force_method?(node)
103
- add_offense(node,
104
- message: format(MSG_CHANGE_FORCE_METHOD,
105
- method_name: replacement_method(node)))
106
- end
102
+ add_offense(node, message: message_change_force_method(node)) unless force_method?(node)
107
103
 
108
104
  range = range_between(node.parent.loc.keyword.begin_pos,
109
105
  exist_node.loc.expression.end_pos)
110
106
  add_offense(range, message: message_remove_file_exist_check(exist_node)) do |corrector|
111
- autocorrect(corrector, node, range)
107
+ autocorrect(corrector, node, range) unless node.parent.elsif?
112
108
  end
113
109
  end
114
110
 
111
+ def message_change_force_method(node)
112
+ format(MSG_CHANGE_FORCE_METHOD, method_name: replacement_method(node))
113
+ end
114
+
115
115
  def message_remove_file_exist_check(node)
116
116
  receiver, method_name = receiver_and_method_name(node)
117
117
  format(MSG_REMOVE_FILE_EXIST_CHECK, receiver: receiver, method_name: method_name)
@@ -74,6 +74,18 @@ module RuboCop
74
74
  end
75
75
  end
76
76
 
77
+ def on_numblock(node)
78
+ return if target_ruby_version >= 3.0
79
+ return unless node.body
80
+ return unless unsorted_dir_loop?(node.send_node)
81
+
82
+ node.argument_list
83
+ .filter { |argument| var_is_required?(node.body, argument.name) }
84
+ .each do
85
+ add_offense(node.send_node) { |corrector| correct_block(corrector, node.send_node) }
86
+ end
87
+ end
88
+
77
89
  def on_block_pass(node)
78
90
  return if target_ruby_version >= 3.0
79
91
  return unless method_require?(node)
@@ -7,6 +7,11 @@ module RuboCop
7
7
  # `instance_of?`, `kind_of?`, `is_a?`, `eql?`, `respond_to?`, and `equal?` methods
8
8
  # are checked by default. These are customizable with `AllowedMethods` option.
9
9
  #
10
+ # The `AllowedMethods` option specifies nil-safe methods,
11
+ # in other words, it is a method that is allowed to skip safe navigation.
12
+ # Note that the `AllowedMethod` option is not an option that specifies methods
13
+ # for which to suppress (allow) this cop's check.
14
+ #
10
15
  # In the example below, the safe navigation operator (`&.`) is unnecessary
11
16
  # because `NilClass` has methods like `respond_to?` and `is_a?`.
12
17
  #
@@ -35,12 +40,13 @@ module RuboCop
35
40
  # # good - without `&.` this will always return `true`
36
41
  # foo&.respond_to?(:to_a)
37
42
  #
38
- # @example AllowedMethods: [foo?]
43
+ # @example AllowedMethods: [nil_safe_method]
39
44
  # # bad
40
- # do_something if attrs&.foo?(:[])
45
+ # do_something if attrs&.nil_safe_method(:[])
41
46
  #
42
47
  # # good
43
- # do_something if attrs&.bar?(:[])
48
+ # do_something if attrs.nil_safe_method(:[])
49
+ # do_something if attrs&.not_nil_safe_method(:[])
44
50
  #
45
51
  class RedundantSafeNavigation < Base
46
52
  include AllowedMethods
@@ -33,16 +33,6 @@ module RuboCop
33
33
  MSG_EACH_WITH_INDEX = 'Use `each` instead of `each_with_index`.'
34
34
  MSG_WITH_INDEX = 'Remove redundant `with_index`.'
35
35
 
36
- # @!method redundant_with_index?(node)
37
- def_node_matcher :redundant_with_index?, <<~PATTERN
38
- (block
39
- $(send
40
- _ {:each_with_index :with_index} ...)
41
- (args
42
- (arg _))
43
- ...)
44
- PATTERN
45
-
46
36
  def on_block(node)
47
37
  return unless (send = redundant_with_index?(node))
48
38
 
@@ -58,8 +48,21 @@ module RuboCop
58
48
  end
59
49
  end
60
50
 
51
+ alias on_numblock on_block
52
+
61
53
  private
62
54
 
55
+ # @!method redundant_with_index?(node)
56
+ def_node_matcher :redundant_with_index?, <<~PATTERN
57
+ {
58
+ (block
59
+ $(send _ {:each_with_index :with_index} ...)
60
+ (args (arg _)) ...)
61
+ (numblock
62
+ $(send _ {:each_with_index :with_index} ...) 1 ...)
63
+ }
64
+ PATTERN
65
+
63
66
  def message(node)
64
67
  if node.method?(:each_with_index)
65
68
  MSG_EACH_WITH_INDEX
@@ -31,19 +31,8 @@ module RuboCop
31
31
  extend AutoCorrector
32
32
 
33
33
  MSG_EACH_WITH_OBJECT = 'Use `each` instead of `each_with_object`.'
34
-
35
34
  MSG_WITH_OBJECT = 'Remove redundant `with_object`.'
36
35
 
37
- # @!method redundant_with_object?(node)
38
- def_node_matcher :redundant_with_object?, <<~PATTERN
39
- (block
40
- $(send _ {:each_with_object :with_object}
41
- _)
42
- (args
43
- (arg _))
44
- ...)
45
- PATTERN
46
-
47
36
  def on_block(node)
48
37
  return unless (send = redundant_with_object?(node))
49
38
 
@@ -59,8 +48,20 @@ module RuboCop
59
48
  end
60
49
  end
61
50
 
51
+ alias on_numblock on_block
52
+
62
53
  private
63
54
 
55
+ # @!method redundant_with_object?(node)
56
+ def_node_matcher :redundant_with_object?, <<~PATTERN
57
+ {
58
+ (block
59
+ $(send _ {:each_with_object :with_object} _) (args (arg _)) ...)
60
+ (numblock
61
+ $(send _ {:each_with_object :with_object} _) 1 ...)
62
+ }
63
+ PATTERN
64
+
64
65
  def message(node)
65
66
  if node.method?(:each_with_object)
66
67
  MSG_EACH_WITH_OBJECT
@@ -12,7 +12,7 @@ module RuboCop
12
12
  # same `rescue` statement. In both cases, the more specific rescue is
13
13
  # unnecessary because it is covered by rescuing the less specific
14
14
  # exception. (ie. `rescue Exception, StandardError` has the same behavior
15
- # whether `StandardError` is included or not, because all `StandardError`s
15
+ # whether `StandardError` is included or not, because all ``StandardError``s
16
16
  # are rescued by `rescue Exception`).
17
17
  #
18
18
  # @example
@@ -64,13 +64,26 @@ module RuboCop
64
64
  end
65
65
 
66
66
  def same_conditions_node_different_branch?(variable, outer_local_variable)
67
- variable_node = variable.scope.node.parent
67
+ variable_node = variable_node(variable)
68
68
  return false unless variable_node.conditional?
69
69
 
70
70
  outer_local_variable_node =
71
71
  find_conditional_node_from_ascendant(outer_local_variable.declaration_node)
72
+ return true unless outer_local_variable_node
72
73
 
73
- outer_local_variable_node.conditional? && variable_node == outer_local_variable_node
74
+ outer_local_variable_node.conditional? &&
75
+ (variable_node == outer_local_variable_node ||
76
+ variable_node == outer_local_variable_node.else_branch)
77
+ end
78
+
79
+ def variable_node(variable)
80
+ parent_node = variable.scope.node.parent
81
+
82
+ if parent_node.when_type?
83
+ parent_node.parent
84
+ else
85
+ parent_node
86
+ end
74
87
  end
75
88
 
76
89
  def find_conditional_node_from_ascendant(node)
@@ -9,7 +9,7 @@ module RuboCop
9
9
  # In rare cases where only one iteration (or at most one iteration) is intended behavior,
10
10
  # the code should be refactored to use `if` conditionals.
11
11
  #
12
- # NOTE: Block methods that are used with `Enumerable`s are considered to be loops.
12
+ # NOTE: Block methods that are used with ``Enumerable``s are considered to be loops.
13
13
  #
14
14
  # `AllowedPatterns` can be used to match against the block receiver in order to allow
15
15
  # code that would otherwise be registered as an offense (eg. `times` used not in an
@@ -101,10 +101,14 @@ module RuboCop
101
101
  check(node) if loop_method?(node)
102
102
  end
103
103
 
104
+ def on_numblock(node)
105
+ check(node) if loop_method?(node)
106
+ end
107
+
104
108
  private
105
109
 
106
110
  def loop_method?(node)
107
- return false unless node.block_type?
111
+ return false unless node.block_type? || node.numblock_type?
108
112
 
109
113
  send_node = node.send_node
110
114
  return false if matches_allowed_pattern?(send_node.source)
@@ -179,6 +183,8 @@ module RuboCop
179
183
 
180
184
  def preceded_by_continue_statement?(break_statement)
181
185
  break_statement.left_siblings.any? do |sibling|
186
+ # Numblocks have the arguments count as a number in the AST.
187
+ next if sibling.is_a?(Integer)
182
188
  next if sibling.loop_keyword? || loop_method?(sibling)
183
189
 
184
190
  sibling.each_descendant(*CONTINUE_KEYWORDS).any?
@@ -31,8 +31,8 @@ module RuboCop
31
31
  # # bad
32
32
  # class Foo
33
33
  # # The following is redundant (methods defined on the class'
34
- # # singleton class are not affected by the public modifier)
35
- # public
34
+ # # singleton class are not affected by the private modifier)
35
+ # private
36
36
  #
37
37
  # def self.method3
38
38
  # end
@@ -142,6 +142,8 @@ module RuboCop
142
142
  check_node(node.body)
143
143
  end
144
144
 
145
+ alias on_numblock on_block
146
+
145
147
  private
146
148
 
147
149
  def autocorrect(corrector, node)
@@ -157,17 +159,17 @@ module RuboCop
157
159
 
158
160
  # @!method dynamic_method_definition?(node)
159
161
  def_node_matcher :dynamic_method_definition?, <<~PATTERN
160
- {(send nil? :define_method ...) (block (send nil? :define_method ...) ...)}
162
+ {(send nil? :define_method ...) ({block numblock} (send nil? :define_method ...) ...)}
161
163
  PATTERN
162
164
 
163
165
  # @!method class_or_instance_eval?(node)
164
166
  def_node_matcher :class_or_instance_eval?, <<~PATTERN
165
- (block (send _ {:class_eval :instance_eval}) ...)
167
+ ({block numblock} (send _ {:class_eval :instance_eval}) ...)
166
168
  PATTERN
167
169
 
168
170
  # @!method class_or_module_or_struct_new_call?(node)
169
171
  def_node_matcher :class_or_module_or_struct_new_call?, <<~PATTERN
170
- (block (send (const {nil? cbase} {:Class :Module :Struct}) :new ...) ...)
172
+ ({block numblock} (send (const {nil? cbase} {:Class :Module :Struct}) :new ...) ...)
171
173
  PATTERN
172
174
 
173
175
  def check_node(node)
@@ -277,7 +279,7 @@ module RuboCop
277
279
  matcher_name = "#{m}_block?".to_sym
278
280
  unless respond_to?(matcher_name)
279
281
  self.class.def_node_matcher matcher_name, <<~PATTERN
280
- (block (send {nil? const} {:#{m}} ...) ...)
282
+ ({block numblock} (send {nil? const} {:#{m}} ...) ...)
281
283
  PATTERN
282
284
  end
283
285
 
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Looks for `ruby2_keywords` calls for methods that do not need it.
7
7
  #
8
8
  # `ruby2_keywords` should only be called on methods that accept an argument splat
9
- # (`*args`) but do not explicit keyword arguments (`k:` or `k: true`) or
9
+ # (`\*args`) but do not explicit keyword arguments (`k:` or `k: true`) or
10
10
  # a keyword splat (`**kwargs`).
11
11
  #
12
12
  # @example
@@ -67,6 +67,8 @@ module RuboCop
67
67
  check_expression(node.body)
68
68
  end
69
69
 
70
+ alias on_numblock on_block
71
+
70
72
  def on_begin(node)
71
73
  check_begin(node)
72
74
  end
@@ -17,16 +17,21 @@ module RuboCop
17
17
 
18
18
  # @api public
19
19
  def allowed_methods
20
- deprecated_values = cop_config_deprecated_values
21
- if deprecated_values.any?(Regexp)
22
- cop_config.fetch('AllowedMethods', [])
20
+ if cop_config_deprecated_values.any?(Regexp)
21
+ cop_config_allowed_methods
23
22
  else
24
- Array(cop_config['AllowedMethods']).concat(deprecated_values)
23
+ cop_config_allowed_methods + cop_config_deprecated_values
25
24
  end
26
25
  end
27
26
 
27
+ def cop_config_allowed_methods
28
+ @cop_config_allowed_methods ||= Array(cop_config.fetch('AllowedMethods', []))
29
+ end
30
+
28
31
  def cop_config_deprecated_values
29
- Array(cop_config['IgnoredMethods']).concat(Array(cop_config['ExcludedMethods']))
32
+ @cop_config_deprecated_values ||=
33
+ Array(cop_config.fetch('IgnoredMethods', [])) +
34
+ Array(cop_config.fetch('ExcludedMethods', []))
30
35
  end
31
36
  end
32
37
  # @deprecated IgnoredMethods class has been replaced with AllowedMethods.
@@ -30,15 +30,23 @@ module RuboCop
30
30
  def allowed_patterns
31
31
  # Since there could be a pattern specified in the default config, merge the two
32
32
  # arrays together.
33
- patterns = Array(cop_config['AllowedPatterns']).concat(Array(cop_config['IgnoredPatterns']))
34
- deprecated_values = cop_config_deprecated_methods_values
35
- return patterns unless deprecated_values.any?(Regexp)
33
+ if cop_config_deprecated_methods_values.any?(Regexp)
34
+ cop_config_patterns_values + cop_config_deprecated_methods_values
35
+ else
36
+ cop_config_patterns_values
37
+ end
38
+ end
36
39
 
37
- Array(patterns.concat(deprecated_values))
40
+ def cop_config_patterns_values
41
+ @cop_config_patterns_values ||=
42
+ Array(cop_config.fetch('AllowedPatterns', [])) +
43
+ Array(cop_config.fetch('IgnoredPatterns', []))
38
44
  end
39
45
 
40
46
  def cop_config_deprecated_methods_values
41
- Array(cop_config['IgnoredMethods']).concat(Array(cop_config['ExcludedMethods']))
47
+ @cop_config_deprecated_methods_values ||=
48
+ Array(cop_config.fetch('IgnoredMethods', [])) +
49
+ Array(cop_config.fetch('ExcludedMethods', []))
42
50
  end
43
51
  end
44
52
 
@@ -220,7 +220,7 @@ module RuboCop
220
220
  def already_on_multiple_lines?(node)
221
221
  return node.first_line != node.arguments.last.last_line if node.def_type?
222
222
 
223
- node.first_line != node.last_line
223
+ !node.single_line?
224
224
  end
225
225
  end
226
226
  end
@@ -5,7 +5,22 @@ module RuboCop
5
5
  # This module checks for Ruby 3.1's hash value omission syntax.
6
6
  module HashShorthandSyntax
7
7
  OMIT_HASH_VALUE_MSG = 'Omit the hash value.'
8
- EXPLICIT_HASH_VALUE_MSG = 'Explicit the hash value.'
8
+ EXPLICIT_HASH_VALUE_MSG = 'Include the hash value.'
9
+ DO_NOT_MIX_MSG_PREFIX = 'Do not mix explicit and implicit hash values.'
10
+ DO_NOT_MIX_OMIT_VALUE_MSG = "#{DO_NOT_MIX_MSG_PREFIX} #{OMIT_HASH_VALUE_MSG}"
11
+ DO_NOT_MIX_EXPLICIT_VALUE_MSG = "#{DO_NOT_MIX_MSG_PREFIX} #{EXPLICIT_HASH_VALUE_MSG}"
12
+
13
+ def on_hash_for_mixed_shorthand(hash_node)
14
+ return if ignore_mixed_hash_shorthand_syntax?(hash_node)
15
+
16
+ hash_value_type_breakdown = breakdown_value_types_of_hash(hash_node)
17
+
18
+ if hash_with_mixed_shorthand_syntax?(hash_value_type_breakdown)
19
+ mixed_shorthand_syntax_check(hash_value_type_breakdown)
20
+ else
21
+ no_mixed_shorthand_syntax_check(hash_value_type_breakdown)
22
+ end
23
+ end
9
24
 
10
25
  def on_pair(node)
11
26
  return if ignore_hash_shorthand_syntax?(node)
@@ -36,8 +51,14 @@ module RuboCop
36
51
  end
37
52
  end
38
53
 
54
+ def ignore_mixed_hash_shorthand_syntax?(hash_node)
55
+ target_ruby_version <= 3.0 || enforced_shorthand_syntax != 'consistent' ||
56
+ !hash_node.hash_type?
57
+ end
58
+
39
59
  def ignore_hash_shorthand_syntax?(pair_node)
40
60
  target_ruby_version <= 3.0 || enforced_shorthand_syntax == 'either' ||
61
+ enforced_shorthand_syntax == 'consistent' ||
41
62
  !pair_node.parent.hash_type?
42
63
  end
43
64
 
@@ -81,6 +102,60 @@ module RuboCop
81
102
 
82
103
  ancestor.respond_to?(:parenthesized?) && !ancestor.parenthesized? && !!right_sibling
83
104
  end
105
+
106
+ def breakdown_value_types_of_hash(hash_node)
107
+ hash_node.pairs.group_by do |pair_node|
108
+ if pair_node.value_omission?
109
+ :value_omitted
110
+ elsif require_hash_value?(pair_node.key.source, pair_node)
111
+ :value_needed
112
+ else
113
+ :value_omittable
114
+ end
115
+ end
116
+ end
117
+
118
+ def hash_with_mixed_shorthand_syntax?(hash_value_type_breakdown)
119
+ hash_value_type_breakdown.keys.size > 1
120
+ end
121
+
122
+ def hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown)
123
+ hash_value_type_breakdown[:value_needed]&.any?
124
+ end
125
+
126
+ def each_omitted_value_pair(hash_value_type_breakdown, &block)
127
+ hash_value_type_breakdown[:value_omitted]&.each(&block)
128
+ end
129
+
130
+ def each_omittable_value_pair(hash_value_type_breakdown, &block)
131
+ hash_value_type_breakdown[:value_omittable]&.each(&block)
132
+ end
133
+
134
+ def mixed_shorthand_syntax_check(hash_value_type_breakdown)
135
+ if hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown)
136
+ each_omitted_value_pair(hash_value_type_breakdown) do |pair_node|
137
+ hash_key_source = pair_node.key.source
138
+ replacement = "#{hash_key_source}: #{hash_key_source}"
139
+ register_offense(pair_node, DO_NOT_MIX_EXPLICIT_VALUE_MSG, replacement)
140
+ end
141
+ else
142
+ each_omittable_value_pair(hash_value_type_breakdown) do |pair_node|
143
+ hash_key_source = pair_node.key.source
144
+ replacement = "#{hash_key_source}:"
145
+ register_offense(pair_node, DO_NOT_MIX_OMIT_VALUE_MSG, replacement)
146
+ end
147
+ end
148
+ end
149
+
150
+ def no_mixed_shorthand_syntax_check(hash_value_type_breakdown)
151
+ return if hash_with_values_that_cant_be_omitted?(hash_value_type_breakdown)
152
+
153
+ each_omittable_value_pair(hash_value_type_breakdown) do |pair_node|
154
+ hash_key_source = pair_node.key.source
155
+ replacement = "#{hash_key_source}:"
156
+ register_offense(pair_node, OMIT_HASH_VALUE_MSG, replacement)
157
+ end
158
+ end
84
159
  end
85
160
  end
86
161
  end