rubocop 1.62.1 → 1.63.1

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +1 -1
  3. data/config/default.yml +23 -3
  4. data/lib/rubocop/config.rb +33 -10
  5. data/lib/rubocop/config_obsoletion.rb +1 -1
  6. data/lib/rubocop/cop/base.rb +37 -0
  7. data/lib/rubocop/cop/internal_affairs/example_description.rb +1 -0
  8. data/lib/rubocop/cop/lint/assignment_in_condition.rb +2 -2
  9. data/lib/rubocop/cop/lint/debugger.rb +27 -2
  10. data/lib/rubocop/cop/lint/empty_conditional_body.rb +1 -1
  11. data/lib/rubocop/cop/lint/non_deterministic_require_order.rb +1 -1
  12. data/lib/rubocop/cop/lint/redundant_with_index.rb +3 -0
  13. data/lib/rubocop/cop/mixin/code_length.rb +12 -1
  14. data/lib/rubocop/cop/mixin/method_complexity.rb +15 -6
  15. data/lib/rubocop/cop/mixin/safe_assignment.rb +1 -1
  16. data/lib/rubocop/cop/naming/block_forwarding.rb +31 -12
  17. data/lib/rubocop/cop/naming/file_name.rb +2 -2
  18. data/lib/rubocop/cop/naming/inclusive_language.rb +1 -2
  19. data/lib/rubocop/cop/style/alias.rb +1 -0
  20. data/lib/rubocop/cop/style/collection_compact.rb +3 -3
  21. data/lib/rubocop/cop/style/copyright.rb +16 -11
  22. data/lib/rubocop/cop/style/eval_with_location.rb +3 -1
  23. data/lib/rubocop/cop/style/exact_regexp_match.rb +2 -1
  24. data/lib/rubocop/cop/style/format_string.rb +9 -9
  25. data/lib/rubocop/cop/style/map_into_array.rb +175 -0
  26. data/lib/rubocop/cop/style/map_to_hash.rb +1 -1
  27. data/lib/rubocop/cop/style/map_to_set.rb +1 -1
  28. data/lib/rubocop/cop/style/redundant_argument.rb +24 -1
  29. data/lib/rubocop/cop/style/redundant_current_directory_in_path.rb +1 -1
  30. data/lib/rubocop/cop/style/redundant_each.rb +1 -1
  31. data/lib/rubocop/cop/style/redundant_filter_chain.rb +1 -1
  32. data/lib/rubocop/cop/style/redundant_line_continuation.rb +8 -14
  33. data/lib/rubocop/cop/style/redundant_percent_q.rb +1 -1
  34. data/lib/rubocop/cop/style/require_order.rb +1 -1
  35. data/lib/rubocop/cop/team.rb +3 -0
  36. data/lib/rubocop/formatter/clang_style_formatter.rb +3 -7
  37. data/lib/rubocop/formatter/tap_formatter.rb +3 -7
  38. data/lib/rubocop/lockfile.rb +46 -7
  39. data/lib/rubocop/rspec/expect_offense.rb +8 -0
  40. data/lib/rubocop/rspec/shared_contexts.rb +13 -1
  41. data/lib/rubocop/runner.rb +3 -0
  42. data/lib/rubocop/version.rb +2 -2
  43. data/lib/rubocop.rb +1 -0
  44. metadata +5 -4
@@ -25,27 +25,27 @@ module RuboCop
25
25
  #
26
26
  # @example EnforcedStyle: format (default)
27
27
  # # bad
28
- # puts sprintf('%10s', 'hoge')
29
- # puts '%10s' % 'hoge'
28
+ # puts sprintf('%10s', 'foo')
29
+ # puts '%10s' % 'foo'
30
30
  #
31
31
  # # good
32
- # puts format('%10s', 'hoge')
32
+ # puts format('%10s', 'foo')
33
33
  #
34
34
  # @example EnforcedStyle: sprintf
35
35
  # # bad
36
- # puts format('%10s', 'hoge')
37
- # puts '%10s' % 'hoge'
36
+ # puts format('%10s', 'foo')
37
+ # puts '%10s' % 'foo'
38
38
  #
39
39
  # # good
40
- # puts sprintf('%10s', 'hoge')
40
+ # puts sprintf('%10s', 'foo')
41
41
  #
42
42
  # @example EnforcedStyle: percent
