module Solargraph::Parser::ParserGem::NodeMethods::DeepInference
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.
Constants
- CASE_STATEMENT
- COMPOUND_STATEMENTS
- CONDITIONAL_ALL
- CONDITIONAL_ALL_BUT_FIRST
- FIRST_TWO_CHILDREN
- FUNCTION_VALUE
- ONLY_ONE_CHILD
- SKIPPABLE
Public Class Methods
Source
# File lib/solargraph/parser/parser_gem/node_methods.rb, line 321 def from_method_body node from_value_position_statement(node, include_explicit_returns: true) end
@param node [AST::Node] a method body compound statement @param include_explicit_returns [Boolean] If true,
include the value nodes of the parameter of the 'return' statements in the type returned.
@return [Array<AST::Node>] low-level value nodes from
both nodes in value position as well as explicit return statements in the method's closure.
Source
# File lib/solargraph/parser/parser_gem/node_methods.rb, line 398 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
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.
@param parent [Parser::AST::Node]
@return [Array<Parser::AST::Node>]
Source
# File lib/solargraph/parser/parser_gem/node_methods.rb, line 343 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
Look at known control statements and use them to find more specific return nodes.
@param node [Parser::AST::Node] Statement which is in
value position for a method body
@param include_explicit_returns [Boolean] If true,
include the value nodes of the parameter of the 'return' statements in the type returned.
@return [Array<Parser::AST::Node>]
Source
# File lib/solargraph/parser/parser_gem/node_methods.rb, line 330 def value_position_nodes_only(node) from_value_position_statement(node, include_explicit_returns: false) end
@param node [AST::Node] an individual statement, to be
evaluated outside the context of a containing method
@return [Array<AST::Node>] low-level value nodes in
value position. Does not include explicit return statements
Private Class Methods
Source
# File lib/solargraph/parser/parser_gem/node_methods.rb, line 438 def explicit_return_values_from_compound_statement parent return [] unless parent.is_a?(::Parser::AST::Node) result = [] nodes = parent.children.select{|n| n.is_a?(::Parser::AST::Node)} nodes.each do |node| next if SKIPPABLE.include?(node.type) if node.type == :return result.concat reduce_to_value_nodes([node.children[0]]) # Return the result here because the rest of the code is # unreachable return result else result.concat explicit_return_values_from_compound_statement(node) end end result end
Useful when this statement isn’t in value position, but we care explicit return statements nonetheless.
@param parent [Parser::AST::Node]
@return [Array<Parser::AST::Node>]
Source
# File lib/solargraph/parser/parser_gem/node_methods.rb, line 458 def reduce_to_value_nodes nodes result = [] nodes.each do |node| if !node.is_a?(::Parser::AST::Node) result.push nil elsif 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]) elsif node.type == :return result.concat reduce_to_value_nodes([node.children[0]]) elsif node.type == :or result.concat reduce_to_value_nodes(node.children) elsif node.type == :block result.concat explicit_return_values_from_compound_statement(node.children[2]) elsif node.type == :resbody result.concat reduce_to_value_nodes([node.children[2]]) else result.push node end end result end
@param nodes [Enumerable<Parser::AST::Node, BasicObject>] @return [Array<Parser::AST::Node, nil>]