class Solargraph::Source::Chain


expression.
Computes Pins and/or ComplexTypes representing the interpreted
each represented as a Link object.
tree level, made up of constants, variables, and method calls,
Represents an expression as a single call chain at the parse

def base

Returns:
  • (Chain) -
def base
  @base ||= Chain.new(links[0..-2])
end

def constant?

Returns:
  • (Boolean) -
def constant?
  links.last.is_a?(Chain::Constant)
end

def define api_map, name_pin, locals

Returns:
  • (::Array) - Pins representing possible return

Parameters:
  • locals (::Enumerable) -- Any local
  • name_pin (Pin::Closure) -- the surrounding closure pin for
  • api_map (ApiMap) --
def define api_map, name_pin, locals
  return [] if undefined?
  # working_pin is the surrounding closure pin for the link

  # being processed, whose #binder method will provide the LHS /

  # 'self type' of the next link (same as the  #return_type method

  # --the type of the result so far).

  #

  # @todo ProxyType uses 'type' for the binder, but '

  working_pin = name_pin
  links[0..-2].each do |link|
    pins = link.resolve(api_map, working_pin, locals)
    type = infer_first_defined(pins, working_pin, api_map, locals)
    return [] if type.undefined?
    working_pin = Pin::ProxyType.anonymous(type)
  end
  links.last.last_context = working_pin
  links.last.resolve(api_map, working_pin, locals)
end

def defined?

def defined?
  !undefined?
end

def infer api_map, name_pin, locals

Returns:
  • (ComplexType) -

Parameters:
  • locals (::Enumerable) --
  • name_pin (Pin::Base) -- The pin for the closure in which this code runs
  • api_map (ApiMap) --
def infer api_map, name_pin, locals
  out = nil
  cached = @@inference_cache[[node, node.location, links.map(&:word), name_pin&.return_type, locals]] unless node.nil?
  return cached if cached && @@inference_invalidation_key == api_map.hash
  out = infer_uncached api_map, name_pin, locals
  if @@inference_invalidation_key != api_map.hash
    @@inference_cache = {}
    @@inference_invalidation_key = api_map.hash
  end
  @@inference_cache[[node, node.location, links.map(&:word), name_pin&.return_type, locals]] = out unless node.nil?
  out
end

def infer_first_defined pins, context, api_map, locals

Returns:
  • (ComplexType) -

Parameters:
  • locals (::Enumerable) --
  • api_map (ApiMap) --
  • context (Pin::Base) --
  • pins (::Array) --
def infer_first_defined pins, context, api_map, locals
  possibles = []
  # @todo this param tag shouldn't be needed to probe the type

  # @todo ...but given it is needed, typecheck should complain that it is needed

  # @param pin [Pin::Base]

  pins.each do |pin|
    # Avoid infinite recursion

    next if @@inference_stack.include?(pin.identity)
    @@inference_stack.push pin.identity
    type = pin.typify(api_map)
    @@inference_stack.pop
    if type.defined?
      if type.generic?
        # @todo even at strong, no typechecking complaint

        #   happens when a [Pin::Base,nil] is passed into a method

        #   that accepts only [Pin::Namespace] as an argument

        type = type.resolve_generics(pin.closure, context.binder)
      end
      if type.defined?
        possibles.push type
        break if pin.is_a?(Pin::Method)
      end
    end
  end
  if possibles.empty?
    # Limit method inference recursion

    return ComplexType::UNDEFINED if @@inference_depth >= 10 && pins.first.is_a?(Pin::Method)
    @@inference_depth += 1
    # @param pin [Pin::Base]

    pins.each do |pin|
      # Avoid infinite recursion

      next if @@inference_stack.include?(pin.identity)
      @@inference_stack.push pin.identity
      type = pin.probe(api_map)
      @@inference_stack.pop
      if type.defined?
        possibles.push type
        break if pin.is_a?(Pin::Method)
      end
    end
    @@inference_depth -= 1
  end
  return ComplexType::UNDEFINED if possibles.empty?
  type = if possibles.length > 1
    # Move nil to the end by convention

    sorted = possibles.sort { |a, _| a.tag == 'nil' ? 1 : 0 }
    ComplexType.new(sorted.uniq)
  else
    ComplexType.new(possibles)
  end
  return type if context.nil? || context.return_type.undefined?
  type.self_to_type(context.return_type)
end

def infer_uncached api_map, name_pin, locals

Returns:
  • (ComplexType) -

Parameters:
  • locals (::Enumerable) --
  • name_pin (Pin::Base) --
  • api_map (ApiMap) --
def infer_uncached api_map, name_pin, locals
  pins = define(api_map, name_pin, locals)
  type = infer_first_defined(pins, links.last.last_context, api_map, locals)
  maybe_nil(type)
end

def initialize links, node = nil, splat = false

Parameters:
  • splat (Boolean) --
  • links (::Array) --
  • node (Parser::AST::Node, nil) --
def initialize links, node = nil, splat = false
  @links = links.clone
  @links.push UNDEFINED_CALL if @links.empty?
  head = true
  @links.map! do |link|
    result = (head ? link.clone_head : link.clone_body)
    head = false
    result
  end
  @node = node
  @splat = splat
end

def literal?

Returns:
  • (Boolean) -
def literal?
  links.last.is_a?(Chain::Literal)
end

def maybe_nil type

Returns:
  • (ComplexType) -

Parameters:
  • type (ComplexType) --
def maybe_nil type
  return type if type.undefined? || type.void? || type.nullable?
  return type unless nullable?
  ComplexType.new(type.items + [ComplexType::NIL])
end

def nullable?

def nullable?
  links.any?(&:nullable?)
end

def splat?

def splat?
  @splat
end

def undefined?

def undefined?
  links.any?(&:undefined?)
end