solargraph 0.54.1 → 0.55.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +39 -0
  3. data/lib/solargraph/api_map/cache.rb +10 -1
  4. data/lib/solargraph/api_map/index.rb +167 -0
  5. data/lib/solargraph/api_map/store.rb +71 -122
  6. data/lib/solargraph/api_map.rb +97 -36
  7. data/lib/solargraph/bench.rb +17 -1
  8. data/lib/solargraph/complex_type/type_methods.rb +7 -0
  9. data/lib/solargraph/complex_type/unique_type.rb +109 -8
  10. data/lib/solargraph/complex_type.rb +48 -18
  11. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +51 -0
  12. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  13. data/lib/solargraph/convention/struct_definition.rb +101 -0
  14. data/lib/solargraph/convention.rb +2 -0
  15. data/lib/solargraph/doc_map.rb +43 -18
  16. data/lib/solargraph/equality.rb +33 -0
  17. data/lib/solargraph/language_server/host/message_worker.rb +31 -11
  18. data/lib/solargraph/language_server/host.rb +13 -11
  19. data/lib/solargraph/language_server/message/base.rb +19 -12
  20. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  21. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  22. data/lib/solargraph/language_server/message/text_document/formatting.rb +4 -0
  23. data/lib/solargraph/library.rb +7 -8
  24. data/lib/solargraph/location.rb +15 -0
  25. data/lib/solargraph/parser/comment_ripper.rb +11 -6
  26. data/lib/solargraph/parser/flow_sensitive_typing.rb +226 -0
  27. data/lib/solargraph/parser/node_methods.rb +15 -1
  28. data/lib/solargraph/parser/parser_gem/class_methods.rb +11 -6
  29. data/lib/solargraph/parser/parser_gem/node_chainer.rb +17 -23
  30. data/lib/solargraph/parser/parser_gem/node_methods.rb +5 -3
  31. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  32. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +21 -1
  33. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  34. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -2
  35. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +26 -5
  36. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  37. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +28 -0
  38. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +28 -0
  39. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  40. data/lib/solargraph/parser.rb +1 -0
  41. data/lib/solargraph/pin/base.rb +35 -19
  42. data/lib/solargraph/pin/base_variable.rb +10 -3
  43. data/lib/solargraph/pin/block.rb +3 -3
  44. data/lib/solargraph/pin/breakable.rb +9 -0
  45. data/lib/solargraph/pin/callable.rb +3 -3
  46. data/lib/solargraph/pin/local_variable.rb +7 -1
  47. data/lib/solargraph/pin/method.rb +26 -18
  48. data/lib/solargraph/pin/namespace.rb +10 -7
  49. data/lib/solargraph/pin/parameter.rb +15 -6
  50. data/lib/solargraph/pin/proxy_type.rb +12 -6
  51. data/lib/solargraph/pin/until.rb +18 -0
  52. data/lib/solargraph/pin/while.rb +18 -0
  53. data/lib/solargraph/pin.rb +3 -0
  54. data/lib/solargraph/position.rb +7 -0
  55. data/lib/solargraph/range.rb +7 -0
  56. data/lib/solargraph/rbs_map/conversions.rb +16 -10
  57. data/lib/solargraph/rbs_map/core_fills.rb +10 -3
  58. data/lib/solargraph/rbs_map.rb +1 -0
  59. data/lib/solargraph/shell.rb +2 -0
  60. data/lib/solargraph/source/chain/array.rb +5 -4
  61. data/lib/solargraph/source/chain/call.rb +55 -19
  62. data/lib/solargraph/source/chain/constant.rb +1 -1
  63. data/lib/solargraph/source/chain/hash.rb +8 -2
  64. data/lib/solargraph/source/chain/if.rb +5 -0
  65. data/lib/solargraph/source/chain/link.rb +19 -5
  66. data/lib/solargraph/source/chain/literal.rb +27 -2
  67. data/lib/solargraph/source/chain/z_super.rb +1 -1
  68. data/lib/solargraph/source/chain.rb +107 -63
  69. data/lib/solargraph/source/cursor.rb +1 -11
  70. data/lib/solargraph/source/source_chainer.rb +2 -2
  71. data/lib/solargraph/source.rb +2 -1
  72. data/lib/solargraph/source_map/clip.rb +4 -2
  73. data/lib/solargraph/type_checker/checks.rb +4 -0
  74. data/lib/solargraph/type_checker.rb +41 -14
  75. data/lib/solargraph/version.rb +1 -1
  76. data/lib/solargraph/workspace/config.rb +7 -3
  77. data/lib/solargraph/workspace.rb +1 -1
  78. data/lib/solargraph/yard_map/mapper/to_constant.rb +1 -0
  79. data/lib/solargraph/yard_map/mapper/to_method.rb +42 -15
  80. data/lib/solargraph/yard_map/mapper/to_namespace.rb +1 -0
  81. data/lib/solargraph/yard_map/mapper.rb +1 -0
  82. data/lib/solargraph/yardoc.rb +1 -1
  83. data/lib/solargraph.rb +1 -0
  84. data/solargraph.gemspec +5 -5
  85. metadata +39 -25
