Class: Solargraph::Parser::FlowSensitiveTyping

Inherits:
Object
  • Object
show all
Extended by:
Logging
Includes:
Logging, NodeMethods
Defined in:
lib/solargraph/parser/flow_sensitive_typing.rb

Constant Summary

Constants included from Logging

Logging::DEFAULT_LOG_LEVEL, Logging::LOG_LEVELS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Logging

logger

Methods included from NodeMethods

any_splatted_call?, call_nodes_from, const_nodes_from, convert_hash, find_recipient_node, get_node_end_position, get_node_start_position, process, returns_from_method_body, unpack_name, value_position_nodes_only

Constructor Details

#initialize(locals, enclosing_breakable_pin = nil) ⇒ FlowSensitiveTyping

Returns a new instance of FlowSensitiveTyping.

Parameters:



8
9
10
11
# File 'lib/solargraph/parser/flow_sensitive_typing.rb', line 8

def initialize(locals, enclosing_breakable_pin = nil)
  @locals = locals
  @enclosing_breakable_pin = enclosing_breakable_pin
end

Class Method Details

.visible_pins(pins, name, closure, location) ⇒ Array<Pin::LocalVariable>

Find a variable pin by name and where it is used.

Resolves our most specific view of this variable’s type by preferring pins created by flow-sensitive typing when we have them based on the Closure and Location.

Parameters:

Returns:



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/solargraph/parser/flow_sensitive_typing.rb', line 90

def self.visible_pins(pins, name, closure, location)
  logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" }
  pins_with_name = pins.select { |p| p.name == name }
  if pins_with_name.empty?
    logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => [] - no pins with name" }
    return []
  end
  pins_with_specific_visibility = pins.select { |p| p.name == name && p.presence && p.visible_at?(closure, location) }
  if pins_with_specific_visibility.empty?
    logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_name} - no pins with specific visibility" }
    return pins_with_name
  end
  visible_pins_specific_to_this_closure = pins_with_specific_visibility.select { |p| p.closure == closure }
  if visible_pins_specific_to_this_closure.empty?
    logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_specific_visibility} - no visible pins specific to this closure (#{closure})}" }
    return pins_with_specific_visibility
  end
  flow_defined_pins = pins_with_specific_visibility.select { |p| p.presence_certain? }
  if flow_defined_pins.empty?
    logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{visible_pins_specific_to_this_closure} - no flow-defined pins" }
    return visible_pins_specific_to_this_closure
  end

  logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{flow_defined_pins}" }

  flow_defined_pins
end

Instance Method Details

#process_and(and_node, true_ranges = []) ⇒ void

This method returns an undefined value.

Parameters:

  • and_node (Parser::AST::Node)
  • true_ranges (Array<Range>) (defaults to: [])


17
18
19
20
21
22
23
24
25
26
27
28
29
# File 'lib/solargraph/parser/flow_sensitive_typing.rb', line 17

def process_and(and_node, true_ranges = [])
  # @type [Parser::AST::Node]
  lhs = and_node.children[0]
  # @type [Parser::AST::Node]
  rhs = and_node.children[1]

  before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
  before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)

  rhs_presence = Range.new(before_rhs_pos,
                           get_node_end_position(rhs))
  process_isa(lhs, true_ranges + [rhs_presence])
end

#process_if(if_node) ⇒ void

This method returns an undefined value.

Parameters:

  • if_node (Parser::AST::Node)


34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/solargraph/parser/flow_sensitive_typing.rb', line 34

def process_if(if_node)
  #
  # See if we can refine a type based on the result of 'if foo.nil?'
  #
  # [3] pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("if foo.is_a? Baz; then foo; else bar; end")
  # => s(:if,
  #   s(:send,
  #     s(:send, nil, :foo), :is_a?,
  #     s(:const, nil, :Baz)),
  #   s(:send, nil, :foo),
  #   s(:send, nil, :bar))
  # [4] pry(main)>
  conditional_node = if_node.children[0]
  # @type [Parser::AST::Node]
  then_clause = if_node.children[1]
  # @type [Parser::AST::Node]
  else_clause = if_node.children[2]

  true_ranges = []
  if always_breaks?(else_clause)
    unless enclosing_breakable_pin.nil?
      rest_of_breakable_body = Range.new(get_node_end_position(if_node),
                                         get_node_end_position(enclosing_breakable_pin.node))
      true_ranges << rest_of_breakable_body
    end
  end

  unless then_clause.nil?
    #
    # Add specialized locals for the then clause range
    #
    before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
    before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
    true_ranges << Range.new(before_then_clause_pos,
                             get_node_end_position(then_clause))
  end

  process_conditional(conditional_node, true_ranges)
end