rubocop 1.64.1 → 1.65.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +11 -1
  4. data/lib/rubocop/config_loader.rb +1 -1
  5. data/lib/rubocop/config_loader_resolver.rb +9 -3
  6. data/lib/rubocop/cop/cop.rb +20 -2
  7. data/lib/rubocop/cop/gemspec/add_runtime_dependency.rb +38 -0
  8. data/lib/rubocop/cop/gemspec/duplicated_assignment.rb +2 -2
  9. data/lib/rubocop/cop/gemspec/ruby_version_globals_usage.rb +3 -3
  10. data/lib/rubocop/cop/layout/case_indentation.rb +1 -1
  11. data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +1 -1
  12. data/lib/rubocop/cop/layout/first_argument_indentation.rb +2 -2
  13. data/lib/rubocop/cop/layout/heredoc_indentation.rb +1 -1
  14. data/lib/rubocop/cop/layout/indentation_width.rb +1 -1
  15. data/lib/rubocop/cop/layout/line_length.rb +20 -20
  16. data/lib/rubocop/cop/layout/space_around_operators.rb +3 -0
  17. data/lib/rubocop/cop/legacy/corrector.rb +12 -2
  18. data/lib/rubocop/cop/lint/duplicate_case_condition.rb +1 -1
  19. data/lib/rubocop/cop/lint/empty_when.rb +1 -1
  20. data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +14 -7
  21. data/lib/rubocop/cop/lint/literal_as_condition.rb +1 -1
  22. data/lib/rubocop/cop/lint/nested_method_definition.rb +1 -1
  23. data/lib/rubocop/cop/lint/to_enum_arguments.rb +2 -9
  24. data/lib/rubocop/cop/lint/unmodified_reduce_accumulator.rb +1 -0
  25. data/lib/rubocop/cop/lint/void.rb +5 -0
  26. data/lib/rubocop/cop/metrics/block_nesting.rb +19 -7
  27. data/lib/rubocop/cop/mixin/alignment.rb +5 -1
  28. data/lib/rubocop/cop/mixin/allowed_methods.rb +7 -1
  29. data/lib/rubocop/cop/mixin/allowed_pattern.rb +15 -3
  30. data/lib/rubocop/cop/mixin/configurable_max.rb +5 -1
  31. data/lib/rubocop/cop/mixin/rescue_node.rb +4 -0
  32. data/lib/rubocop/cop/style/arguments_forwarding.rb +1 -1
  33. data/lib/rubocop/cop/style/hash_except.rb +8 -5
  34. data/lib/rubocop/cop/style/map_compact_with_conditional_block.rb +77 -43
  35. data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +5 -0
  36. data/lib/rubocop/cop/style/quoted_symbols.rb +1 -1
  37. data/lib/rubocop/cop/style/redundant_begin.rb +1 -1
  38. data/lib/rubocop/cop/style/redundant_file_extension_in_require.rb +1 -1
  39. data/lib/rubocop/cop/style/send_with_literal_method_name.rb +15 -1
  40. data/lib/rubocop/cop/style/super_arguments.rb +28 -10
  41. data/lib/rubocop/cop/style/symbol_proc.rb +8 -1
  42. data/lib/rubocop/cop/style/zero_length_predicate.rb +28 -24
  43. data/lib/rubocop/cop/team.rb +8 -0
  44. data/lib/rubocop/cop/util.rb +7 -1
  45. data/lib/rubocop/cops_documentation_generator.rb +1 -1
  46. data/lib/rubocop/ext/regexp_parser.rb +4 -21
  47. data/lib/rubocop/formatter/html_formatter.rb +3 -1
  48. data/lib/rubocop/rspec/shared_contexts.rb +20 -0
  49. data/lib/rubocop/rspec/support.rb +1 -0
  50. data/lib/rubocop/server/cache.rb +10 -0
  51. data/lib/rubocop/server/client_command/exec.rb +2 -2
  52. data/lib/rubocop/server/client_command/start.rb +1 -1
  53. data/lib/rubocop/server/core.rb +4 -0
  54. data/lib/rubocop/version.rb +1 -1
  55. data/lib/rubocop.rb +1 -0
  56. metadata +11 -10