@@ -132,6 +132,7 @@ module Solargraph
132
132
  result = false
133
133
  filenames.each do |filename|
134
134
  detach filename
135
+ source_map_hash.delete(filename)
135
136
  result ||= workspace.remove(filename)
136
137
  end
137
138
  result
@@ -192,11 +193,7 @@ module Solargraph
192
193
  else
193
194
  mutex.synchronize do
194
195
  clip = api_map.clip(cursor)
195
- if cursor.assign?
196
- [Pin::ProxyType.new(name: cursor.word, return_type: clip.infer)]
197
- else
198
- clip.define.map { |pin| pin.realize(api_map) }
199
- end
196
+ clip.define.map { |pin| pin.realize(api_map) }
200
197
  end
201
198
  end
202
199
  rescue FileNotFoundError => e
@@ -240,7 +237,7 @@ module Solargraph
240
237
  # @param column [Integer]
241
238
  # @param strip [Boolean] Strip special characters from variable names
242
239
  # @param only [Boolean] Search for references in the current file only
243
- # @return [Array<Solargraph::Range>]
240
+ # @return [Array<Solargraph::Location>]
244
241
  # @todo Take a Location instead of filename/line/column
245
242
  def references_from filename, line, column, strip: false, only: false
246
243
  sync_catalog
@@ -398,6 +395,8 @@ module Solargraph
398
395
  return [] unless open?(filename)
399
396
  result = []
400
397
  source = read(filename)
398
+
399
+ # @type [Hash{Class<Solargraph::Diagnostics::Base> => Array<String>}]
401
400
  repargs = {}
402
401
  workspace.config.reporters.each do |line|
403
402
  if line == 'all!'
@@ -431,7 +430,8 @@ module Solargraph
431
430
  Bench.new(
432
431
  source_maps: source_map_hash.values,
433
432
  workspace: workspace,
434
- external_requires: external_requires
433
+ external_requires: external_requires,
434
+ live_map: @current ? source_map_hash[@current.filename] : nil
435
435
  )
436
436
  end
437
437
 
@@ -650,7 +650,6 @@ module Solargraph
650
650
  return if @sync_count == 0
651
651
 
652
652
  mutex.synchronize do
653
- logger.warn "CATALOG"
654
653
  logger.info "Cataloging #{workspace.directory.empty? ? 'generic workspace' : workspace.directory}"
655
654
  api_map.catalog bench
656
655
  source_map_hash.values.each { |map| find_external_requires(map) }
@@ -5,6 +5,8 @@ module Solargraph
5
5
  # and Range.
6
6
  #
7
7
  class Location
8
+ include Equality
9
+
8
10
  # @return [String]
9
11
  attr_reader :filename
10
12
 
@@ -18,11 +20,24 @@ module Solargraph
18
20
  @range = range
19
21
  end
20
22
 
23
+ # @sg-ignore Fix "Not enough arguments to Module#protected"
24
+ protected def equality_fields
25
+ [filename, range]
26
+ end
27
+
21
28
  # @param location [self]
22
29
  def contain? location
23
30
  range.contain?(location.range.start) && range.contain?(location.range.ending) && filename == location.filename
24
31
  end
25
32
 
