Module: Solargraph::Parser::ParserGem::NodeMethods::DeepInference

Defined in:
lib/solargraph/parser/parser_gem/node_methods.rb

Overview

Concepts:

- statement - one single node in the AST.  Generally used
  synonymously with how the Parser gem uses the term
  'expression'.  This may have side effects (e.g.,
  registering a method in the namespace, modifying
  variables or doing I/O).  It may encapsulate multiple
  other statements (see compound statement).

- value - something that can be assigned to a variable by
  evaluating a statement

- value node - the 'lowest level' AST node whose return
  type is a subset of the value type of the overall
  statement.  Might be a literal, a method call, etc - the
  goal is to find the lowest level node, which we can use
  Chains and Pins later on to determine the type of.

  e.g., if the node 'b ? 123 : 456' were a return value, we
  know the actual return values possible are 123 and 456,
  and can disregard the rest.

- value type - the type representing the multiple possible
  values that can result from evaluation of the statement.

- return type - the type describing the values a statement
  might evaluate to.  When used with a method, the term
  describes the values that may result from the method
  being called, and includes explicit return statements
  within the method body's closure.

- method body - a compound statement with parameters whose
  return value type must account both for the explicit
  'return' statemnts as well as the final statements
  executed in any given control flow through the method.

- explicit return statement - a statement which, when part of a
   method body, is a possible value of a call to that method -
   e.g., "return 123"

- compound statement - a statement which can be expanded to
   be multiple statements in a row, executed in the context
   of a method which can be explicitly returned from.

- value position - the positions in the AST where the
  return type of the statement would be one of the return
  types of any compound statements it is a part of.  For a
  compound statement, the last of the child statements
  would be in return position.  This concept can be applied
  recursively through e.g. conditionals to find a list of
  statements in value positions.

Constant Summary collapse

CONDITIONAL_ALL_BUT_FIRST =
[:if, :unless]
CONDITIONAL_ALL =
[:or]
ONLY_ONE_CHILD =
[:return]
FIRST_TWO_CHILDREN =
[:rescue]
COMPOUND_STATEMENTS =
[:begin, :kwbegin]
SKIPPABLE =
[:def, :defs, :class, :sclass, :module]
FUNCTION_VALUE =
[:block]
CASE_STATEMENT =
[:case]

Class Method Summary collapse

Class Method Details

.from_method_body(node) ⇒ Array<AST::Node>

Low-level value nodes from both nodes in value position as well as explicit return statements in the method’s closure.

Parameters:

  • node (AST::Node)

    a method body compound statement

  • include_explicit_returns (Boolean)

    If true, include the value nodes of the parameter of the ‘return’ statements in the type returned.

Returns:

  • (Array<AST::Node>)

    low-level value nodes from both nodes in value position as well as explicit return statements in the method’s closure.



332
333
334
# File 'lib/solargraph/parser/parser_gem/node_methods.rb', line 332

def from_method_body node
  from_value_position_statement(node, include_explicit_returns: true)
end

.from_value_position_compound_statement(parent) ⇒ Array<Parser::AST::Node>

Treat parent as as a begin block and use the last node’s return node plus any explicit return nodes’ return nodes. e.g.,

  123
  456
  return 'a' if foo == bar
  789

would return 'a' and 789.

Parameters:

  • parent (Parser::AST::Node)

Returns:

  • (Array<Parser::AST::Node>)


409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
# File 'lib/solargraph/parser/parser_gem/node_methods.rb', line 409

def from_value_position_compound_statement parent
  result = []
  nodes = parent.children.select{|n| n.is_a?(AST::Node)}
  nodes.each_with_index do |node, idx|
    if node.type == :block
      result.concat explicit_return_values_from_compound_statement(node.children[2])
    elsif node.type == :rescue
      # body statements
      result.concat from_value_position_statement(node.children[0])
      # rescue statements
      result.concat from_value_position_statement(node.children[1])
    elsif SKIPPABLE.include?(node.type)
      next
    elsif node.type == :resbody
      result.concat reduce_to_value_nodes([node.children[2]])
    elsif node.type == :return
      result.concat reduce_to_value_nodes([node.children[0]])
      # Return here because the rest of the code is
      # unreachable and shouldn't be looked at
      return result
    else
      result.concat explicit_return_values_from_compound_statement(node)
    end
    # handle last line of compound statements, which is in
    # value position.  we already have the explicit values
    # from above; now we need to also gather the value
    # position nodes
    result.concat from_value_position_statement(nodes.last, include_explicit_returns: false) if idx == nodes.length - 1
  end
  result
end

.from_value_position_statement(node, include_explicit_returns: true) ⇒ Array<Parser::AST::Node>

Look at known control statements and use them to find more specific return nodes.

Parameters:

  • node (Parser::AST::Node)

    Statement which is in value position for a method body

  • include_explicit_returns (Boolean) (defaults to: true)

    If true, include the value nodes of the parameter of the ‘return’ statements in the type returned.

Returns:

  • (Array<Parser::AST::Node>)


354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
# File 'lib/solargraph/parser/parser_gem/node_methods.rb', line 354

def from_value_position_statement node, include_explicit_returns: true
  # STDERR.puts("from_expression called on #{node.inspect}")
  return [] unless node.is_a?(::Parser::AST::Node)
  # @type [Array<Parser::AST::Node>]
  result = []
  if COMPOUND_STATEMENTS.include?(node.type)
    result.concat from_value_position_compound_statement node
  elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type)
    result.concat reduce_to_value_nodes(node.children[1..-1])
    # result.push NIL_NODE unless node.children[2]
  elsif CONDITIONAL_ALL.include?(node.type)
    result.concat reduce_to_value_nodes(node.children)
  elsif ONLY_ONE_CHILD.include?(node.type)
    result.concat reduce_to_value_nodes([node.children[0]])
  elsif FIRST_TWO_CHILDREN.include?(node.type)
    result.concat reduce_to_value_nodes([node.children[0], node.children[1]])
  elsif FUNCTION_VALUE.include?(node.type)
    # the block itself is a first class value that could be returned
    result.push node
    # @todo any explicit returns actually return from
    #   scope in which the proc is run.  This asssumes
    #   that the function is executed here.
    result.concat explicit_return_values_from_compound_statement(node.children[2]) if include_explicit_returns
  elsif CASE_STATEMENT.include?(node.type)
    node.children[1..-1].each do |cc|
      if cc.nil?
        result.push NIL_NODE
      elsif cc.type == :when
        result.concat reduce_to_value_nodes([cc.children.last])
      else
        # else clause in case
        result.concat reduce_to_value_nodes([cc])
      end
    end
  elsif node.type == :resbody
    result.concat reduce_to_value_nodes([node.children[2]])
  else
    result.push node
  end
  result
end

.value_position_nodes_only(node) ⇒ Array<AST::Node>

Returns low-level value nodes in value position. Does not include explicit return statements.

Parameters:

  • node (AST::Node)

    an individual statement, to be evaluated outside the context of a containing method

Returns:

  • (Array<AST::Node>)

    low-level value nodes in value position. Does not include explicit return statements



341
342
343
# File 'lib/solargraph/parser/parser_gem/node_methods.rb', line 341

def value_position_nodes_only(node)
  from_value_position_statement(node, include_explicit_returns: false)
end