Class: Steep::Typing

Inherits:
Object
  • Object
show all
Defined in:
lib/steep/typing.rb

Defined Under Namespace

Classes: UnknownNodeError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(source:, root_context:, parent: nil, parent_last_update: parent&.last_update, contexts: nil) ⇒ Typing



24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'lib/steep/typing.rb', line 24

def initialize(source:, root_context:, parent: nil, parent_last_update: parent&.last_update, contexts: nil)
  @source = source

  @parent = parent
  @parent_last_update = parent_last_update
  @last_update = parent&.last_update || 0
  @should_update = false

  @errors = []
  @typing = {}.compare_by_identity
  @root_context = root_context
  @contexts = contexts || TypeInference::ContextArray.from_source(source: source)
end

Instance Attribute Details

#contextsObject (readonly)

Returns the value of attribute contexts.



21
22
23
# File 'lib/steep/typing.rb', line 21

def contexts
  @contexts
end

#errorsObject (readonly)

Returns the value of attribute errors.



15
16
17
# File 'lib/steep/typing.rb', line 15

def errors
  @errors
end

#last_updateObject (readonly)

Returns the value of attribute last_update.



19
20
21
# File 'lib/steep/typing.rb', line 19

def last_update
  @last_update
end

#parentObject (readonly)

Returns the value of attribute parent.



17
18
19
# File 'lib/steep/typing.rb', line 17

def parent
  @parent
end

#parent_last_updateObject (readonly)

Returns the value of attribute parent_last_update.



18
19
20
# File 'lib/steep/typing.rb', line 18

def parent_last_update
  @parent_last_update
end

#root_contextObject (readonly)

Returns the value of attribute root_context.



22
23
24
# File 'lib/steep/typing.rb', line 22

def root_context
  @root_context
end

#should_updateObject (readonly)

Returns the value of attribute should_update.



20
21
22
# File 'lib/steep/typing.rb', line 20

def should_update
  @should_update
end

#sourceObject (readonly)

Returns the value of attribute source.



14
15
16
# File 'lib/steep/typing.rb', line 14

def source
  @source
end

#typingObject (readonly)

Returns the value of attribute typing.



16
17
18
# File 'lib/steep/typing.rb', line 16

def typing
  @typing
end

Class Method Details

.summary(node) ⇒ Object



145
146
147
148
149
150
151
# File 'lib/steep/typing.rb', line 145

def self.summary(node)
  src = node.loc.expression.source.split(/\n/).first
  line = node.loc.first_line
  col = node.loc.column

  "#{line}:#{col}:#{src}"
end

Instance Method Details

#add_context(range, context:) ⇒ Object



49
50
51
52
# File 'lib/steep/typing.rb', line 49

def add_context(range, context:)
  contexts.insert_context(range, context: context)
  @last_update += 1
end

#add_context_for_body(node, context:) ⇒ Object



79
80
81
82
83
84
85
86
87
88
89
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
117
118
119
120
121
122
123
124
125
126
# File 'lib/steep/typing.rb', line 79

def add_context_for_body(node, context:)
  case node.type
  when :class
    name_node, super_node, _ = node.children
    begin_pos = if super_node
                  super_node.loc.expression.end_pos
                else
                  name_node.loc.expression.end_pos
                end
    end_pos = node.loc.end.begin_pos

    add_context(begin_pos..end_pos, context: context)

  when :module
    name_node = node.children[0]
    begin_pos = name_node.loc.expression.end_pos
    end_pos = node.loc.end.begin_pos
    add_context(begin_pos..end_pos, context: context)

  when :def, :defs
    args_node = case node.type
                when :def
                  node.children[1]
                when :defs
                  node.children[2]
                end
    body_begin_pos = if args_node.loc.expression
                       args_node.loc.expression.end_pos
                     else
                       node.loc.name.end_pos
                     end
    body_end_pos = node.loc.end.begin_pos
    add_context(body_begin_pos..body_end_pos, context: context)

  when :block
    send_node, args_node, _ = node.children
    begin_pos = if send_node.type != :lambda && args_node.loc.expression
                  args_node.loc.expression.end_pos
                else
                  node.loc.begin.end_pos
                end
    end_pos = node.loc.end.begin_pos
    add_context(begin_pos..end_pos, context: context)

  else
    raise "Unexpected node for insert_context: #{node.type}"
  end
end

#add_context_for_node(node, context:) ⇒ Object



72
73
74
75
76
77
# File 'lib/steep/typing.rb', line 72

def add_context_for_node(node, context:)
  begin_pos = node.loc.expression.begin_pos
  end_pos = node.loc.expression.end_pos

  add_context(begin_pos..end_pos, context: context)
end

#add_error(error) ⇒ Object



38
39
40
# File 'lib/steep/typing.rb', line 38

def add_error(error)
  errors << error
end

#add_typing(node, type, _context) ⇒ Object



42
43
44
45
46
47
# File 'lib/steep/typing.rb', line 42

def add_typing(node, type, _context)
  typing[node] = type
  @last_update += 1

  type
end

#context_at(line:, column:) ⇒ Object



128
129
130
131
# File 'lib/steep/typing.rb', line 128

def context_at(line:, column:)
  contexts.at(line: line, column: column) ||
    (parent ? parent.context_at(line: line, column: column) : root_context)
end

#dump(io) ⇒ Object



133
134
135
136
137
138
139
140
141
142
143
# File 'lib/steep/typing.rb', line 133

def dump(io)
  io.puts "Typing: "
  nodes.each_value do |node|
    io.puts "  #{Typing.summary(node)} => #{type_of(node: node).inspect}"
  end

  io.puts "Errors: "
  errors.each do |error|
    io.puts "  #{Typing.summary(error.node)} => #{error.inspect}"
  end
end

#each_typing(&block) ⇒ Object



167
168
169
# File 'lib/steep/typing.rb', line 167

def each_typing(&block)
  typing.each(&block)
end

#has_type?(node) ⇒ Boolean



54
55
56
# File 'lib/steep/typing.rb', line 54

def has_type?(node)
  typing.key?(node)
end

#new_child(range) ⇒ Object



153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/steep/typing.rb', line 153

def new_child(range)
  child = self.class.new(source: source,
                         parent: self,
                         root_context: root_context,
                         contexts: TypeInference::ContextArray.new(buffer: contexts.buffer, range: range, context: nil))
  @should_update = true

  if block_given?
    yield child
  else
    child
  end
end

#save!Object



171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/steep/typing.rb', line 171

def save!
  raise "Unexpected save!" unless parent
  raise "Parent modified since #new_child: parent.last_update=#{parent.last_update}, parent_last_update=#{parent_last_update}" unless parent.last_update == parent_last_update

  each_typing do |node, type|
    parent.add_typing(node, type, nil)
  end

  parent.contexts.merge(contexts)

  errors.each do |error|
    parent.add_error error
  end
end

#type_of(node:) ⇒ Object



58
59
60
61
62
63
64
65
66
67
68
69
70
# File 'lib/steep/typing.rb', line 58

def type_of(node:)
  type = typing[node]

  if type
    type
  else
    if parent
      parent.type_of(node: node)
    else
      raise UnknownNodeError.new(:type, node: node)
    end
  end
end