33
+ def inspect
34
+ "<#{self.class.name}: filename=#{filename}, range=#{range.inspect}>"
35
+ end
36
+
37
+ def to_s
38
+ inspect
39
+ end
40
+
26
41
  # @return [Hash]
27
42
  def to_hash
28
43
  {
@@ -13,6 +13,7 @@ module Solargraph
13
13
  end
14
14
 
15
15
  def on_comment *args
16
+ # @type [Array(Symbol, String, Array([Integer, nil], [Integer, nil]))]
16
17
  result = super
17
18
  if @buffer_lines[result[2][0]][0..result[2][1]].strip =~ /^#/
18
19
  chomped = result[1].chomp
@@ -24,24 +25,28 @@ module Solargraph
24
25
  result
25
26
  end
26
27
 
28
+ # @param result [Array(Symbol, String, Array([Integer, nil], [Integer, nil]))]
29
+ # @return [void]
30
+ def create_snippet(result)
31
+ chomped = result[1].chomp
32
+ @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0] || 0, result[2][1] || 0, result[2][0] || 0, (result[2][1] || 0) + chomped.length), chomped)
33
+ end
34
+
27
35
  def on_embdoc_beg *args
28
36
  result = super
29
- chomped = result[1].chomp
30
- @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
37
+ create_snippet(result)
31
38
  result
32
39
  end
33
40
 
34
41
  def on_embdoc *args
35
42
  result = super
36
- chomped = result[1].chomp
37
- @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
43
+ create_snippet(result)
38
44
  result
39
45
  end
40
46
 
41
47
  def on_embdoc_end *args
42
48
  result = super
43
- chomped = result[1].chomp
44
- @comments[result[2][0]] = Snippet.new(Range.from_to(result[2][0], result[2][1], result[2][0], result[2][1] + chomped.length), chomped)
49
+ create_snippet(result)
45
50
  result
46
51
  end
47
52
 