43
43
  # # bad
44
- # puts format('%10s', 'hoge')
45
- # puts sprintf('%10s', 'hoge')
44
+ # puts format('%10s', 'foo')
45
+ # puts sprintf('%10s', 'foo')
46
46
  #
47
47
  # # good
48
- # puts '%10s' % 'hoge'
48
+ # puts '%10s' % 'foo'
49
49
  #
50
50
  class FormatString < Base
51
51
  include ConfigurableEnforcedStyle
@@ -0,0 +1,175 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Style
6
+ # Checks for usages of `each` with `<<`, `push`, or `append` which
7
+ # can be replaced by `map`.
8
+ #
9
+ # If `PreferredMethods` is configured for `map` in `Style/CollectionMethods`,
10
+ # this cop uses the specified method for replacement.
11
+ #
12
+ # NOTE: The return value of `Enumerable#each` is `self`, whereas the
13
+ # return value of `Enumerable#map` is an `Array`. They are not autocorrected
14
+ # when a return value could be used because these types differ.
15
+ #
16
+ # NOTE: It only detects when the mapping destination is a local variable
17
+ # initialized as an empty array and referred to only by the pushing operation.
18
+ # This is because, if not, it's challenging to statically guarantee that the
19
+ # mapping destination variable remains an empty array:
20
+ #
21
+ # [source,ruby]
22
+ # ----
23
+ # @dest = []
24
+ # src.each { |e| @dest << e * 2 } # `src` method may mutate `@dest`
25
+ #
26
+ # dest = []
27
+ # src.each { |e| dest << transform(e, dest) } # `transform` method may mutate `dest`
28
+ # ----
29
+ #
30
+ # @safety
31
+ # This cop is unsafe because not all objects that have an `each`
32
+ # method also have a `map` method (e.g. `ENV`). Additionally, for calls
33
+ # with a block, not all objects that have a `map` method return an array
34
+ # (e.g. `Enumerator::Lazy`).
35
+ #
36
+ # @example
37
+ # # bad
38
+ # dest = []
39
+ # src.each { |e| dest << e * 2 }
40
+ # dest
41
+ #
42
+ # # good
43
+ # dest = src.map { |e| e * 2 }
44
+ #
45
+ # # good - contains another operation
46
+ # dest = []
47
+ # src.each { |e| dest << e * 2; puts e }
48
+ # dest
49
+ #
50
+ class MapIntoArray < Base
51
+ include RangeHelp
52
+ extend AutoCorrector
53
+
54
+ MSG = 'Use `%<new_method_name>s` instead of `each` to map elements into an array.'
55
+
56
+ # @!method each_block_with_push?(node)
57
+ def_node_matcher :each_block_with_push?, <<-PATTERN
58
+ [
59
+ ^({begin kwbegin} ...)
60
+ ({block numblock} (send _ :each) _
61
+ (send (lvar _) {:<< :push :append} _))
62
+ ]
63
+ PATTERN
64
+
65
+ # @!method empty_array_asgn?(node)
66
+ def_node_matcher :empty_array_asgn?, '(lvasgn _ (array))'
67
+
68
+ # @!method lvar_ref?(node, name)
69
+ def_node_matcher :lvar_ref?, '(lvar %1)'
70
+
71
+ def self.joining_forces
72
+ VariableForce
73
+ end
74
+
75
+ def after_leaving_scope(scope, _variable_table)
76
+ (@scopes ||= []) << scope
77
+ end
78
+
79
+ def on_block(node)
80
+ return unless each_block_with_push?(node)
81
+
82
+ dest_var = find_dest_var(node)
83
+ return unless (asgn = find_closest_assignment(node, dest_var))
84
+ return unless empty_array_asgn?(asgn)
85
+ return unless dest_used_only_for_mapping?(node, dest_var, asgn)
86
+
87
+ register_offense(node, dest_var, asgn)
88
+ end
89
+
90
+ alias on_numblock on_block
91
+
92
+ private
93
+
94
+ def find_dest_var(block)
95
+ node = block.body.receiver
96
+ name = node.children.first
97
+
98
+ candidates = @scopes.lazy.filter_map { |s| s.variables[name] }
99
+ candidates.find { |v| v.references.any? { |n| n.node.equal?(node) } }
100
+ end
101
+
102
+ def find_closest_assignment(block, dest_var)
103
+ dest_var.assignments.reverse_each.lazy.map(&:node).find do |node|
104
+ node.source_range.end_pos < block.source_range.begin_pos
105
+ end
106
+ end
107
+
108
+ def dest_used_only_for_mapping?(block, dest_var, asgn)
109
+ range = asgn.source_range.join(block.source_range)
110
+
111
+ asgn.parent.equal?(block.parent) &&
112
+ dest_var.references.one? { |r| range.contains?(r.node.source_range) } &&
113
+ dest_var.assignments.one? { |a| range.contains?(a.node.source_range) }
114
+ end
115
+
116
+ def register_offense(block, dest_var, asgn)
117
+ add_offense(block, message: format(MSG, new_method_name: new_method_name)) do |corrector|
118
+ next if return_value_used?(block)
119
+
120
+ corrector.replace(block.send_node.selector, new_method_name)
121
+ remove_assignment(corrector, asgn)
122
+ correct_push_node(corrector, block.body)
123
+ correct_return_value_handling(corrector, block, dest_var)
124
+ end
125
+ end
126
+
127
+ def new_method_name
128
+ default = 'map'
129
+ alternative = config.for_cop('Style/CollectionMethods').dig('PreferredMethods', default)
130
+ alternative || default
131
+ end
132
+
133
+ def return_value_used?(node)
134
+ parent = node.parent
135
+
136
+ case parent&.type
137
+ when nil
138
+ false
139
+ when :begin, :kwbegin
140
+ !node.right_sibling && return_value_used?(parent)
141
+ when :block, :numblock
142
+ !parent.void_context?
143
+ else
144
+ true
145
+ end
146
+ end
147
+
148
+ def remove_assignment(corrector, asgn)
149
+ range = range_with_surrounding_space(asgn.source_range, side: :right)
150
+ range = range_with_surrounding_space(range, side: :right, newlines: false)
151
+
152
+ corrector.remove(range)
153
+ end
154
+
155
+ def correct_push_node(corrector, push_node)
156
+ range = push_node.source_range
157
+ arg_range = push_node.first_argument.source_range
158
+
159
+ corrector.remove(range_between(range.begin_pos, arg_range.begin_pos))
160
+ corrector.remove(range_between(arg_range.end_pos, range.end_pos))
161
+ end
162
+
163
+ def correct_return_value_handling(corrector, block, dest_var)
164
+ next_node = block.right_sibling
165
+
166
+ if lvar_ref?(next_node, dest_var.name)
167
+ corrector.remove(range_with_surrounding_space(next_node.source_range, side: :left))
168
+ end
169
+
170
+ corrector.insert_before(block, "#{dest_var.name} = ")
171
+ end
172
+ end
173
+ end
174
+ end
175
+ end
@@ -55,7 +55,7 @@ module RuboCop
55
55
  message = format(MSG, method: map_node.loc.selector.source, dot: to_h_node.loc.dot.source)
