Performance

Performance/AncestorsInclude

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Yes (Unsafe)

1.7

-

Identifies usages of ancestors.include? and change them to use instead.

Safety

This cop is unsafe because it can’t tell whether the receiver is a class or an object. e.g. the false positive was for Nokogiri::XML::Node#ancestors.

Examples

# bad
A.ancestors.include?(B)

# good
A <= B

Performance/ArraySemiInfiniteRangeSlice

Required Ruby version: 2.7
Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

No

Yes (Unsafe)

1.9

-

Identifies places where slicing arrays with semi-infinite ranges can be replaced by Array#take and Array#drop. This cop was created due to a mistake in microbenchmark and hence is disabled by default. Refer https://github.com/rubocop/rubocop-performance/pull/175#issuecomment-731892717

Safety

This cop is unsafe for string slices because strings do not have #take and #drop methods.

Examples

# bad
array[..2]
array[...2]
array[2..]
array[2...]
array.slice(..2)

# good
array.take(3)
array.take(2)
array.drop(2)
array.drop(2)
array.take(3)

Performance/BigDecimalWithNumericArgument

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.7

-

Identifies places where numeric argument to BigDecimal should be converted to string. Initializing from String is faster than from Numeric for BigDecimal.

Examples

# bad
BigDecimal(1, 2)
4.to_d(6)
BigDecimal(1.2, 3, exception: true)
4.5.to_d(6, exception: true)

# good
BigDecimal('1', 2)
BigDecimal('4', 6)
BigDecimal('1.2', 3, exception: true)
BigDecimal('4.5', 6, exception: true)

Performance/BindCall

Required Ruby version: 2.7
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

1.6

-

In Ruby 2.7, UnboundMethod#bind_call has been added.

This cop identifies places where bind(obj).call(args, …​) can be replaced by bind_call(obj, args, …​).

The bind_call(obj, args, …​) method is faster than bind(obj).call(args, …​).

Examples

# bad
umethod.bind(obj).call(foo, bar)
umethod.bind(obj).(foo, bar)

# good
umethod.bind_call(obj, foo, bar)

Performance/BlockGivenWithExplicitBlock

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.9

-

Identifies unnecessary use of a block_given? where explicit check of block argument would suffice.

Examples

# bad
def method(&block)
  do_something if block_given?
end

# good
def method(&block)
  do_something if block
end

# good - block is reassigned
def method(&block)
  block ||= -> { do_something }
  warn "Using default ..." unless block_given?
  # ...
end

Performance/Caller

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.49

1.9

Identifies places where caller[n] can be replaced by caller(n..n).first.

Examples

# bad
caller[1]
caller.first
caller_locations[1]
caller_locations.first

# good
caller(2..2).first
caller(1..1).first
caller_locations(2..2).first
caller_locations(1..1).first

Performance/CaseWhenSplat

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

Yes (Unsafe)

0.34

1.13

Reordering when conditions with a splat to the end of the when branches can improve performance.

Ruby has to allocate memory for the splat expansion every time that the case when statement is run. Since Ruby does not support fall through inside of case when, like some other languages do, the order of the when branches should not matter. By placing any splat expansions at the end of the list of when branches we will reduce the number of times that memory has to be allocated for the expansion. The exception to this is if multiple of your when conditions can be true for any given condition. A likely scenario for this defining a higher level when condition to override a condition that is inside of the splat expansion.

Safety

This cop is not unsafe autocorrection because it is not a guaranteed performance improvement. If the data being processed by the case condition is normalized in a manner that favors hitting a condition in the splat expansion, it is possible that moving the splat condition to the end will use more memory, and run slightly slower. See for more details: https://github.com/rubocop/rubocop/pull/6163

Examples

# bad
case foo
when *condition
  bar
when baz
  foobar
end

case foo
when *[1, 2, 3, 4]
  bar
when 5
  baz
end

# good
case foo
when baz
  foobar
when *condition
  bar
end

case foo
when 1, 2, 3, 4
  bar
when 5
  baz
end

Performance/Casecmp

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Yes (Unsafe)

0.36

-

Identifies places where a case-insensitive string comparison can better be implemented using casecmp.

Safety

This cop is unsafe because String#casecmp and String#casecmp? behave differently when using Non-ASCII characters.

Examples

# bad
str.downcase == 'abc'
str.upcase.eql? 'ABC'
'abc' == str.downcase
'ABC'.eql? str.upcase
str.downcase == str.downcase