@@ -0,0 +1,226 @@
1
+ module Solargraph
2
+ module Parser
3
+ class FlowSensitiveTyping
4
+ include Solargraph::Parser::NodeMethods
5
+
6
+ # @param locals [Array<Solargraph::Pin::LocalVariable, Solargraph::Pin::Parameter>]
7
+ def initialize(locals, enclosing_breakable_pin = nil)
8
+ @locals = locals
9
+ @enclosing_breakable_pin = enclosing_breakable_pin
10
+ end
11
+
12
+ # @param and_node [Parser::AST::Node]
13
+ def process_and(and_node, true_ranges = [])
14
+ lhs = and_node.children[0]
15
+ rhs = and_node.children[1]
16
+
17
+ before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
18
+ before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
19
+
20
+ rhs_presence = Range.new(before_rhs_pos,
21
+ get_node_end_position(rhs))
22
+ process_isa(lhs, true_ranges + [rhs_presence])
23
+ end
24
+
25
+ # @param if_node [Parser::AST::Node]
26
+ def process_if(if_node)
27
+ #
28
+ # See if we can refine a type based on the result of 'if foo.nil?'
29
+ #
30
+ # [3] pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("if foo.is_a? Baz; then foo; else bar; end")
31
+ # => s(:if,
32
+ # s(:send,
33
+ # s(:send, nil, :foo), :is_a?,
34
+ # s(:const, nil, :Baz)),
35
+ # s(:send, nil, :foo),
36
+ # s(:send, nil, :bar))
37
+ # [4] pry(main)>
38
+ conditional_node = if_node.children[0]
39
+ then_clause = if_node.children[1]
40
+ else_clause = if_node.children[2]
41
+
42
+ true_ranges = []
43
+ if always_breaks?(else_clause)
44
+ unless enclosing_breakable_pin.nil?
45
+ rest_of_breakable_body = Range.new(get_node_end_position(if_node),
46
+ get_node_end_position(enclosing_breakable_pin.node))
47
+ true_ranges << rest_of_breakable_body
48
+ end
49
+ end
50
+
51
+ unless then_clause.nil?
52
+ #
53
+ # Add specialized locals for the then clause range
54
+ #
55
+ before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
56
+ before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
57
+ true_ranges << Range.new(before_then_clause_pos,
58
+ get_node_end_position(then_clause))
59
+ end
60
+
61
+ process_conditional(conditional_node, true_ranges)
62
+ end
63
+
64
+ class << self
65
+ include Logging
66
+ end
67
+
68
+ # Find a variable pin by name and where it is used.
69
+ #
70
+ # Resolves our most specific view of this variable's type by
71
+ # preferring pins created by flow-sensitive typing when we have
72
+ # them based on the Closure and Location.
73
+ #
74
+ # @param pins [Array<Pin::LocalVariable>]
75
+ # @param closure [Pin::Closure]
76
+ # @param location [Location]
77
+ def self.visible_pins(pins, name, closure, location)
78
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" }
79
+ pins_with_name = pins.select { |p| p.name == name }
80
+ if pins_with_name.empty?
81
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => [] - no pins with name" }
82
+ return []
83
+ end
84
+ pins_with_specific_visibility = pins.select { |p| p.name == name && p.presence && p.visible_at?(closure, location) }
85
+ if pins_with_specific_visibility.empty?
86
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_name} - no pins with specific visibility" }
87
+ return pins_with_name
88
+ end
89
+ visible_pins_specific_to_this_closure = pins_with_specific_visibility.select { |p| p.closure == closure }
90
+ if visible_pins_specific_to_this_closure.empty?
91
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_specific_visibility} - no visible pins specific to this closure (#{closure})}" }
92
+ return pins_with_specific_visibility
93
+ end
94
+ flow_defined_pins = pins_with_specific_visibility.select { |p| p.presence_certain? }
95
+ if flow_defined_pins.empty?
96
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{visible_pins_specific_to_this_closure} - no flow-defined pins" }
97
+ return visible_pins_specific_to_this_closure
98
+ end
99
+
100
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{flow_defined_pins}" }
101
+
102
+ flow_defined_pins
103
+ end
104
+
105
+ include Logging
106
+
107
+ private
108
+
109
+ # @param pin [Pin::LocalVariable]
110
+ # @param if_node [Parser::AST::Node]
111
+ def add_downcast_local(pin, downcast_type_name, presence)
112
+ # @todo Create pin#update method
113
+ new_pin = Solargraph::Pin::LocalVariable.new(
114
+ location: pin.location,
115
+ closure: pin.closure,
116
+ name: pin.name,
117
+ assignment: pin.assignment,
118
+ comments: pin.comments,
119
+ presence: presence,
120
+ return_type: ComplexType.try_parse(downcast_type_name),
121
+ presence_certain: true
122
+ )
123
+ locals.push(new_pin)
124
+ end
125
+
126
+ # @param facts_by_pin [Hash{Pin::LocalVariable => Array<Hash{Symbol => String}>}]
127
+ # @param presences [Array<Range>]
128
+ # @return [void]
129
+ def process_facts(facts_by_pin, presences)
130
+ #
131
+ # Add specialized locals for the rest of the block
132
+ #
133
+ facts_by_pin.each_pair do |pin, facts|
134
+ facts.each do |fact|
135
+ downcast_type_name = fact.fetch(:type)
136
+ presences.each do |presence|
137
+ add_downcast_local(pin, downcast_type_name, presence)
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ # @param conditional_node [Parser::AST::Node]
144
+ def process_conditional(conditional_node, true_ranges)
145
+ if conditional_node.type == :send
146
+ process_isa(conditional_node, true_ranges)
147
+ elsif conditional_node.type == :and
148
+ process_and(conditional_node, true_ranges)
149
+ end
150
+ end
151
+
152
+ # @param isa_node [Parser::AST::Node]
153
+ # @return [Array(String, String)]
154
+ def parse_isa(isa_node)
155
+ return unless isa_node.type == :send && isa_node.children[1] == :is_a?
156
+ # Check if conditional node follows this pattern:
157
+ # s(:send,
158
+ # s(:send, nil, :foo), :is_a?,
159
+ # s(:const, nil, :Baz)),
160
+ isa_receiver = isa_node.children[0]
161
+ isa_type_name = type_name(isa_node.children[2])
162
+ return unless isa_type_name
163
+
164
+ # check if isa_receiver looks like this:
165
+ # s(:send, nil, :foo)
166
+ # and set variable_name to :foo
167
+ if isa_receiver.type == :send && isa_receiver.children[0].nil? && isa_receiver.children[1].is_a?(Symbol)
168
+ variable_name = isa_receiver.children[1].to_s
169
+ end
170
+ # or like this:
171
+ # (lvar :repr)
172
+ variable_name = isa_receiver.children[0].to_s if isa_receiver.type == :lvar
173
+ return unless variable_name
174
+
175
+ [isa_type_name, variable_name]
176
+ end
177
+
178
+ def find_local(variable_name, position)
179
+ pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) }
180
+ return unless pins.length == 1
181
+ pins.first
182
+ end
183
+
184
+ def process_isa(isa_node, true_presences)
185
+ isa_type_name, variable_name = parse_isa(isa_node)
186
+ return if variable_name.nil? || variable_name.empty?
187
+ isa_position = Range.from_node(isa_node).start
188
+
189
+ pin = find_local(variable_name, isa_position)
190
+ return unless pin
191
+
192
+ if_true = {}
193
+ if_true[pin] ||= []
194
+ if_true[pin] << { type: isa_type_name }
195
+ process_facts(if_true, true_presences)
196
+ end
197
+
198
+ # @param node [Parser::AST::Node]
199
+ def type_name(node)
200
+ # e.g.,
201
+ # s(:const, nil, :Baz)
202
+ return unless node.type == :const
203
+ module_node = node.children[0]
204
+ class_node = node.children[1]
205
+
206
+ return class_node.to_s if module_node.nil?
207
+
208
+ module_type_name = type_name(module_node)
209
+ return unless module_type_name
210
+
211
+ "#{module_type_name}::#{class_node}"
212
+ end
213
+
214
+ # @todo "return type could not be inferred" should not trigger here
215
+ # @sg-ignore
216
+ # @param clause_node [Parser::AST::Node]
217
+ def always_breaks?(clause_node)
218
+ clause_node&.type == :break
219
+ end
220
+
221
+ attr_reader :locals
222
+
223
+ attr_reader :enclosing_breakable_pin
224
+ end
225
+ end
226
+ end
@@ -74,10 +74,24 @@ module Solargraph
74
74
 