@@ -23,9 +23,9 @@ module RuboCop
23
23
  # {foo: 1, bar: 2, baz: 3}.reject {|k, v| k == :bar }
24
24
  # {foo: 1, bar: 2, baz: 3}.select {|k, v| k != :bar }
25
25
  # {foo: 1, bar: 2, baz: 3}.filter {|k, v| k != :bar }
26
- # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[foo bar].include?(k) }
27
- # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[foo bar].include?(k) }
28
- # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[foo bar].include?(k) }
26
+ # {foo: 1, bar: 2, baz: 3}.reject {|k, v| %i[bar].include?(k) }
27
+ # {foo: 1, bar: 2, baz: 3}.select {|k, v| !%i[bar].include?(k) }
28
+ # {foo: 1, bar: 2, baz: 3}.filter {|k, v| !%i[bar].include?(k) }
29
29
  #
30
30
  # # good
31
31
  # {foo: 1, bar: 2, baz: 3}.except(:bar)
@@ -73,8 +73,9 @@ module RuboCop
73
73
  PATTERN
74
74
 
75
75
  def on_send(node)
76
+ method_name = node.method_name
76
77
  block = node.parent
77
- return unless bad_method?(block) && semantically_except_method?(node, block)
78
+ return unless bad_method?(method_name, block) && semantically_except_method?(node, block)
78
79
 
79
80
  except_key = except_key(block)
80
81
  return if except_key.nil? || !safe_to_register_offense?(block, except_key)
@@ -91,7 +92,7 @@ module RuboCop
91
92
  private
92
93
 
93
94
  # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
94
- def bad_method?(block)
95
+ def bad_method?(method_name, block)
95
96
  if active_support_extensions_enabled?
96
97
  bad_method_with_active_support?(block) do |key_arg, send_node|
97
98
  if send_node.method?(:in?) && send_node.receiver&.source != key_arg.source
@@ -103,6 +104,8 @@ module RuboCop
103
104
  end
104
105
  else
105
106
  bad_method_with_poro?(block) do |key_arg, send_node|
107
+ return false if method_name == :reject && block.body.method?(:!)
108
+
106
109
  !send_node.method?(:include?) || send_node.first_argument&.source == key_arg.source
107
110
  end
108
111
  end
@@ -4,6 +4,7 @@ module RuboCop
4
4
  module Cop
5
5
  module Style
6
6
  # Prefer `select` or `reject` over `map { ... }.compact`.
7
+ # This cop also handles `filter_map { ... }`, similar to `map { ... }.compact`.
7
8
  #
8
9
  # @example
9
10
  #
@@ -11,6 +12,9 @@ module RuboCop
11
12
  # array.map { |e| some_condition? ? e : next }.compact
12
13
  #
13
14
  # # bad
15
+ # array.filter_map { |e| some_condition? ? e : next }
16
+ #
17
+ # # bad
14
18
  # array.map do |e|
15
19
  # if some_condition?
16
20
  # e
@@ -40,55 +44,73 @@ module RuboCop
40
44
  class MapCompactWithConditionalBlock < Base
41
45
  extend AutoCorrector
42
46
 