# good
str.casecmp('ABC').zero?
'abc'.casecmp(str).zero?

Performance/ChainArrayAllocation

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

0.59

-

Identifies usages of array.compact.flatten.map { |x| x.downcase }. Each of these methods (compact, flatten, map) will generate a new intermediate array that is promptly thrown away. Instead it is faster to mutate when we know it’s safe.

Examples

# bad
array = ["a", "b", "c"]
array.compact.flatten.map { |x| x.downcase }

# good
array = ["a", "b", "c"]
array.compact!
array.flatten!
array.map! { |x| x.downcase }
array

Performance/CollectionLiteralInLoop

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

1.8

-

Identifies places where Array and Hash literals are used within loops. It is better to extract them into a local variable or constant to avoid unnecessary allocations on each iteration.

You can set the minimum number of elements to consider an offense with MinSize.

Examples

# bad
users.select do |user|
  %i[superadmin admin].include?(user.role)
end

# good
admin_roles = %i[superadmin admin]
users.select do |user|
  admin_roles.include?(user.role)
end

# good
ADMIN_ROLES = %i[superadmin admin]
...
users.select do |user|
  ADMIN_ROLES.include?(user.role)
end

Configurable attributes

Name Default value Configurable values

MinSize

1

Integer

Performance/CompareWithBlock

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.46

-

Identifies places where sort { |a, b| a.foo <⇒ b.foo } can be replaced by sort_by(&:foo). This cop also checks sort!, min, max and minmax methods.

Examples

# bad
array.sort   { |a, b| a.foo <=> b.foo }
array.sort!  { |a, b| a.foo <=> b.foo }
array.max    { |a, b| a.foo <=> b.foo }
array.min    { |a, b| a.foo <=> b.foo }
array.minmax { |a, b| a.foo <=> b.foo }
array.sort   { |a, b| a[:foo] <=> b[:foo] }

# good
array.sort_by(&:foo)
array.sort_by!(&:foo)
array.sort_by { |v| v.foo }
array.sort_by do |var|
  var.foo
end
array.max_by(&:foo)
array.min_by(&:foo)
array.minmax_by(&:foo)
array.sort_by { |a| a[:foo] }

Performance/ConcurrentMonotonicTime

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.12

-

Identifies places where Concurrent.monotonic_time can be replaced by Process.clock_gettime(Process::CLOCK_MONOTONIC).

Examples

# bad
Concurrent.monotonic_time

# good
Process.clock_gettime(Process::CLOCK_MONOTONIC)

Performance/ConstantRegexp

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.9

1.10

Finds regular expressions with dynamic components that are all constants.

Ruby allocates a new Regexp object every time it executes a code containing such a regular expression. It is more efficient to extract it into a constant, memoize it, or add an /o option to perform #{} interpolation only once and reuse that Regexp object.

Examples

# bad
def tokens(pattern)
  pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/) }
end

# good
ALL_SEPARATORS = /\A#{SEPARATORS}\Z/
def tokens(pattern)
  pattern.scan(TOKEN).reject { |token| token.match?(ALL_SEPARATORS) }
end

# good
def tokens(pattern)
  pattern.scan(TOKEN).reject { |token| token.match?(/\A#{SEPARATORS}\Z/o) }
end

# good
def separators
  @separators ||= /\A#{SEPARATORS}\Z/
end

Performance/Count

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes (Unsafe)

0.31

1.8

Identifies usages of count on an Enumerable that follow calls to select, find_all, filter or reject. Querying logic can instead be passed to the count call.

Safety

This cop is unsafe because it has known compatibility issues with ActiveRecord and other frameworks. ActiveRecord’s count ignores the block that is passed to it. ActiveRecord will ignore the block that is passed to count. Other methods, such as select, will convert the association to an array and then run the block on the array. A simple work around to make count work with a block is to call to_a.count {…​}.

For example:

`Model.where(id: [1, 2, 3]).select { |m| m.method == true }.size`

becomes:

`Model.where(id: [1, 2, 3]).to_a.count { |m| m.method == true }`

Examples

# bad
[1, 2, 3].select { |e| e > 2 }.size
[1, 2, 3].reject { |e| e > 2 }.size
[1, 2, 3].select { |e| e > 2 }.length
[1, 2, 3].reject { |e| e > 2 }.length
[1, 2, 3].select { |e| e > 2 }.count { |e| e.odd? }
[1, 2, 3].reject { |e| e > 2 }.count { |e| e.even? }
array.select(&:value).count

# good
[1, 2, 3].count { |e| e > 2 }
[1, 2, 3].count { |e| e < 2 }
[1, 2, 3].count { |e| e > 2 && e.odd? }
[1, 2, 3].count { |e| e < 2 && e.even? }
Model.select('field AS field_one').count
Model.select(:value).count

Performance/DeletePrefix

Required Ruby version: 2.5
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Yes (Unsafe)

1.6

1.11

In Ruby 2.5, String#delete_prefix has been added.

This cop identifies places where gsub(/\Aprefix/, '') and sub(/\Aprefix/, '') can be replaced by delete_prefix('prefix').

This cop has SafeMultiline configuration option that true by default because ^prefix is unsafe as it will behave incompatible with delete_prefix for receiver is multiline string.

The delete_prefix('prefix') method is faster than gsub(/\Aprefix/, '').

Safety

This cop is unsafe because Pathname has sub but not delete_prefix.

Examples

# bad
str.gsub(/\Aprefix/, '')
str.gsub!(/\Aprefix/, '')

str.sub(/\Aprefix/, '')
str.sub!(/\Aprefix/, '')

# good
str.delete_prefix('prefix')
str.delete_prefix!('prefix')

SafeMultiline: true (default)

# good
str.gsub(/^prefix/, '')
str.gsub!(/^prefix/, '')
str.sub(/^prefix/, '')
str.sub!(/^prefix/, '')

SafeMultiline: false

# bad
str.gsub(/^prefix/, '')
str.gsub!(/^prefix/, '')
str.sub(/^prefix/, '')
str.sub!(/^prefix/, '')

Configurable attributes

Name Default value Configurable values

SafeMultiline

true

Boolean

Performance/DeleteSuffix

Required Ruby version: 2.5
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Yes (Unsafe)

1.6

1.11

In Ruby 2.5, String#delete_suffix has been added.

This cop identifies places where gsub(/suffix\z/, '') and sub(/suffix\z/, '') can be replaced by delete_suffix('suffix').

This cop has SafeMultiline configuration option that true by default because suffix$ is unsafe as it will behave incompatible with delete_suffix? for receiver is multiline string.

The delete_suffix('suffix') method is faster than gsub(/suffix\z/, '').

Safety

This cop is unsafe because Pathname has sub but not delete_suffix.

Examples

# bad
str.gsub(/suffix\z/, '')
str.gsub!(/suffix\z/, '')

str.sub(/suffix\z/, '')
str.sub!(/suffix\z/, '')

# good
str.delete_suffix('suffix')
str.delete_suffix!('suffix')

SafeMultiline: true (default)

# good
str.gsub(/suffix$/, '')
str.gsub!(/suffix$/, '')
str.sub(/suffix$/, '')
str.sub!(/suffix$/, '')

SafeMultiline: false

# bad
str.gsub(/suffix$/, '')
str.gsub!(/suffix$/, '')
str.sub(/suffix$/, '')
str.sub!(/suffix$/, '')

Configurable attributes

Name Default value Configurable values

SafeMultiline

true

Boolean

Performance/Detect

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes (Unsafe)

0.30

1.8

Identifies usages of first, last, [0] or [-1] chained to select, find_all or filter and change them to use detect instead.

Safety

This cop is unsafe because it assumes that the receiver is an Array or equivalent, but can’t reliably detect it. For example, if the receiver is a Hash, it may report a false positive.

Examples

# bad
[].select { |item| true }.first
[].select { |item| true }.last
[].find_all { |item| true }.first
[].find_all { |item| true }.last
[].filter { |item| true }.first
[].filter { |item| true }.last
[].filter { |item| true }[0]
[].filter { |item| true }[-1]

# good
[].detect { |item| true }
[].reverse.detect { |item| true }

Performance/DoubleStartEndWith

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.36

0.48

Checks for double #start_with? or #end_with? calls separated by ||. In some cases such calls can be replaced with an single #start_with?/#end_with? call.

IncludeActiveSupportAliases configuration option is used to check for starts_with? and ends_with?. These methods are defined by Active Support.

Examples

# bad
str.start_with?("a") || str.start_with?(Some::CONST)
str.start_with?("a", "b") || str.start_with?("c")
str.end_with?(var1) || str.end_with?(var2)

# good
str.start_with?("a", Some::CONST)
str.start_with?("a", "b", "c")
str.end_with?(var1, var2)

IncludeActiveSupportAliases: false (default)

# good
str.starts_with?("a", "b") || str.starts_with?("c")
str.ends_with?(var1) || str.ends_with?(var2)

str.starts_with?("a", "b", "c")
str.ends_with?(var1, var2)

IncludeActiveSupportAliases: true

# bad
str.starts_with?("a", "b") || str.starts_with?("c")
str.ends_with?(var1) || str.ends_with?(var2)

# good
str.starts_with?("a", "b", "c")
str.ends_with?(var1, var2)

Configurable attributes

Name Default value Configurable values

IncludeActiveSupportAliases

false

Boolean

Performance/EndWith

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes (Unsafe)

0.36

1.10

Identifies unnecessary use of a regex where String#end_with? would suffice.

This cop has SafeMultiline configuration option that true by default because end$ is unsafe as it will behave incompatible with end_with? for receiver is multiline string.

Safety

This will change to a new method call which isn’t guaranteed to be on the object. Switching these methods has to be done with knowledge of the types of the variables which rubocop doesn’t have.

Examples

# bad
'abc'.match?(/bc\Z/)
/bc\Z/.match?('abc')
'abc' =~ /bc\Z/
/bc\Z/ =~ 'abc'
'abc'.match(/bc\Z/)
/bc\Z/.match('abc')

# good
'abc'.end_with?('bc')

SafeMultiline: true (default)

# good
'abc'.match?(/bc$/)
/bc$/.match?('abc')
'abc' =~ /bc$/
/bc$/ =~ 'abc'
'abc'.match(/bc$/)
/bc$/.match('abc')

SafeMultiline: false

# bad
'abc'.match?(/bc$/)
/bc$/.match?('abc')
'abc' =~ /bc$/
/bc$/ =~ 'abc'
'abc'.match(/bc$/)
/bc$/.match('abc')

Configurable attributes

Name Default value Configurable values

SafeMultiline

true

Boolean

Performance/FixedSize

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

No

0.35

-

Do not compute the size of statically sized objects.

Examples

# String methods
# bad
'foo'.size
%q[bar].count
%(qux).length

# Symbol methods
# bad
:fred.size
:'baz'.length

# Array methods
# bad
[1, 2, thud].count
%W(1, 2, bar).size

# Hash methods
# bad
{ a: corge, b: grault }.length

# good
foo.size
bar.count
qux.length

# good
:"#{fred}".size
CONST = :baz.length

# good
[1, 2, *thud].count
garply = [1, 2, 3]
garply.size

# good
{ a: corge, **grault }.length
waldo = { a: corge, b: grault }
waldo.size

Performance/FlatMap

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.30

-

Identifies usages of map { …​ }.flatten and change them to use flat_map { …​ } instead.

Examples

# bad
[1, 2, 3, 4].map { |e| [e, e] }.flatten(1)
[1, 2, 3, 4].collect { |e| [e, e] }.flatten(1)

# good
[1, 2, 3, 4].flat_map { |e| [e, e] }
[1, 2, 3, 4].map { |e| [e, e] }.flatten
[1, 2, 3, 4].collect { |e| [e, e] }.flatten

Configurable attributes

Name Default value Configurable values

EnabledForFlattenWithoutParams

false

Boolean

Performance/InefficientHashSearch

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Yes (Unsafe)

0.56

-

Checks for inefficient searching of keys and values within hashes.

Hash#keys.include? is less efficient than Hash#key? because the former allocates a new array and then performs an O(n) search through that array, while Hash#key? does not allocate any array and performs a faster O(1) search for the key.

Hash#values.include? is less efficient than Hash#value?. While they both perform an O(n) search through all of the values, calling values allocates a new array while using value? does not.

Safety

This cop is unsafe because it can’t tell whether the receiver is a hash object.

Examples

# bad
{ a: 1, b: 2 }.keys.include?(:a)
{ a: 1, b: 2 }.keys.include?(:z)
h = { a: 1, b: 2 }; h.keys.include?(100)

# good
{ a: 1, b: 2 }.key?(:a)
{ a: 1, b: 2 }.has_key?(:z)
h = { a: 1, b: 2 }; h.key?(100)

# bad
{ a: 1, b: 2 }.values.include?(2)
{ a: 1, b: 2 }.values.include?('garbage')
h = { a: 1, b: 2 }; h.values.include?(nil)

# good
{ a: 1, b: 2 }.value?(2)
{ a: 1, b: 2 }.has_value?('garbage')
h = { a: 1, b: 2 }; h.value?(nil)

Performance/IoReadlines

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

Yes

1.7

-

Identifies places where inefficient readlines method can be replaced by each_line to avoid fully loading file content into memory.

Examples

# bad
File.readlines('testfile').each { |l| puts l }
IO.readlines('testfile', chomp: true).each { |l| puts l }

conn.readlines(10).map { |l| l.size }
file.readlines.find { |l| l.start_with?('#') }
file.readlines.each { |l| puts l }

# good
File.open('testfile', 'r').each_line { |l| puts l }
IO.open('testfile').each_line(chomp: true) { |l| puts l }

conn.each_line(10).map { |l| l.size }
file.each_line.find { |l| l.start_with?('#') }
file.each_line { |l| puts l }

Performance/MapCompact

Required Ruby version: 2.7
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes (Unsafe)

1.11

-

In Ruby 2.7, Enumerable#filter_map has been added.

This cop identifies places where map { …​ }.compact can be replaced by filter_map.

[true, false, nil].compact              #=> [true, false]
[true, false, nil].filter_map(&:itself) #=> [true]

Safety

This cop’s autocorrection is unsafe because map { …​ }.compact that is not compatible with filter_map.

Examples

# bad
ary.map(&:foo).compact
ary.collect(&:foo).compact

# good
ary.filter_map(&:foo)
ary.map(&:foo).compact!
ary.compact.map(&:foo)

Performance/MethodObjectAsBlock

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

No

1.9

-

Identifies places where methods are converted to blocks, with the use of &method, and passed as arguments to method calls. It is faster to replace those with explicit blocks, calling those methods inside.

Examples

# bad
array.map(&method(:do_something))
[1, 2, 3].each(&out.method(:puts))

# good
array.map { |x| do_something(x) }
[1, 2, 3].each { |x| out.puts(x) }

Performance/OpenStruct

Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

No

No

0.61

-

Checks for OpenStruct.new calls. Instantiation of an OpenStruct invalidates Ruby global method cache as it causes dynamic method definition during program runtime. This could have an effect on performance, especially in case of single-threaded applications with multiple OpenStruct instantiations.

Safety

This cop is unsafe because OpenStruct.new and Struct.new are not equivalent.

Examples

# bad
class MyClass
  def my_method
    OpenStruct.new(my_key1: 'my_value1', my_key2: 'my_value2')
  end
end

# good
class MyClass
  MyStruct = Struct.new(:my_key1, :my_key2)
  def my_method
    MyStruct.new('my_value1', 'my_value2')
  end
end

Performance/RangeInclude

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Yes (Unsafe)

0.36

1.7

Identifies uses of Range#include? and Range#member?, which iterates over each item in a Range to see if a specified item is there. In contrast, Range#cover? simply compares the target item with the beginning and end points of the Range. In a great majority of cases, this is what is wanted.

Safety

This cop is unsafe because Range#include? (or Range#member?) and Range#cover? are not equivalent behavior.

Examples

# bad
('a'..'z').include?('b') # => true
('a'..'z').member?('b')  # => true

# good
('a'..'z').cover?('b') # => true

# Example of a case where `Range#cover?` may not provide
# the desired result:

('a'..'z').cover?('yellow') # => true

Performance/RedundantBlockCall

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.36

-

Identifies the use of a &block parameter and block.call where yield would do just as well.

Examples

# bad
def method(&block)
  block.call
end
def another(&func)
  func.call 1, 2, 3
end

# good
def method
  yield
end
def another
  yield 1, 2, 3
end

Performance/RedundantEqualityComparisonBlock

Required Ruby version: 2.5
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

No

Yes (Unsafe)

1.10

-

Checks for uses Enumerable#all?, Enumerable#any?, Enumerable#one?, and Enumerable#none? are compared with === or similar methods in block.

By default, Object#=== behaves the same as Object#==, but this behavior is appropriately overridden in subclass. For example, Range#=== returns true when argument is within the range.

This cop has AllowRegexpMatch option and it is true by default because regexp.match?('string') often used in block changes to the opposite result:

[/pattern/].all? { |regexp| regexp.match?('pattern') } # => true
[/pattern/].all? { |regexp| regexp =~ 'pattern' }      # => true
[/pattern/].all?('pattern')                            # => false

Safety

This cop is unsafe because === and == do not always behave the same.

Examples

# bad
items.all? { |item| pattern === item }
items.all? { |item| item == other }
items.all? { |item| item.is_a?(Klass) }
items.all? { |item| item.kind_of?(Klass) }

# good
items.all?(pattern)
items.all?(Klass)

AllowRegexpMatch: true (default)

# good
items.all? { |item| item =~ pattern }
items.all? { |item| item.match?(pattern) }

AllowRegexpMatch: false

# bad
items.all? { |item| item =~ pattern }
items.all? { |item| item.match?(pattern) }

Configurable attributes

Name Default value Configurable values

AllowRegexpMatch

true

Boolean

Performance/RedundantMatch

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.36

-

Identifies the use of Regexp#match or String#match, which returns #<MatchData>/nil. The return value of =~ is an integral index/nil and is more performant.

Examples

# bad
do_something if str.match(/regex/)
while regex.match('str')
  do_something
end

# good
method(str =~ /regex/)
return value unless regex =~ 'str'

Performance/RedundantMerge

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

No

Yes (Unsafe)

0.36

1.11

Identifies places where Hash#merge! can be replaced by Hash#[]=. You can set the maximum number of key-value pairs to consider an offense with MaxKeyValuePairs.

Safety

This cop is unsafe because RuboCop cannot determine if the receiver of merge! is actually a hash or not.

Examples

# bad
hash.merge!(a: 1)
hash.merge!({'key' => 'value'})

# good
hash[:a] = 1
hash['key'] = 'value'

MaxKeyValuePairs: 2 (default)

# bad
hash.merge!(a: 1, b: 2)

# good
hash[:a] = 1
hash[:b] = 2

Configurable attributes

Name Default value Configurable values

MaxKeyValuePairs

2

Integer

Performance/RedundantSortBlock

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.7

-

Identifies places where sort { |a, b| a <⇒ b } can be replaced with sort.

Examples

# bad
array.sort { |a, b| a <=> b }

# good
array.sort

Performance/RedundantSplitRegexpArgument

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.10

-

Identifies places where split argument can be replaced from a deterministic regexp to a string.

Examples

# bad
'a,b,c'.split(/,/)

# good
'a,b,c'.split(',')

Performance/RedundantStringChars

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.7

-

Checks for redundant String#chars.

Examples

# bad
str.chars[0..2]
str.chars.slice(0..2)
str.chars.last

# good
str[0..2].chars

# bad
str.chars.first
str.chars.first(2)

# good
str[0]
str[0...2].chars
str[-1]

# bad
str.chars.take(2)
str.chars.length
str.chars.size
str.chars.empty?

# good
str[0...2].chars
str.length
str.size
str.empty?

# For example, if the receiver is an empty string, it will be incompatible.
# If a negative value is specified for the receiver, `nil` is returned.
str.chars.last(2) # Incompatible with `str[-2..-1].chars`.
str.chars.drop(2) # Incompatible with `str[2..-1].chars`.

Performance/RegexpMatch

Required Ruby version: 2.4
Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.47

-

In Ruby 2.4, String#match?, Regexp#match?, and Symbol#match? have been added. The methods are faster than match. Because the methods avoid creating a MatchData object or saving backref. So, when MatchData is not used, use match? instead of match.

Examples

# bad
def foo
  if x =~ /re/
    do_something
  end
end

# bad
def foo
  if x !~ /re/
    do_something
  end
end

# bad
def foo
  if x.match(/re/)
    do_something
  end
end

# bad
def foo
  if /re/ === x
    do_something
  end
end

# good
def foo
  if x.match?(/re/)
    do_something
  end
end

# good
def foo
  if !x.match?(/re/)
    do_something
  end
end

# good
def foo
  if x =~ /re/
    do_something(Regexp.last_match)
  end
end

# good
def foo
  if x.match(/re/)
    do_something($~)
  end
end

# good
def foo
  if /re/ === x
    do_something($~)
  end
end

Performance/ReverseEach

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.30

-

Identifies usages of reverse.each and change them to use reverse_each instead.

If the return value is used, it will not be detected because the result will be different.

[1, 2, 3].reverse.each {} #=> [3, 2, 1]
[1, 2, 3].reverse_each {} #=> [1, 2, 3]

Examples

# bad
items.reverse.each

# good
items.reverse_each

Performance/ReverseFirst

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.7

-

Identifies places where reverse.first(n) and reverse.first can be replaced by last(n).reverse and last.

Examples

# bad
array.reverse.first(5)
array.reverse.first

# good
array.last(5).reverse
array.last

Performance/SelectMap

Required Ruby version: 2.7
Enabled by default Safe Supports autocorrection Version Added Version Changed

Disabled

Yes

No

1.11

-

In Ruby 2.7, Enumerable#filter_map has been added.

This cop identifies places where select.map can be replaced by filter_map.

Examples

# bad
ary.select(&:foo).map(&:bar)
ary.filter(&:foo).map(&:bar)

# good
ary.filter_map { |o| o.bar if o.foo }

Performance/Size

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.30

-

Identifies usages of count on an Array and Hash and change them to size.

TODO: Add advanced detection of variables that could have been assigned to an array or a hash.

Examples

# bad
[1, 2, 3].count
(1..3).to_a.count
Array[*1..3].count
Array(1..3).count

# bad
{a: 1, b: 2, c: 3}.count
[[:foo, :bar], [1, 2]].to_h.count
Hash[*('a'..'z')].count
Hash(key: :value).count

# good
[1, 2, 3].size
(1..3).to_a.size
Array[*1..3].size
Array(1..3).size

# good
{a: 1, b: 2, c: 3}.size
[[:foo, :bar], [1, 2]].to_h.size
Hash[*('a'..'z')].size
Hash(key: :value).size

# good
[1, 2, 3].count { |e| e > 2 }

Performance/SortReverse

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.7

-

Identifies places where sort { |a, b| b <⇒ a } can be replaced by a faster sort.reverse.

Examples

# bad
array.sort { |a, b| b <=> a }

# good
array.sort.reverse

Performance/Squeeze

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.7

-

Identifies places where gsub(/a+/, 'a') and gsub!(/a+/, 'a') can be replaced by squeeze('a') and squeeze!('a').

The squeeze('a') method is faster than gsub(/a+/, 'a').

Examples

# bad
str.gsub(/a+/, 'a')
str.gsub!(/a+/, 'a')

# good
str.squeeze('a')
str.squeeze!('a')

Performance/StartWith

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes (Unsafe)

0.36

1.10

Identifies unnecessary use of a regex where String#start_with? would suffice.

This cop has SafeMultiline configuration option that true by default because ^start is unsafe as it will behave incompatible with start_with? for receiver is multiline string.

Safety

This will change to a new method call which isn’t guaranteed to be on the object. Switching these methods has to be done with knowledge of the types of the variables which rubocop doesn’t have.

Examples

# bad
'abc'.match?(/\Aab/)
/\Aab/.match?('abc')
'abc' =~ /\Aab/
/\Aab/ =~ 'abc'
'abc'.match(/\Aab/)
/\Aab/.match('abc')

# good
'abc'.start_with?('ab')

SafeMultiline: true (default)

# good
'abc'.match?(/^ab/)
/^ab/.match?('abc')
'abc' =~ /^ab/
/^ab/ =~ 'abc'
'abc'.match(/^ab/)
/^ab/.match('abc')

SafeMultiline: false

# bad
'abc'.match?(/^ab/)
/^ab/.match?('abc')
'abc' =~ /^ab/
/^ab/ =~ 'abc'
'abc'.match(/^ab/)
/^ab/.match('abc')

Configurable attributes

Name Default value Configurable values

SafeMultiline

true

Boolean

Performance/StringIdentifierArgument

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes

1.13

-

Identifies places where string identifier argument can be replaced by symbol identifier argument. It prevents the redundancy of the internal string-to-symbol conversion.

This cop targets methods that take identifier (e.g. method name) argument and the following examples are parts of it.

Examples

# bad
send('do_something')
attr_accessor 'do_something'
instance_variable_get('@ivar')

# good
send(:do_something)
attr_accessor :do_something
instance_variable_get(:@ivar)

Performance/StringInclude

Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes (Unsafe)

1.7

1.12

Identifies unnecessary use of a regex where String#include? would suffice.

Safety

This cop’s offenses are not safe to autocorrect if a receiver is nil or a Symbol.

Examples

# bad
str.match?(/ab/)
/ab/.match?(str)
str =~ /ab/
/ab/ =~ str
str.match(/ab/)
/ab/.match(str)

# good
str.include?('ab')

Performance/StringReplacement

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.33

-

Identifies places where gsub can be replaced by tr or delete.

Examples

# bad
'abc'.gsub('b', 'd')
'abc'.gsub('a', '')
'abc'.gsub(/a/, 'd')
'abc'.gsub!('a', 'd')

# good
'abc'.gsub(/.*/, 'a')
'abc'.gsub(/a+/, 'd')
'abc'.tr('b', 'd')
'a b c'.delete(' ')

Performance/Sum

Required Ruby version: 2.4
Enabled by default Safe Supports autocorrection Version Added Version Changed

Pending

Yes

Yes (Unsafe)

1.8

1.13

Identifies places where custom code finding the sum of elements in some Enumerable object can be replaced by Enumerable#sum method.

Safety

Autocorrections are unproblematic wherever an initial value is provided explicitly:

[1, 2, 3].reduce(4, :+) # => 10
[1, 2, 3].sum(4) # => 10

[].reduce(4, :+) # => 4
[].sum(4) # => 4

This also holds true for non-numeric types which implement a :+ method:

['l', 'o'].reduce('Hel', :+) # => "Hello"
['l', 'o'].sum('Hel') # => "Hello"

When no initial value is provided though, Enumerable#reduce will pick the first enumerated value as initial value and successively add all following values to it, whereas Enumerable#sum will set an initial value of 0 (Integer) which can lead to a TypeError:

[].reduce(:+) # => nil
[1, 2, 3].reduce(:+) # => 6
['H', 'e', 'l', 'l', 'o'].reduce(:+) # => "Hello"

[].sum # => 0
[1, 2, 3].sum # => 6
['H', 'e', 'l', 'l', 'o'].sum # => in `+': String can't be coerced into Integer (TypeError)

Examples

OnlySumOrWithInitialValue: false (default)

# bad
[1, 2, 3].inject(:+)                        # Autocorrections for cases without initial value are unsafe
[1, 2, 3].inject(&:+)                       # and will only be performed when using the `-A` option.
[1, 2, 3].reduce { |acc, elem| acc + elem } # They can be prohibited completely using `SafeAutoCorrect: true`.
[1, 2, 3].reduce(10, :+)
[1, 2, 3].map { |elem| elem ** 2 }.sum
[1, 2, 3].collect(&:count).sum(10)

# good
[1, 2, 3].sum
[1, 2, 3].sum(10)
[1, 2, 3].sum { |elem| elem ** 2 }
[1, 2, 3].sum(10, &:count)

OnlySumOrWithInitialValue: true

# bad
[1, 2, 3].reduce(10, :+)
[1, 2, 3].map { |elem| elem ** 2 }.sum
[1, 2, 3].collect(&:count).sum(10)

# good
[1, 2, 3].sum(10)
[1, 2, 3].sum { |elem| elem ** 2 }
[1, 2, 3].sum(10, &:count)

Configurable attributes

Name Default value Configurable values

OnlySumOrWithInitialValue

false

Boolean

Performance/TimesMap

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes (Unsafe)

0.36

1.13

Checks for .times.map calls. In most cases such calls can be replaced with an explicit array creation.

Safety

This cop’s autocorrection is unsafe because Integer#times does nothing if receiver is 0 or less. However, Array.new raises an error if argument is less than 0.

For example:

-1.times{}    # does nothing
Array.new(-1) # ArgumentError: negative array size

Examples

# bad
9.times.map do |i|
  i.to_s
end

# good
Array.new(9) do |i|
  i.to_s
end

Performance/UnfreezeString

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes (Unsafe)

0.50

1.9

In Ruby 2.3 or later, use unary plus operator to unfreeze a string literal instead of String#dup and String.new. Unary plus operator is faster than String#dup.

Safety

This cop’s autocorrection is unsafe because String.new (without operator) is not exactly the same as ''`. These differ in encoding. `String.new.encoding` is always `ASCII-8BIT`. However, `('').encoding is the same as script encoding(e.g. UTF-8). if you expect ASCII-8BIT encoding, disable this cop.

Examples

# bad
''.dup
"something".dup
String.new
String.new('')
String.new('something')

# good
+'something'
+''

Performance/UriDefaultParser

Enabled by default Safe Supports autocorrection Version Added Version Changed

Enabled

Yes

Yes

0.50

-

Identifies places where URI::Parser.new can be replaced by URI::DEFAULT_PARSER.

Examples

# bad
URI::Parser.new

# good
URI::DEFAULT_PARSER