75
75
  # @abstract
76
76
  # @param node [Parser::AST::Node]
77
- # @return [Hash{Parser::AST::Node => Chain}]
77
+ # @return [Hash{Parser::AST::Node => Source::Chain}]
78
78
  def convert_hash node
79
79
  raise NotImplementedError
80
80
  end
81
+
82
+ # @abstract
83
+ # @param node [Parser::AST::Node]
84
+ # @return [Position]
85
+ def get_node_start_position(node)
86
+ raise NotImplementedError
87
+ end
88
+
89
+ # @abstract
90
+ # @param node [Parser::AST::Node]
91
+ # @return [Position]
92
+ def get_node_end_position(node)
93
+ raise NotImplementedError
94
+ end
81
95
  end
82
96
  end
83
97
  end
@@ -3,6 +3,15 @@
3
3
  require 'parser/current'
4
4
  require 'parser/source/buffer'
5
5
 
6
+ # Awaiting ability to use a version containing https://github.com/whitequark/parser/pull/1076
7
+ #
8
+ # @!parse
9
+ # class ::Parser::Base < ::Parser::Builder
10
+ # # @return [Integer]
11
+ # def version; end
12
+ # end
13
+ # class ::Parser::CurrentRuby < ::Parser::Base; end
14
+
6
15
  module Solargraph
7
16
  module Parser
8
17
  module ParserGem
@@ -11,13 +20,9 @@ module Solargraph
11
20
  # @param filename [String, nil]
12
21
  # @return [Array(Parser::AST::Node, Hash{Integer => String})]
13
22
  def parse_with_comments code, filename = nil
14
- buffer = ::Parser::Source::Buffer.new(filename, 0)
15
- buffer.source = code
16
- node = parser.parse(buffer)
23
+ node = parse(code, filename)
17
24
  comments = CommentRipper.new(code, filename, 0).parse
18
25
  [node, comments]
19
- rescue ::Parser::SyntaxError => e
20
- raise Parser::SyntaxError, e.message
21
26
  end
22
27
 
23
28
  # @param code [String]
@@ -28,7 +33,7 @@ module Solargraph
28
33
  buffer = ::Parser::Source::Buffer.new(filename, line)
