class Solargraph::Parser::ParserGem::NodeChainer


A factory for generating chains from nodes.

def chain

Returns:
  • (Source::Chain) -
def chain
  links = generate_links(@node)
  Chain.new(links, @node, (Parser.is_ast_node?(@node) && @node.type == :splat))
end

def chain node, filename = nil, parent = nil

Returns:
  • (Source::Chain) -

Parameters:
  • parent (Parser::AST::Node, nil) --
  • filename (String, nil) --
  • node (Parser::AST::Node) --
def chain node, filename = nil, parent = nil
  NodeChainer.new(node, filename, parent).chain
end

def generate_links n

Returns:
  • (Array) -

Parameters:
  • n (Parser::AST::Node) --
def generate_links n
  return [] unless n.is_a?(::Parser::AST::Node)
  return generate_links(n.children[0]) if n.type == :splat
  # @type [Array<Chain::Link>]

  result = []
  if n.type == :block
    result.concat NodeChainer.chain(n.children[0], @filename, n).links
  elsif n.type == :send
    if n.children[0].is_a?(::Parser::AST::Node)
      result.concat generate_links(n.children[0])
      result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
    elsif n.children[0].nil?
      args = []
      n.children[2..-1].each do |c|
        args.push NodeChainer.chain(c, @filename, n)
      end
      result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
    else
      raise "No idea what to do with #{n}"
    end
  elsif n.type == :csend
    if n.children[0].is_a?(::Parser::AST::Node)
      result.concat generate_links(n.children[0])
      result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
    elsif n.children[0].nil?
      result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
    else
      raise "No idea what to do with #{n}"
    end
  elsif n.type == :self
    result.push Chain::Head.new('self')
  elsif n.type == :zsuper
    result.push Chain::ZSuper.new('super')
  elsif n.type == :super
    args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
    result.push Chain::Call.new('super', args)
  elsif n.type == :yield
    args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
    result.push Chain::Call.new('yield', args)
  elsif n.type == :const
    const = unpack_name(n)
    result.push Chain::Constant.new(const)
  elsif [:lvasgn, :ivasgn, :gvasgn, :cvasgn].include?(n.type)
    result.concat generate_links(n.children[1])
  elsif n.type == :lvar
    result.push Chain::Call.new(n.children[0].to_s)
  elsif n.type == :ivar
     result.push Chain::InstanceVariable.new(n.children[0].to_s)
  elsif n.type == :cvar
     result.push Chain::ClassVariable.new(n.children[0].to_s)
  elsif n.type == :gvar
     result.push Chain::GlobalVariable.new(n.children[0].to_s)
  elsif n.type == :or_asgn
    # @todo: Need a new Link class here that evaluates the

    #   existing variable type with the RHS, and generates a

    #   union type of the LHS alone if never nil, or minus nil +

    #   RHS if it is nilable.

    result.concat generate_links n.children[1]
  elsif [:class, :module, :def, :defs].include?(n.type)
    # @todo Undefined or what?

    result.push Chain::UNDEFINED_CALL
  elsif n.type == :and
    result.concat generate_links(n.children.last)
  elsif n.type == :or
    result.push Chain::Or.new([NodeChainer.chain(n.children[0], @filename), NodeChainer.chain(n.children[1], @filename, n)])
  elsif n.type == :if
    result.push Chain::If.new([NodeChainer.chain(n.children[1], @filename), NodeChainer.chain(n.children[2], @filename, n)])
  elsif [:begin, :kwbegin].include?(n.type)
    result.concat generate_links(n.children.last)
  elsif n.type == :block_pass
    block_variable_name_node = n.children[0]
    if block_variable_name_node.nil?
      # anonymous block forwarding (e.g., "&")

      # added in Ruby 3.1 - https://bugs.ruby-lang.org/issues/11256

      result.push Chain::BlockVariable.new(nil)
    else
      if block_variable_name_node.type == :sym
        result.push Chain::BlockSymbol.new("#{block_variable_name_node.children[0].to_s}")
      else
        result.push Chain::BlockVariable.new("&#{block_variable_name_node.children[0].to_s}")
      end
    end
  elsif n.type == :hash
    result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
  elsif n.type == :array
    chained_children = n.children.map { |c| NodeChainer.chain(c) }
    result.push Source::Chain::Array.new(chained_children)
  else
    lit = infer_literal_node_type(n)
    result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
  end
  result
end

def hash_is_splatted? node

Parameters:
  • node (Parser::AST::Node) --
def hash_is_splatted? node
  return false unless Parser.is_ast_node?(node) && node.type == :hash
  return false unless Parser.is_ast_node?(node.children.last) && node.children.last.type == :kwsplat
  return false if Parser.is_ast_node?(node.children.last.children[0]) && node.children.last.children[0].type == :hash
  true
end

def initialize node, filename = nil, parent = nil

Parameters:
  • parent (Parser::AST::Node, nil) --
  • filename (String, nil) --
  • node (Parser::AST::Node) --
def initialize node, filename = nil, parent = nil
  @node = node
  @filename = filename
  @parent = parent
end

def load_string(code)

Returns:
  • (Source::Chain) -

Parameters:
  • code (String) --
def load_string(code)
  node = Parser.parse(code.sub(/\.$/, ''))
  chain = NodeChainer.new(node).chain
  chain.links.push(Chain::Link.new) if code.end_with?('.')
  chain
end

def node_args node

Returns:
  • (Array) -

Parameters:
  • node (Parser::AST::Node) --
def node_args node
  node.children[2..-1].map do |child|
    NodeChainer.chain(child, @filename, node)
  end
end

def passed_block node

Returns:
  • (Source::Chain, nil) -

Parameters:
  • node (Parser::AST::Node) --
def passed_block node
  return unless node == @node && @parent&.type == :block
  NodeChainer.chain(@parent.children[2], @filename)
end