43
- MSG = 'Replace `map { ... }.compact` with `%<method>s`.'
44
-
45
- # @!method map_and_compact?(node)
46
- def_node_matcher :map_and_compact?, <<~RUBY
47
- (call
48
- (block
49
- (call _ :map)
50
- (args
51
- $(arg _))
52
- {
53
- (if $_ $(lvar _) {next nil?})
54
- (if $_ {next nil?} $(lvar _))
55
- (if $_ (next $(lvar _)) {next nil nil?})
56
- (if $_ {next nil nil?} (next $(lvar _)))
57
- (begin
58
- {
59
- (if $_ next nil?)
60
- (if $_ nil? next)
61
- }
62
- $(lvar _))
63
- (begin
64
- {
65
- (if $_ (next $(lvar _)) nil?)
66
- (if $_ nil? (next $(lvar _)))
67
- }
68
- (nil))
69
- }) :compact)
47
+ MSG = 'Replace `%<current>s` with `%<method>s`.'
48
+ RESTRICT_ON_SEND = %i[compact filter_map].freeze
49
+
50
+ # @!method conditional_block(node)
51
+ def_node_matcher :conditional_block, <<~RUBY
52
+ (block
53
+ (call _ {:map :filter_map})
54
+ (args
55
+ $(arg _))
56
+ {
57
+ (if $_ $(lvar _) {next nil?})
58
+ (if $_ {next nil?} $(lvar _))
59
+ (if $_ (next $(lvar _)) {next nil nil?})
60
+ (if $_ {next nil nil?} (next $(lvar _)))
61
+ (begin
62
+ {
63
+ (if $_ next nil?)
64
+ (if $_ nil? next)
65
+ }
66
+ $(lvar _))
67
+ (begin
68
+ {
69
+ (if $_ (next $(lvar _)) nil?)
70
+ (if $_ nil? (next $(lvar _)))
71
+ }
72
+ (nil))
73
+ })
70
74
  RUBY
71
75
 
72
76
  def on_send(node)
73
- map_and_compact?(node) do |block_argument_node, condition_node, return_value_node|
74
- return unless returns_block_argument?(block_argument_node, return_value_node)
75
- return if condition_node.parent.elsif?
76
-
77
- method = truthy_branch?(return_value_node) ? 'select' : 'reject'
78
- range = range(node)
79
-
80
- add_offense(range, message: format(MSG, method: method)) do |corrector|
81
- corrector.replace(
82
- range,
83
- "#{method} { |#{block_argument_node.source}| #{condition_node.source} }"
84
- )
85
- end
77
+ map_candidate = node.children.first
78
+ if (block_argument, condition, return_value = conditional_block(map_candidate))
79
+ return unless node.method?(:compact) && node.arguments.empty?
80
+
81
+ range = map_with_compact_range(node)
82
+ elsif (block_argument, condition, return_value = conditional_block(node.parent))
83
+ return unless node.method?(:filter_map)
84
+
85
+ range = filter_map_range(node)
86
+ else
87
+ return
86
88
  end
89
+
90
+ inspect(node, block_argument, condition, return_value, range)
87
91
  end
88
92
  alias on_csend on_send
89
93
 
90
94
  private
91
95
 
96
+ def inspect(node, block_argument_node, condition_node, return_value_node, range)
97
+ return unless returns_block_argument?(block_argument_node, return_value_node)
98
+ return if condition_node.parent.elsif?
99
+
100
+ method = truthy_branch?(return_value_node) ? 'select' : 'reject'
101
+ current = current(node)
102
+
103
+ add_offense(range, message: format(MSG, current: current, method: method)) do |corrector|
104
+ return if part_of_ignored_node?(node) || ignored_node?(node)
105
+
106
+ corrector.replace(
107
+ range, "#{method} { |#{block_argument_node.source}| #{condition_node.source} }"
108
+ )
109
+
110
+ ignore_node(node)
111
+ end
112
+ end
113
+
92
114
  def returns_block_argument?(block_argument_node, return_value_node)
93
115
  block_argument_node.name == return_value_node.children.first
94
116
  end
@@ -123,10 +145,22 @@ module RuboCop
123
145
  end
124
146
  end
125
147
 
126
- def range(node)
127
- map_node = node.receiver.send_node
148
+ def current(node)
149
+ if node.method?(:compact)
150
+ map_or_filter_map_method = node.children.first
151
+
152
+ "#{map_or_filter_map_method.method_name} { ... }.compact"
153
+ else
154
+ 'filter_map { ... }'
155
+ end
156
+ end
157
+
158
+ def map_with_compact_range(node)
159
+ node.receiver.send_node.loc.selector.begin.join(node.source_range.end)
160
+ end
128
161
 
129
- map_node.loc.selector.join(node.source_range.end)
162
+ def filter_map_range(node)
163
+ node.loc.selector.join(node.parent.source_range.end)
130
164
  end