29
34
  buffer.source = code
30
35
  parser.parse(buffer)
31
- rescue ::Parser::SyntaxError => e
36
+ rescue ::Parser::SyntaxError, ::Parser::UnknownEncodingInMagicComment => e
32
37
  raise Parser::SyntaxError, e.message
33
38
  end
34
39
 
@@ -57,22 +57,22 @@ module Solargraph
57
57
  elsif n.type == :send
58
58
  if n.children[0].is_a?(::Parser::AST::Node)
59
59
  result.concat generate_links(n.children[0])
60
- result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
60
+ result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n))
61
61
  elsif n.children[0].nil?
62
62
  args = []
63
63
  n.children[2..-1].each do |c|
64
64
  args.push NodeChainer.chain(c, @filename, n)
65
65
  end
66
- result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
66
+ result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n))
67
67
  else
68
68
  raise "No idea what to do with #{n}"
69
69
  end
70
70
  elsif n.type == :csend
71
71
  if n.children[0].is_a?(::Parser::AST::Node)
72
72
  result.concat generate_links(n.children[0])
73
- result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
73
+ result.push Chain::QCall.new(n.children[1].to_s, Location.from_node(n), node_args(n))
74
74
  elsif n.children[0].nil?
75
- result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
75
+ result.push Chain::QCall.new(n.children[1].to_s, Location.from_node(n), node_args(n))
76
76
  else
77
77
  raise "No idea what to do with #{n}"
78
78
  end
@@ -82,28 +82,22 @@ module Solargraph
82
82
  result.push Chain::ZSuper.new('super')
83
83
  elsif n.type == :super
84
84
  args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
85
- result.push Chain::Call.new('super', args)
85
+ result.push Chain::Call.new('super', Location.from_node(n), args)
86
86
  elsif n.type == :yield
87
87
  args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
88
- result.push Chain::Call.new('yield', args)
88
+ result.push Chain::Call.new('yield', Location.from_node(n), args)
89
89
  elsif n.type == :const
90
90
  const = unpack_name(n)
91
91
  result.push Chain::Constant.new(const)
92
- elsif [:lvasgn, :ivasgn, :gvasgn, :cvasgn].include?(n.type)
93
- result.concat generate_links(n.children[1])
94
- elsif n.type == :lvar
95
- result.push Chain::Call.new(n.children[0].to_s)
96
- elsif n.type == :ivar
97
- result.push Chain::InstanceVariable.new(n.children[0].to_s)
98
- elsif n.type == :cvar
99
- result.push Chain::ClassVariable.new(n.children[0].to_s)
100
- elsif n.type == :gvar
101
- result.push Chain::GlobalVariable.new(n.children[0].to_s)
92
+ elsif [:lvar, :lvasgn].include?(n.type)
93
+ result.push Chain::Call.new(n.children[0].to_s, Location.from_node(n))
94
+ elsif [:ivar, :ivasgn].include?(n.type)
95
+ result.push Chain::InstanceVariable.new(n.children[0].to_s)
96
+ elsif [:cvar, :cvasgn].include?(n.type)
97
+ result.push Chain::ClassVariable.new(n.children[0].to_s)
98
+ elsif [:gvar, :gvasgn].include?(n.type)
99
+ result.push Chain::GlobalVariable.new(n.children[0].to_s)
102
100
  elsif n.type == :or_asgn
103
- # @todo: Need a new Link class here that evaluates the
104
- # existing variable type with the RHS, and generates a
105
- # union type of the LHS alone if never nil, or minus nil +
106
- # RHS if it is nilable.
107
101
  result.concat generate_links n.children[1]
108
102
  elsif [:class, :module, :def, :defs].include?(n.type)
109
103
  # @todo Undefined or what?
@@ -130,13 +124,13 @@ module Solargraph
130
124
  end
131
125
  end
132
126
  elsif n.type == :hash
133
- result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
127
+ result.push Chain::Hash.new('::Hash', n, hash_is_splatted?(n))
134
128
  elsif n.type == :array
135
129
  chained_children = n.children.map { |c| NodeChainer.chain(c) }
136
- result.push Source::Chain::Array.new(chained_children)
130
+ result.push Source::Chain::Array.new(chained_children, n)
137
131
  else
