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
-
.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.
-
.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.
-
.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.
-
.value_position_nodes_only(node) ⇒ Array<AST::Node>
Low-level value nodes in value position.
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.
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.
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.
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.
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 |