56
56
  add_offense(map_node.loc.selector, message: message) do |corrector|
57
57
  # If the `to_h` call already has a block, do not autocorrect.
58
- next if to_h_node.block_node
58
+ next if to_h_node.block_literal?
59
59
 
60
60
  autocorrect(corrector, to_h_node, map_node)
61
61
  end
@@ -44,7 +44,7 @@ module RuboCop
44
44
  message = format(MSG, method: map_node.loc.selector.source)
45
45
  add_offense(map_node.loc.selector, message: message) do |corrector|
46
46
  # If the `to_set` call already has a block, do not autocorrect.
47
- next if to_set_node.block_node
47
+ next if to_set_node.block_literal?
48
48
 
49
49
  autocorrect(corrector, to_set_node, map_node)
50
50
  end
@@ -81,7 +81,13 @@ module RuboCop
81
81
  redundant_argument = redundant_arg_for_method(node.method_name.to_s)
82
82
  return false if redundant_argument.nil?
83
83
 
84
- node.first_argument.source.sub(/\A'/, '"').sub(/'\z/, '"') == redundant_argument
84
+ target_argument = if node.first_argument.respond_to?(:value)
85
+ node.first_argument.value
86
+ else
87
+ node.first_argument
88
+ end
89
+
90
+ argument_matched?(target_argument, redundant_argument)
85
91
  end
86
92
 
87
93
  def redundant_arg_for_method(method_name)
@@ -98,6 +104,23 @@ module RuboCop
98
104
  range_with_surrounding_space(node.first_argument.source_range, newlines: false)
99
105
  end
100
106
  end
107
+
108
+ def argument_matched?(target_argument, redundant_argument)
109
+ argument = if target_argument.is_a?(AST::Node)
110
+ target_argument.source
111
+ elsif exclude_cntrl_character?(target_argument, redundant_argument)
112
+ target_argument.inspect
113
+ else
114
+ target_argument.to_s
115
+ end
116
+
117
+ argument == redundant_argument
118
+ end
119
+
120
+ def exclude_cntrl_character?(target_argument, redundant_argument)
121
+ !target_argument.to_s.sub(/\A'/, '"').sub(/'\z/, '"').match?(/[[:cntrl:]]/) ||
122
+ !redundant_argument.match?(/[[:cntrl:]]/)
123
+ end
101
124
  end
102
125
  end
103
126
  end
@@ -18,10 +18,10 @@ module RuboCop
18
18
  extend AutoCorrector
19
19
 
20
20
  MSG = 'Remove the redundant current directory path.'
21
+ RESTRICT_ON_SEND = %i[require_relative].freeze
21
22
  CURRENT_DIRECTORY_PATH = './'
22
23
 
23
24
  def on_send(node)
24
- return unless node.method?(:require_relative)
25
25
  return unless (first_argument = node.first_argument)
26
26
  return unless first_argument.str_content&.start_with?(CURRENT_DIRECTORY_PATH)
27
27
  return unless (index = first_argument.source.index(CURRENT_DIRECTORY_PATH))
@@ -86,7 +86,7 @@ module RuboCop
86
86
  def range(node)
87
87
  return node.selector unless node.method?(:each)
88
88
 
89
- if node.parent.call_type?
89
+ if node.parent&.call_type?
90
90
  node.selector.join(node.parent.loc.dot)
91
91
  else
92
92
  node.loc.dot.join(node.selector)
@@ -79,7 +79,7 @@ module RuboCop
79
79
  private_constant :REPLACEMENT_METHODS
80
80
 
81
81
  def on_send(node)
82
- return if node.arguments? || node.block_node
82
+ return if node.arguments? || node.block_literal?
83
83
 
84
84
  select_predicate?(node) do |select_node, filter_method|
85
85
  return if RAILS_METHODS.include?(filter_method) && !active_support_extensions_enabled?
@@ -72,7 +72,7 @@ module RuboCop
72
72
  ALLOWED_STRING_TOKENS = %i[tSTRING tSTRING_CONTENT].freeze
73
73
  ARGUMENT_TYPES = %i[
74
74
  kFALSE kNIL kSELF kTRUE tCONSTANT tCVAR tFLOAT tGVAR tIDENTIFIER tINTEGER tIVAR
75
- tLABEL tLBRACK tLCURLY tLPAREN_ARG tSTRING tSTRING_BEG tSYMBOL tXSTRING_BEG
75
+ tLBRACK tLCURLY tLPAREN_ARG tSTRING tSTRING_BEG tSYMBOL tXSTRING_BEG
76
76
  ].freeze
77
77
 
78
78
  def on_new_investigation
@@ -124,10 +124,8 @@ module RuboCop
124
124
  return true unless (node = find_node_for_line(range.line))
125
125
  return false if argument_newline?(node)
126
126
 
127
- continuation_node = node.parent || node
128
- return false if allowed_type?(node) || allowed_type?(continuation_node)
129
-
130
- continuation_node.source.include?("\n") || continuation_node.source.include?("\\\n")
127
+ source = node.parent ? node.parent.source : node.source
128
+ parse(source.gsub("\\\n", "\n")).valid_syntax?
131
129
  end
132
130
 
133
131
  def inside_string_literal?(range, token)
@@ -142,22 +140,22 @@ module RuboCop
142
140
  current_token.type == :tIDENTIFIER && ARGUMENT_TYPES.include?(next_token.type)
143
141
  end
144
142
 
145
- # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
143
+ # rubocop:disable Metrics/AbcSize
146
144
  def argument_newline?(node)
147
145
  node = node.to_a.last if node.assignment?
148
146
  return false if node.parenthesized_call?
149
147
 
150
148
  node = node.children.first if node.root? && node.begin_type?
151
149
 
152
- if argument_is_method?(node) || node.begin_type?
153
- argument_newline?(node.children.first)
150
+ if argument_is_method?(node)
151
+ argument_newline?(node.first_argument)
154
152
  else
155
153
  return false unless method_call_with_arguments?(node)
156
154
 
157
- !same_line?(node, node.first_argument)
155
+ node.loc.selector.line != node.first_argument.loc.line
158
156
  end
159
157
  end
160
- # rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
158
+ # rubocop:enable Metrics/AbcSize
161
159
 
162
160
  def find_node_for_line(line)
163
161
  processed_source.ast.each_node do |node|
@@ -165,10 +163,6 @@ module RuboCop
165
163
  end
166
164
  end
167
165
 
168
- def allowed_type?(node)
169
- node.and_type? || node.or_type? || (node.if_type? && node.ternary?)
170
- end
171
-
172
166
  def same_line?(node, line)
173
167
  return false unless (source_range = node.source_range)
174
168
 
@@ -53,7 +53,7 @@ module RuboCop
53
53
  return if interpolated_quotes?(node) || allowed_percent_q?(node)
54
54
 
55
55
  add_offense(node) do |corrector|
56
- delimiter = /^%Q[^"]+$|'/.match?(node.source) ? QUOTE : SINGLE_QUOTE
56
+ delimiter = /\A%Q[^"]+\z|'/.match?(node.source) ? QUOTE : SINGLE_QUOTE
57
57
 
58
58
  corrector.replace(node.loc.begin, delimiter)
59
59
  corrector.replace(node.loc.end, delimiter)
@@ -131,7 +131,7 @@ module RuboCop
131
131
  end
132
132
 
133
133
  def in_same_section?(node1, node2)
134
- !node1.source_range.with(end_pos: node2.source_range.end_pos).source.include?("\n\n")
134
+ !node1.source_range.join(node2.source_range.end).source.include?("\n\n")
135
135
  end
136
136
  end
137
137
  end
@@ -174,6 +174,9 @@ module RuboCop
174
174
  end
175
175
 
176
176
  def support_target_rails_version?(cop)
177
+ # In this case, the rails version was already checked by `#excluded_file?`
178
+ return true if defined?(RuboCop::Rails::TargetRailsVersion::USES_REQUIRES_GEM_API)
179
+
177
180
  return true unless cop.class.respond_to?(:support_target_rails_version?)
178
181
 
179
182
  cop.class.support_target_rails_version?(cop.target_rails_version)
@@ -24,14 +24,10 @@ module RuboCop
24
24
  message: message(offense)
25
25
  )
26
26
 
27
- begin
28
- return unless valid_line?(offense)
27
+ return unless valid_line?(offense)
29
28
 
30
- report_line(offense.location)
31
- report_highlighted_area(offense.highlighted_area)
32
- rescue IndexError
33
- # range is not on a valid line; perhaps the source file is empty
34
- end
29
+ report_line(offense.location)
30
+ report_highlighted_area(offense.highlighted_area)
35
31
  end
36
32
 
37
33
  def valid_line?(offense)
@@ -53,14 +53,10 @@ module RuboCop
53
53
  message: message(offense)
54
54
  )
55
55
 
56
- begin
57
- return unless valid_line?(offense)
56
+ return unless valid_line?(offense)
58
57
 
59
- report_line(offense.location)
60
- report_highlighted_area(offense.highlighted_area)
61
- rescue IndexError
62
- # range is not on a valid line; perhaps the source file is empty
63
- end
58
+ report_line(offense.location)
59
+ report_highlighted_area(offense.highlighted_area)
64
60
  end
65
61
 
66
62
  def annotate_message(msg)
@@ -1,18 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ begin
4
+ require 'bundler'
5
+ rescue LoadError
6
+ nil
7
+ end
8
+
3
9
  module RuboCop
4
10
  # Encapsulation of a lockfile for use when checking for gems.
5
11
  # Does not actually resolve gems, just parses the lockfile.
6
12
  # @api private
7
13
  class Lockfile
8
- # Gems that the bundle depends on
14
+ # @param [String, Pathname, nil] lockfile_path
15
+ def initialize(lockfile_path = nil)
16
+ lockfile_path ||= defined?(Bundler) ? Bundler.default_lockfile : nil
17
+
18
+ @lockfile_path = lockfile_path
19
+ end
20
+
21
+ # Gems that the bundle directly depends on.
22
+ # @return [Array<Bundler::Dependency>, nil]
9
23
  def dependencies
10
24
  return [] unless parser
11
25
 
12
26
  parser.dependencies.values
13
27
  end
14
28
 
15
- # All activated gems, including transitive dependencies
29
+ # All activated gems, including transitive dependencies.
30
+ # @return [Array<Bundler::Dependency>, nil]
16
31
  def gems
17
32
  return [] unless parser
18
33
 
@@ -21,20 +36,44 @@ module RuboCop
21
36
  parser.dependencies.values.concat(parser.specs.flat_map(&:dependencies))
22
37
  end
23
38
 
39
+ # Returns the locked versions of gems from this lockfile.
40
+ # @param [Boolean] include_transitive_dependencies: When false, only direct dependencies
41
+ # are returned, i.e. those listed explicitly in the `Gemfile`.
42
+ # @returns [Hash{String => Gem::Version}] The locked gem versions, keyed by the gems' names.
43
+ def gem_versions(include_transitive_dependencies: true)
44
+ return {} unless parser
45
+
46
+ all_gem_versions = parser.specs.to_h { |spec| [spec.name, spec.version] }
47
+
48
+ if include_transitive_dependencies
49
+ all_gem_versions
50
+ else
51
+ direct_dep_names = parser.dependencies.keys
52
+ all_gem_versions.slice(*direct_dep_names)
53
+ end
54
+ end
55
+
56
+ # Whether this lockfile includes the named gem, directly or indirectly.
57
+ # @param [String] name
58
+ # @return [Boolean]
24
59
  def includes_gem?(name)
25
60
  gems.any? { |gem| gem.name == name }
26
61
  end
27
62
 
28
63
  private
29
64
 
65
+ # @return [Bundler::LockfileParser, nil]
30
66
  def parser
31
- return unless defined?(Bundler) && Bundler.default_lockfile
32
67
  return @parser if defined?(@parser)
33
68
 
34
- lockfile = Bundler.read_file(Bundler.default_lockfile)
35
- @parser = lockfile ? Bundler::LockfileParser.new(lockfile) : nil
36
- rescue Bundler::BundlerError
37
- nil
69
+ @parser = if defined?(::Bundler) && @lockfile_path
70
+ begin
71
+ lockfile = ::Bundler.read_file(@lockfile_path)
72
+ lockfile ? ::Bundler::LockfileParser.new(lockfile) : nil
73
+ rescue ::Bundler::BundlerError
74
+ nil
75
+ end
76
+ end
38
77
  end
39
78
  end
40
79
  end
@@ -111,6 +111,7 @@ module RuboCop
111
111
  source
112
112
  end
113
113
 
114
+ # rubocop:disable Metrics/AbcSize
114
115
  def expect_offense(source, file = nil, severity: nil, chomp: false, **replacements)
115
116
  expected_annotations = parse_annotations(source, **replacements)
116
117
  source = expected_annotations.plain_source
@@ -123,8 +124,15 @@ module RuboCop
123
124
  expect(actual_annotations).to eq(expected_annotations), ''
124
125
  expect(@offenses.map(&:severity).uniq).to eq([severity]) if severity
125
126
 
127
+ # Validate that all offenses have a range that formatters can display
128
+ expect do
129
+ @offenses.each { |offense| offense.location.source_line }
130
+ end.not_to raise_error, 'One of the offenses has a misconstructed range, for ' \
131
+ 'example if the offense is on line 1 and the source is empty'
132
+
126
133
  @offenses
127
134
  end
135
+ # rubocop:enable Metrics/AbcSize
128
136
 
129
137
  # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity
130
138
  def expect_correction(correction, loop: true, source: nil)
@@ -110,7 +110,19 @@ RSpec.shared_context 'config' do # rubocop:disable Metrics/BlockLength
110
110
  let(:config) do
111
111
  hash = { 'AllCops' => all_cops_config, cop_class.cop_name => cur_cop_config }.merge!(other_cops)
112
112
 
113
- RuboCop::Config.new(hash, "#{Dir.pwd}/.rubocop.yml")
113
+ config = RuboCop::Config.new(hash, "#{Dir.pwd}/.rubocop.yml")
114
+
115
+ rails_version_in_gemfile = Gem::Version.new(
116
+ rails_version || RuboCop::Config::DEFAULT_RAILS_VERSION
117
+ )
118
+
119
+ allow(config).to receive(:gem_versions_in_target).and_return(
120
+ {
121
+ 'railties' => rails_version_in_gemfile
122
+ }
123
+ )
124
+
125
+ config
114
126
  end
115
127
 
116
128
  let(:cop) { cop_class.new(config, cop_options) }
@@ -20,6 +20,9 @@ module RuboCop
20
20
  message = 'Infinite loop detected'
21
21
  message += " in #{path}" if path
22
22
  message += " and caused by #{root_cause}" if root_cause
23
+ message += ' Hint: Please update to the latest RuboCop version if not already in use,'
24
+ message += ' and report a bug if the issue still occurs on this version.'
25
+ message += ' Please check the latest version at https://rubygems.org/gems/rubocop'
23
26
  super(message)
24
27
  end
25
28
  end
@@ -3,7 +3,7 @@
3
3
  module RuboCop
4
4
  # This module holds the RuboCop version information.
5
5
  module Version
6
- STRING = '1.62.1'
6
+ STRING = '1.63.1'
7
7
 
8
8
  MSG = '%<version>s (using %<parser_version>s, ' \
9
9
  'rubocop-ast %<rubocop_ast_version>s, ' \
@@ -11,7 +11,7 @@ module RuboCop
11
11
 
12
12
  CANONICAL_FEATURE_NAMES = {
13
13
  'Rspec' => 'RSpec', 'Graphql' => 'GraphQL', 'Md' => 'Markdown', 'Factory_bot' => 'FactoryBot',
14
- 'Thread_safety' => 'ThreadSafety'
14
+ 'Thread_safety' => 'ThreadSafety', 'Rspec_rails' => 'RSpecRails'
15
15
  }.freeze
16
16
  EXTENSION_PATH_NAMES = {
17
17
  'rubocop-md' => 'markdown', 'rubocop-factory_bot' => 'factory_bot'
data/lib/rubocop.rb CHANGED
@@ -557,6 +557,7 @@ require_relative 'rubocop/cop/style/lambda'
557
557
  require_relative 'rubocop/cop/style/lambda_call'
558
558
  require_relative 'rubocop/cop/style/line_end_concatenation'
559
559
  require_relative 'rubocop/cop/style/magic_comment_format'
560
+ require_relative 'rubocop/cop/style/map_into_array'
560
561
  require_relative 'rubocop/cop/style/map_to_hash'
561
562
  require_relative 'rubocop/cop/style/map_to_set'
562
563
  require_relative 'rubocop/cop/style/method_call_without_args_parentheses'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.62.1
4
+ version: 1.63.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bozhidar Batsov
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2024-03-11 00:00:00.000000000 Z
13
+ date: 2024-04-10 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: json
@@ -778,6 +778,7 @@ files:
778
778
  - lib/rubocop/cop/style/line_end_concatenation.rb
779
779
  - lib/rubocop/cop/style/magic_comment_format.rb
780
780
  - lib/rubocop/cop/style/map_compact_with_conditional_block.rb
781
+ - lib/rubocop/cop/style/map_into_array.rb
781
782
  - lib/rubocop/cop/style/map_to_hash.rb
782
783
  - lib/rubocop/cop/style/map_to_set.rb
783
784
  - lib/rubocop/cop/style/method_call_with_args_parentheses.rb
@@ -1031,9 +1032,9 @@ licenses:
1031
1032
  - MIT
1032
1033
  metadata:
1033
1034
  homepage_uri: https://rubocop.org/
1034
- changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.62.1
1035
+ changelog_uri: https://github.com/rubocop/rubocop/releases/tag/v1.63.1
1035
1036
  source_code_uri: https://github.com/rubocop/rubocop/
1036
- documentation_uri: https://docs.rubocop.org/rubocop/1.62/
1037
+ documentation_uri: https://docs.rubocop.org/rubocop/1.63/
1037
1038
  bug_tracker_uri: https://github.com/rubocop/rubocop/issues
1038
1039
  rubygems_mfa_required: 'true'
1039
1040
  post_install_message: