rubocop-performance icon indicating copy to clipboard operation
rubocop-performance copied to clipboard

Include unnecessary blocks in affix checks

Open dduugg opened this issue 7 months ago • 0 comments

Is your feature request related to a problem? Please describe.

Performance/DoubleStartEndWith already exists to flag repeated calls to start_with?/end_with? that could be replaced by a single call. Another pattern involving excess affix checks is:

prefixes.any? { |prefix| str.start_with?(prefix) }

which is less performant and less readable than the varargs version:

str.start_with?(*prefixes)

Benchmark code:

require 'benchmark/ips'

prefixes = 100.times.map { ('a'..'z').to_a.shuffle[0,8].join }
str =  ('a'..'z').to_a.shuffle[0,20].join

Benchmark.ips do |x|
  x.report('block') { prefixes.any? { |prefix| str.start_with?(prefix) } }
  x.report('varargs') { str.start_with?(*prefixes) }
  x.compare!
end

Results:

Warming up --------------------------------------
               block    24.496k i/100ms
             varargs   104.671k i/100ms
Calculating -------------------------------------
               block    247.547k (± 0.8%) i/s    (4.04 μs/i) -      1.249M in   5.047027s
             varargs      1.053M (± 0.8%) i/s  (949.67 ns/i) -      5.338M in   5.069843s

Comparison:
             varargs:  1053000.4 i/s
               block:   247547.4 i/s - 4.25x  slower

Describe the solution you'd like

A new cop (or an expansion to DoubleStartEndWith) that flags start_with?/end_with? called inside an any? block.

Describe alternatives you've considered

🤔

Additional context

https://github.com/Homebrew/brew/pull/20612/files has examples that were manually corrected.

dduugg avatar Sep 06 '25 16:09 dduugg