Class: RuboCop::Cop::Performance::CollectionLiteralInLoop
| Relationships & Source Files | |
| Super Chains via Extension / Inclusion / Inheritance | |
| Class Chain: 
          self,
          Base
         | |
| Instance Chain: 
          self,
          Base
         | |
| Inherits: | Base 
 | 
| Defined in: | lib/rubocop/cop/performance/collection_literal_in_loop.rb | 
Overview
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.
| Note | Since Ruby 3.4, certain simple arguments to Array#include?are
optimized directly in Ruby. This avoids allocations without changing the
code, as such no offense will be registered in those cases. Currently that
includes: strings,self, local variables, instance variables, and method
calls without arguments. Additionally, any number of methods can be chained:[1, 2, 3].include?(@foo)and[1, 2, 3].include?(@foo.bar.baz)both avoid
the array allocation. | 
Constant Summary
- 
    ARRAY_INCLUDE_OPTIMIZED_TYPES =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 66%i[str self lvar ivar send].freeze 
- 
    ARRAY_METHODS =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 64(ENUMERABLE_METHOD_NAMES | NONMUTATING_ARRAY_METHODS).to_set.freeze 
- 
    ENUMERABLE_METHOD_NAMES =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 49(Enumerable.instance_methods + [:each]).to_set.freeze 
- 
    HASH_METHODS =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 76(ENUMERABLE_METHOD_NAMES | NONMUTATING_HASH_METHODS).to_set.freeze 
- 
    LOOP_TYPES =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 47(POST_CONDITION_LOOP_TYPES + %i[while until for]).freeze 
- 
    MSG =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 43'Avoid immutable %<literal_class>s literals in loops. ' \ 'It is better to extract it into a local variable or a constant.' 
- 
    NONMUTATING_ARRAY_METHODS =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 50%i[& * + - <=> == [] all? any? assoc at bsearch bsearch_index collect combination compact count cycle deconstruct difference dig drop drop_while each each_index empty? eql? fetch filter find_index first flatten hash include? index inspect intersection join last length map max min minmax none? one? pack permutation product rassoc reject repeated_combination repeated_permutation reverse reverse_each rindex rotate sample select shuffle size slice sort sum take take_while to_a to_ary to_h to_s transpose union uniq values_at zip |].freeze 
- 
    NONMUTATING_HASH_METHODS =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 68%i[< <= == > >= [] any? assoc compact dig each each_key each_pair each_value empty? eql? fetch fetch_values filter flatten has_key? has_value? hash include? inspect invert key key? keys? length member? merge rassoc rehash reject select size slice to_a to_h to_hash to_proc to_s transform_keys transform_values value? values values_at].freeze 
- 
    POST_CONDITION_LOOP_TYPES =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 46%i[while_post until_post].freeze 
- 
    RESTRICT_ON_SEND =
    
 # File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 78ARRAY_METHODS + HASH_METHODS 
Instance Method Summary
- #on_send(node)
- #check_literal?(node, method, arguments) ⇒ Boolean private
- #enumerable_method?(method_name) ⇒ Boolean private
- #keyword_loop?(type) ⇒ Boolean private
- #literal_class(node) private
- #loop?(ancestor, node) ⇒ Boolean private
- #min_size private
- #node_within_enumerable_loop?(node, ancestor) ⇒ Boolean private
- #nonmutable_method_of_array_or_hash?(node, method) ⇒ Boolean private
- 
    
      #optimized_array_include?(node, method, arguments)  ⇒ Boolean 
    
    private
    Since Ruby 3.4, simple arguments to Array#include? are optimized. 
- #parent_is_loop?(node) ⇒ Boolean private
Instance Method Details
    #check_literal?(node, method, arguments)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 102
def check_literal?(node, method, arguments) !node.nil? && nonmutable_method_of_array_or_hash?(node, method) && node.children.size >= min_size && node.recursive_basic_literal? && !optimized_array_include?(node, method, arguments) end
    #enumerable_method?(method_name)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 161
def enumerable_method?(method_name) ENUMERABLE_METHOD_NAMES.include?(method_name) end
    #keyword_loop?(type)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 143
def keyword_loop?(type) LOOP_TYPES.include?(type) end
#literal_class(node) (private)
[ GitHub ]# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 153
def literal_class(node) if node.array_type? 'Array' elsif node.hash_type? 'Hash' end end
    #loop?(ancestor, node)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 139
def loop?(ancestor, node) keyword_loop?(ancestor.type) || kernel_loop?(ancestor) || node_within_enumerable_loop?(node, ancestor) end
#min_size (private)
[ GitHub ]# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 165
def min_size Integer(cop_config['MinSize'] || 1) end
    #node_within_enumerable_loop?(node, ancestor)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 147
def node_within_enumerable_loop?(node, ancestor) enumerable_loop?(ancestor) do |receiver| receiver != node && !receiver&.descendants&.include?(node) end end
    #nonmutable_method_of_array_or_hash?(node, method)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 130
def nonmutable_method_of_array_or_hash?(node, method) (node.array_type? && ARRAY_METHODS.include?(method)) || (node.hash_type? && HASH_METHODS.include?(method)) end
#on_send(node)
[ GitHub ]# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 92
def on_send(node) receiver, method, *arguments = *node.children return unless check_literal?(receiver, method, arguments) && parent_is_loop?(receiver) = format(MSG, literal_class: literal_class(receiver)) add_offense(receiver, message: ) end
    #optimized_array_include?(node, method, arguments)  ⇒ Boolean  (private)
  
Since Ruby 3.4, simple arguments to Array#include? are optimized. See https://github.com/ruby/ruby/pull/12123 for more details.
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 113
def optimized_array_include?(node, method, arguments) return false unless target_ruby_version >= 3.4 && node.array_type? && method == :include? # Disallow include?(1, 2) return false if arguments.count != 1 arg = arguments.first # Allow `include?(foo.bar.baz.bat)` while arg.send_type? return false if arg.arguments.any? # Disallow include?(foo(bar)) break unless arg.receiver arg = arg.receiver end ARRAY_INCLUDE_OPTIMIZED_TYPES.include?(arg.type) end
    #parent_is_loop?(node)  ⇒ Boolean  (private)
  
# File 'lib/rubocop/cop/performance/collection_literal_in_loop.rb', line 135
def parent_is_loop?(node) node.each_ancestor.any? { |ancestor| loop?(ancestor, node) } end