138
132
  lit = infer_literal_node_type(n)
139
- result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
133
+ result.push (lit ? Chain::Literal.new(lit, n) : Chain::Link.new)
140
134
  end
141
135
  result
142
136
  end
@@ -12,7 +12,7 @@ require 'ast'
12
12
  # class Node
13
13
  # # New children
14
14
  #
15
- # # @return [Array<AST::Node>]
15
+ # # @return [Array<self>]
16
16
  # attr_reader :children
17
17
  # end
18
18
  # end
@@ -40,7 +40,7 @@ module Solargraph
40
40
  if n.is_a?(AST::Node)
41
41
  if n.type == :cbase
42
42
  parts = [''] + pack_name(n)
43
- else
43
+ elsif n.type == :const
44
44
  parts += pack_name(n)
45
45
  end
46
46
  else
@@ -59,6 +59,8 @@ module Solargraph
59
59
  return '::String'
60
60
  elsif node.type == :array
61
61
  return '::Array'
62
+ elsif node.type == :nil
63
+ return '::NilClass'
62
64
  elsif node.type == :hash
63
65
  return '::Hash'
64
66
  elsif node.type == :int
@@ -462,7 +464,7 @@ module Solargraph
462
464
  result
463
465
  end
464
466
 
465
- # @param nodes [Enumerable<Parser::AST::Node, BaseObject>]
467
+ # @param nodes [Enumerable<Parser::AST::Node, BasicObject>]
466
468
  # @return [Array<Parser::AST::Node, nil>]
467
469
  def reduce_to_value_nodes nodes
468
470
  result = []
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Parser
5
+ module ParserGem
6
+ module NodeProcessors
7
+ class AndNode < Parser::NodeProcessor::Base
8
+ include ParserGem::NodeMethods
9
+
10
+ def process
11
+ process_children
12
+
13
+ position = get_node_start_position(node)
14
+ enclosing_breakable_pin = pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location.range.contain?(position)}.last
15
+ FlowSensitiveTyping.new(locals, enclosing_breakable_pin).process_and(node)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -8,6 +8,17 @@ module Solargraph
8
8
  include ParserGem::NodeMethods
9
9
 
10
10
  def process
11
+ if Convention::StructDefinition::StructAssignmentNode.valid?(node)
12
+ process_struct_assignment
13
+ else
14
+ process_constant_assignment
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ # @return [void]
21
+ def process_constant_assignment
11
22
  pins.push Solargraph::Pin::Constant.new(
12
23
  location: get_node_location(node),
13
24
  closure: region.closure,
@@ -18,7 +29,16 @@ module Solargraph
18
29
  process_children
19
30
  end
20
31
 
21
- private
32
+ # TODO: Move this out of [CasgnNode] once [Solargraph::Parser::NodeProcessor] supports
33
+ # multiple processors.
34
+ def process_struct_assignment
35
+ processor_klass = Convention::StructDefinition::NodeProcessors::StructNode
36
+ processor = processor_klass.new(node, region, pins, locals)
37
+ processor.process
38
+
39
+ @pins = processor.pins
40
+ @locals = processor.locals
41
+ end
22
42
 
23
43
  # @return [String]
24
44
  def const_name
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Solargraph
4
+ module Parser
5
+ module ParserGem
6
+ module NodeProcessors
7
+ class IfNode < Parser::NodeProcessor::Base
8
+ include ParserGem::NodeMethods
9
+
10
+ def process
11
+ process_children
12
+
13
+ position = get_node_start_position(node)
14
+ enclosing_breakable_pin = pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location.range.contain?(position)}.last
15
+ FlowSensitiveTyping.new(locals, enclosing_breakable_pin).process_if(node)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -8,8 +8,8 @@ module Solargraph
8
8
  include ParserGem::NodeMethods
9
9
 
10
10
  def process
11
- # variable not visible until next statement
12
- presence = Range.new(get_node_end_position(node), region.closure.location.range.ending)
11
+ here = get_node_start_position(node)
12
+ presence = Range.new(here, region.closure.location.range.ending)
13
13
  loc = get_node_location(node)
14
14
  locals.push Solargraph::Pin::LocalVariable.new(
15
15
  location: loc,