131
165
  end
132
166
  end
@@ -18,6 +18,7 @@ module RuboCop
18
18
  return if inside_endless_method_def?(node)
19
19
  return if require_parentheses_for_hash_value_omission?(node)
20
20
  return if syntax_like_method_call?(node)
21
+ return if method_call_before_constant_resolution?(node)
21
22
  return if super_call_without_arguments?(node)
22
23
  return if legitimate_call_with_parentheses?(node)
23
24
  return if allowed_camel_case_method_call?(node)
@@ -63,6 +64,10 @@ module RuboCop
63
64
  node.implicit_call? || node.operator_method?
64
65
  end
65
66
 
67
+ def method_call_before_constant_resolution?(node)
68
+ node.parent&.const_type?
69
+ end
70
+
66
71
  def super_call_without_arguments?(node)
67
72
  node.super_type? && node.arguments.none?
68
73
  end
@@ -9,7 +9,7 @@ module RuboCop
9
9
  #
10
10
  # String interpolation is always kept in double quotes.
11
11
  #
12
- # Note: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
12
+ # NOTE: `Lint/SymbolConversion` can be used in parallel to ensure that symbols
13
13
  # are not quoted that don't need to be. This cop is for configuring the quoting
14
14
  # style to use for symbols that require quotes.
15
15
  #
@@ -75,7 +75,7 @@ module RuboCop
75
75
 
76
76
  def on_def(node)
77
77
  return unless node.body&.kwbegin_type?
78
- return if node.endless? && !node.body.children.one?
78
+ return if node.endless?
79
79
 
80
80
  register_offense(node.body)
81
81
  end
@@ -6,7 +6,7 @@ module RuboCop
6
6
  # Checks for the presence of superfluous `.rb` extension in
7
7
  # the filename provided to `require` and `require_relative`.
8
8
  #
9
- # Note: If the extension is omitted, Ruby tries adding '.rb', '.so',
9
+ # NOTE: If the extension is omitted, Ruby tries adding '.rb', '.so',
10
10
  # and so on to the name until found. If the file named cannot be found,
11
11
  # a `LoadError` will be raised.
12
12
  # There is an edge case where `foo.so` file is loaded instead of a `LoadError`
@@ -7,6 +7,20 @@ module RuboCop
7
7
  # Since the `send` method can be used to call private methods, by default,
8
8
  # only the `public_send` method is detected.
9
9
  #
10
+ # NOTE: Writer methods with names ending in `=` are always permitted because their
11
+ # behavior differs as follows:
12
+ #
13
+ # [source,ruby]
14
+ # ----
15
+ # def foo=(foo)
16
+ # @foo = foo
17
+ # 42
18
+ # end
19
+ #
20
+ # self.foo = 1 # => 1
21
+ # send(:foo=, 1) # => 42
22
+ # ----
23
+ #
10
24
  # @safety
11
25
  # This cop is not safe because it can incorrectly detect based on the receiver.
12
26
  # Additionally, when `AllowSend` is set to `true`, it cannot determine whether
@@ -43,7 +57,7 @@ module RuboCop
43
57
  MSG = 'Use `%<method_name>s` method call directly instead.'
44
58
  RESTRICT_ON_SEND = %i[public_send send __send__].freeze
45
59
  STATIC_METHOD_NAME_NODE_TYPES = %i[sym str].freeze
