123456789_123456789_123456789_123456789_123456789_

Class: RuboCop::Cop::InternalAffairs::NodePatternGroups::ASTWalker

Relationships & Source Files
Namespace Children
Classes:
Inherits: Object
Defined in: lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb

Overview

Walks an AST that has been processed by InternalAffairs::NodePatternGroups::Processor in order to find node_type and node_sequence nodes that can be replaced with a node group in InternalAffairs/NodePatternGroups.

Calling #walk sets #node_groups with an array of NodeGroup structs that contain metadata about nodes that can be replaced, including location data. That metadata is used by the cop to register offenses and perform corrections.

Class Method Summary

Instance Attribute Summary

Instance Method Summary

Constructor Details

.newASTWalker

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 30

def initialize
  reset!
end

Instance Attribute Details

#node_groups (readonly)

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 38

attr_reader :node_groups

Instance Method Details

#each_child_node(node, *types) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 75

def each_child_node(node, *types)
  return to_enum(__method__, node, *types) unless block_given?

  node.children.each do |child|
    yield child if types.empty? || types.include?(child.type)
  end

  self
end

#each_node_group(types_to_check) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 85

def each_node_group(types_to_check)
  # Find all node groups where all of the members are present in the union
  type_names = types_to_check.map(&:child)

  NODE_GROUPS.select { |_, group| group & type_names == group }.each_key do |name|
    nodes = get_relevant_nodes(types_to_check, name)

    yield name, nodes
  end
end

#get_relevant_nodes(node_types, group_name) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 96

def get_relevant_nodes(node_types, group_name)
  node_types.each_with_object([]) do |node_type, arr|
    next unless NODE_GROUPS[group_name].include?(node_type.child)

    arr << node_type
  end
end

#node_group_data(name, union, node_types, start_index, other) (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 104

def node_group_data(name, union, node_types, start_index, other)
  NodeGroup.new(
    name: name,
    union: union,
    node_types: node_types,
    sequence?: node_types.first.type == :node_sequence,
    start_index: start_index,
    pipe: union.source_range.source['|'],
    other_elements?: other
  )
end

#on_union(node)

Search union nodes for node_type and node_sequence nodes that can be collapsed into a node group. * node_type nodes are nodes with no further configuration (ie. send) * node_sequence nodes are nodes with further configuration (ie. (send …​))

Each group of types that can be collapsed will have a ASTWalker::NodeGroup record added to #node_groups, which is then used by the cop.

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 59

def on_union(node)
  all_node_types = each_child_node(node, :node_type, :node_sequence).to_a

  each_node_group(all_node_types) do |group_name, node_types|
    next unless sequences_match?(node_types)

    node_groups << node_group_data(
      group_name, node, node_types,
      all_node_types.index(node_types.first),
      (node.children - node_types).any?
    )
  end
end

#reset!

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 34

def reset!
  @node_groups = []
end

#sequences_match?(types) ⇒ Boolean (private)

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 116

def sequences_match?(types)
  # Ensure all given types have the same type and the same sequence
  # ie. `(send ...)` and `(csend ...) is a match
  #     `(send)` and `(csend ...)` is not a match
  #     `send` and `(csend ...)` is not a match

  types.each_cons(2).all? do |left, right|
    left.type == right.type && left.children[1..] == right.children[1..]
  end
end

#walk(node)

Recursively walk the AST in a depth-first manner. Only union nodes are handled further.

[ GitHub ]

  
# File 'lib/rubocop/cop/internal_affairs/node_pattern_groups/ast_walker.rb', line 42

def walk(node)
  return if node.nil?

  on_union(node) if node.type == :union

  node.child_nodes.each do |child|
    walk(child)
  end
end