46
- METHOD_NAME_PATTERN = /\A[a-zA-Z_][a-zA-Z0-9_]*[!?=]?\z/.freeze
60
+ METHOD_NAME_PATTERN = /\A[a-zA-Z_][a-zA-Z0-9_]*[!?]?\z/.freeze
47
61
  RESERVED_WORDS = %i[
48
62
  BEGIN END alias and begin break case class def defined? do else elsif end ensure
49
63
  false for if in module next nil not or redo rescue retry return self super then true
@@ -3,8 +3,25 @@
3
3
  module RuboCop
4
4
  module Cop
5
5
  module Style
6
- # Checks for redundant argument forwarding when calling super
7
- # with arguments identical to the method definition.
6
+ # Checks for redundant argument forwarding when calling super with arguments identical to
7
+ # the method definition.
8
+ #
9
+ # Using zero arity `super` within a `define_method` block results in `RuntimeError`:
10
+ #
11
+ # [source,ruby]
12
+ # ----
13
+ # def m
14
+ # define_method(:foo) { super() } # => OK
15
+ # end
16
+ #
17
+ # def m
18
+ # define_method(:foo) { super } # => RuntimeError
19
+ # end
20
+ # ----
21
+ #
22
+ # Furthermore, any arguments accompanied by a block may potentially be delegating to
23
+ # `define_method`, therefore, `super` used within these blocks will be allowed.
24
+ # This approach might result in false negatives, yet ensuring safe detection takes precedence.
8
25
  #
9
26
  # @example
10
27
  # # bad
@@ -44,8 +61,10 @@ module RuboCop
44
61
 
45
62
  def on_super(super_node)
46
63
  def_node = super_node.ancestors.find do |node|
47
- # You can't implicitly call super when dynamically defining methods
48
- break if define_method?(node)
64
+ # When defining dynamic methods, implicitly calling `super` is not possible.
65
+ # Since there is a possibility of delegation to `define_method`,
66
+ # `super` used within the block is always allowed.
67
+ break if node.block_type?
49
68
 
50
69
  break node if DEF_TYPES.include?(node.type)
51
70
  end
@@ -127,6 +146,11 @@ module RuboCop
127
146
  # https://bugs.ruby-lang.org/issues/20505
128
147
  def block_reassigned?(def_node, block_arg_name)
129
148
  def_node.each_node(*ASSIGN_TYPES).any? do |assign_node|
149
+ # TODO: Since `Symbol#name` is supported from Ruby 3.0, the inheritance check for
150
+ # `AST::Node` can be removed when requiring Ruby 3.0+.
151
+ lhs = assign_node.node_parts[0]
152
+ next if lhs.is_a?(AST::Node) && !lhs.respond_to?(:name)
153
+
130
154
  assign_node.name == block_arg_name
131
155
  end
132
156
  end
@@ -135,12 +159,6 @@ module RuboCop
135
159
  def_arg.forward_arg_type? && super_arg.forwarded_args_type?
136
160
  end
137
161
 
138
- def define_method?(node)
139
- return false unless node.block_type?
140
-
141
- node.method?(:define_method) || node.method?(:define_singleton_method)
142
- end
143
-
144
162
  def preprocess_super_args(super_args)
145
163
  super_args.flat_map do |node|
146
164
  if node.hash_type? && !node.braces?
@@ -223,7 +223,14 @@ module RuboCop
223
223
  end
224
224
 
225
225
  def autocorrect_without_args(corrector, node)
226
- autocorrect_lambda_block(corrector, node) if node.send_node.lambda_literal?
226
+ if node.send_node.lambda_literal?
227
+ if node.send_node.loc.selector.source == '->'
228
+ corrector.replace(node, "lambda(&:#{node.body.method_name})")
229
+ return
230
+ else
231
+ autocorrect_lambda_block(corrector, node)
232
+ end
233
+ end
227
234
 
228
235
  corrector.replace(block_range_with_space(node), "(&:#{node.body.method_name})")
229
236
  end
@@ -47,15 +47,16 @@ module RuboCop
47
47
  check_zero_length_comparison(node)
48
48
  check_nonzero_length_comparison(node)
49
49
  end
50
+ alias on_csend on_send
50
51
 
51
52
  private
52
53
 
53
54
  def check_zero_length_predicate(node)
54
- return unless (length_method = zero_length_predicate(node.parent))
55
+ return unless zero_length_predicate?(node.parent)
55
56
  return if non_polymorphic_collection?(node.parent)
56
57
 
57
58
  offense = node.loc.selector.join(node.parent.source_range.end)
58
- message = format(ZERO_MSG, current: "#{length_method}.zero?")
59
+ message = format(ZERO_MSG, current: offense.source)
59
60
 
60
61
  add_offense(offense, message: message) do |corrector|
61
62
  corrector.replace(offense, 'empty?')
@@ -92,44 +93,47 @@ module RuboCop
92
93
  end
93
94
  end
94
95
 
95
- # @!method zero_length_predicate(node)
96
- def_node_matcher :zero_length_predicate, <<~PATTERN
97
- (send (send (...) ${:length :size}) :zero?)
96
+ # @!method zero_length_predicate?(node)
97
+ def_node_matcher :zero_length_predicate?, <<~PATTERN
98
+ (call (call (...) {:length :size}) :zero?)
98
99
  PATTERN
99
100
 
100
101
  # @!method zero_length_comparison(node)
101
102
  def_node_matcher :zero_length_comparison, <<~PATTERN
102
- {(send (send (...) ${:length :size}) $:== (int $0))
103
- (send (int $0) $:== (send (...) ${:length :size}))
104
- (send (send (...) ${:length :size}) $:< (int $1))
105
- (send (int $1) $:> (send (...) ${:length :size}))}
103
+ {(call (call (...) ${:length :size}) $:== (int $0))
104
+ (call (int $0) $:== (call (...) ${:length :size}))
105
+ (call (call (...) ${:length :size}) $:< (int $1))
106
+ (call (int $1) $:> (call (...) ${:length :size}))}
106
107
  PATTERN
107
108
 
108
109
  # @!method nonzero_length_comparison(node)
109
110
  def_node_matcher :nonzero_length_comparison, <<~PATTERN
110
- {(send (send (...) ${:length :size}) ${:> :!=} (int $0))
111
- (send (int $0) ${:< :!=} (send (...) ${:length :size}))}
111
+ {(call (call (...) ${:length :size}) ${:> :!=} (int $0))
112
+ (call (int $0) ${:< :!=} (call (...) ${:length :size}))}
112
113
  PATTERN
113
114
 
114
115
  def replacement(node)
115
- receiver = zero_length_receiver(node)
116
- return "#{receiver.source}.empty?" if receiver
116
+ length_node = zero_length_node(node)
117
+ if length_node&.receiver
118
+ return "#{length_node.receiver.source}#{length_node.loc.dot.source}empty?"
119
+ end
117
120
 
118
- "!#{other_receiver(node).source}.empty?"
121
+ other_length_node = other_length_node(node)
122
+ "!#{other_length_node.receiver.source}#{other_length_node.loc.dot.source}empty?"
119
123
  end
120
124
 
121
- # @!method zero_length_receiver(node)
122
- def_node_matcher :zero_length_receiver, <<~PATTERN
123
- {(send (send $_ _) :== (int 0))
124
- (send (int 0) :== (send $_ _))
125
- (send (send $_ _) :< (int 1))
126
- (send (int 1) :> (send $_ _))}
125
+ # @!method zero_length_node(node)
126
+ def_node_matcher :zero_length_node, <<~PATTERN
127
+ {(send $(call _ _) :== (int 0))
128
+ (send (int 0) :== $(call _ _))
129
+ (send $(call _ _) :< (int 1))
130
+ (send (int 1) :> $(call _ _))}
127
131
  PATTERN
128
132
 
129
- # @!method other_receiver(node)
130
- def_node_matcher :other_receiver, <<~PATTERN
131
- {(send (send $_ _) _ _)
132
- (send _ _ (send $_ _))}
133
+ # @!method other_length_node(node)
134
+ def_node_matcher :other_length_node, <<~PATTERN
135
+ {(call $(call _ _) _ _)
136
+ (call _ _ $(call _ _))}
133
137
  PATTERN
134
138
 
135
139
  # Some collection like objects in the Ruby standard library
@@ -74,6 +74,10 @@ module RuboCop
74
74
  # @deprecated. Use investigate
75
75
  # @return Array<offenses>
76
76
  def inspect_file(processed_source)
77
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
78
+ `inspect_file` is deprecated. Use `investigate` instead.
79
+ WARNING
80
+
77
81
  investigate(processed_source).offenses
78
82
  end
79
83
 
@@ -108,6 +112,10 @@ module RuboCop
108
112
 
109
113
  # @deprecated
110
114
  def forces
115
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
116
+ `forces` is deprecated.
117
+ WARNING
118
+
111
119
  @forces ||= self.class.forces_for(cops)
112
120
  end
113
121
 
@@ -20,6 +20,10 @@ module RuboCop
20
20
 
21
21
  # @deprecated Use `ProcessedSource#line_with_comment?`, `contains_comment?` or similar
22
22
  def comment_lines?(node)
23
+ warn Rainbow(<<~WARNING).yellow, uplevel: 1
24
+ `comment_lines?` is deprecated. Use `ProcessedSource#line_with_comment?`, `contains_comment?` or similar instead.
25
+ WARNING
26
+
23
27
  processed_source[line_range(node)].any? { |line| comment_line?(line) }
24
28
  end
25
29
 
@@ -173,7 +177,9 @@ module RuboCop
173
177
  def same_line?(node1, node2)
174
178
  line1 = line(node1)
175
179
  line2 = line(node2)
176
- line1 && line2 && line1 == line2
180
+ return false unless line1 && line2
181
+
182
+ line1 == line2
177
183
  end
178
184
 
179
185
  def indent(node, offset: 0)
@@ -306,7 +306,7 @@ class CopsDocumentationGenerator # rubocop:disable Metrics/ClassLength
306
306
  filename = "#{department_to_basename(department)}.adoc"
307
307
  content = +"=== Department xref:#{filename}[#{type_title}]\n\n"
308
308
  cops_of_department(department).each do |cop|
309
- anchor = cop.cop_name.sub('/', '').downcase
309
+ anchor = cop.cop_name.delete('/').downcase
310
310
  content << "* xref:#{filename}##{anchor}[#{cop.cop_name}]\n"
311
311
  end
312
312
 
@@ -22,26 +22,9 @@ module RuboCop
22
22
  module Base
23
23
  attr_accessor :origin
24
24
 
25
- if Gem::Version.new(Regexp::Parser::VERSION) >= Gem::Version.new('2.0')
26
- # Shortcut to `loc.expression`
27
- def expression
28
- @expression ||= origin.adjust(begin_pos: ts, end_pos: ts + full_length)
29
- end
30
- # Please remove this `else` branch when support for regexp_parser 1.8 will be dropped.
31
- # It's for compatibility with regexp_parser 1.8 and will never be maintained.
32
- else
33
- attr_accessor :source
34
-
35
- def start_index
36
- # ts is a byte index; convert it to a character index
37
- @start_index ||= source.byteslice(0, ts).length
38
- end
39
-
40
- # Shortcut to `loc.expression`
41
- def expression
42
- end_pos = start_index + full_length
43
- @expression ||= origin.adjust(begin_pos: start_index, end_pos: end_pos)
44
- end
25
+ # Shortcut to `loc.expression`
26
+ def expression
27
+ @expression ||= origin.adjust(begin_pos: ts, end_pos: ts + full_length)
45
28
  end
46
29
 
47
30
  # @returns a location map like `parser` does, with:
@@ -69,8 +52,8 @@ module RuboCop
69
52
 
70
53
  body = expression.adjust(end_pos: -q.text.length)
71
54
  q.origin = origin
72
- q.source = source if q.respond_to?(:source=) # for regexp_parser 1.8
73
55
  q_loc = q.expression
56
+
74
57
  { body: body, quantifier: q_loc }
75
58
  end
76
59
  end
@@ -52,7 +52,9 @@ module RuboCop
52
52
 
53
53
  template = File.read(TEMPLATE_PATH, encoding: Encoding::UTF_8)
54
54
  erb = ERB.new(template)
55
- html = erb.result(context.binding).lines.map { (_1 =~ /^\s*$/).nil? ? _1 : "\n" }.join
55
+ html = erb.result(context.binding).lines.map do |line|
56
+ line.match?(/\A\s*\z/) ? "\n" : line
57
+ end.join
56
58
 
57
59
  output.write html
58
60
  end
@@ -50,6 +50,26 @@ RSpec.shared_context 'isolated environment' do # rubocop:disable Metrics/BlockLe
50
50
  end
51
51
  end
52
52
 
53
+ # Workaround for https://github.com/rubocop/rubocop/issues/12978,
54
+ # there should already be no gemfile in the temp directory
55
+ RSpec.shared_context 'isolated bundler' do
56
+ around do |example|
57
+ # No bundler env and reset cached gemfile path
58
+ Bundler.with_unbundled_env do
59
+ old_values = Bundler.instance_variables.to_h do |name|
60
+ [name, Bundler.instance_variable_get(name)]
61
+ end
62
+ Bundler.instance_variables.each { |name| Bundler.remove_instance_variable(name) }
63
+ example.call
64
+ ensure
65
+ Bundler.instance_variables.each { |name| Bundler.remove_instance_variable(name) }
66
+ old_values.each do |name, value|
67
+ Bundler.instance_variable_set(name, value)
68
+ end
69
+ end
70
+ end
71
+ end
72
+
53
73
  RSpec.shared_context 'maintain registry' do
54
74
  around(:each) { |example| RuboCop::Cop::Registry.with_temporary_global { example.run } }
55
75
 
@@ -13,6 +13,7 @@ RSpec.configure do |config|
13
13
  config.include HostEnvironmentSimulatorHelper
14
14
  config.include_context 'config', :config
15
15
  config.include_context 'isolated environment', :isolated_environment
16
+ config.include_context 'isolated bundler', :isolated_bundler
16
17
  config.include_context 'lsp', :lsp
17
18
  config.include_context 'maintain registry', :restore_registry
18
19
  config.include_context 'ruby 2.0', :ruby20
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'digest'
3
4
  require 'pathname'
4
5
  require_relative '../cache_config'
5
6
  require_relative '../config_finder'
@@ -19,6 +20,7 @@ module RuboCop
19
20
  # @api private
20
21
  class Cache
21
22
  GEMFILE_NAMES = %w[Gemfile gems.rb].freeze
23
+ LOCKFILE_NAMES = %w[Gemfile.lock gems.locked].freeze
22
24
 
23
25
  class << self
24
26
  attr_accessor :cache_root_path
@@ -41,6 +43,14 @@ module RuboCop
41
43
  @project_dir_cache_key ||= project_dir[1..].tr('/', '+')
42
44
  end
43
45
 
46
+ def restart_key
47
+ lockfile_path = LOCKFILE_NAMES.map do |lockfile_name|
48
+ Pathname(project_dir).join(lockfile_name)
49
+ end.find(&:exist?)
50
+
51
+ Digest::SHA1.hexdigest(lockfile_path&.read || RuboCop::Version::STRING)
52
+ end
53
+
44
54
  def dir
45
55
  Pathname.new(File.join(cache_path, project_dir_cache_key)).tap do |d|
46
56
  d.mkpath unless d.exist?
@@ -41,7 +41,7 @@ module RuboCop
41
41
  end
42
42
 
43
43
  def incompatible_version?
44
- Cache.version_path.read != RuboCop::Version::STRING
44
+ Cache.version_path.read != Cache.restart_key
45
45
  end
46
46
 
47
47
  def stderr
@@ -54,7 +54,7 @@ module RuboCop
54
54
  end
55
55
 
56
56
  status = Cache.status_path.read
57
- raise "RuboCop server: '#{status}' is not a valid status!" if (status =~ /^\d+$/).nil?
57
+ raise "RuboCop server: '#{status}' is not a valid status!" unless /\A\d+\z/.match?(status)
58
58
 
59
59
  status.to_i
60
60
  end
@@ -34,7 +34,7 @@ module RuboCop
34
34
  exit 0
35
35
  end
36
36
 
37
- Cache.write_version_file(RuboCop::Version::STRING)
37
+ Cache.write_version_file(Cache.restart_key)
38
38
 
39
39
  host = ENV.fetch('RUBOCOP_SERVER_HOST', '127.0.0.1')
40
40
  port = ENV.fetch('RUBOCOP_SERVER_PORT', 0)