solargraph 0.54.5 → 0.55.4

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 (103) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/typecheck.yml +1 -1
  3. data/CHANGELOG.md +27 -0
  4. data/lib/solargraph/api_map/store.rb +9 -4
  5. data/lib/solargraph/api_map.rb +116 -39
  6. data/lib/solargraph/complex_type/type_methods.rb +1 -0
  7. data/lib/solargraph/complex_type/unique_type.rb +91 -9
  8. data/lib/solargraph/complex_type.rb +35 -6
  9. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +60 -0
  10. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +100 -0
  11. data/lib/solargraph/convention/struct_definition.rb +101 -0
  12. data/lib/solargraph/convention.rb +1 -0
  13. data/lib/solargraph/doc_map.rb +83 -23
  14. data/lib/solargraph/gem_pins.rb +2 -1
  15. data/lib/solargraph/language_server/host/message_worker.rb +10 -7
  16. data/lib/solargraph/language_server/host.rb +3 -1
  17. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +1 -0
  18. data/lib/solargraph/location.rb +8 -0
  19. data/lib/solargraph/logging.rb +1 -0
  20. data/lib/solargraph/parser/comment_ripper.rb +12 -6
  21. data/lib/solargraph/parser/flow_sensitive_typing.rb +227 -0
  22. data/lib/solargraph/parser/node_methods.rb +14 -0
  23. data/lib/solargraph/parser/node_processor.rb +3 -2
  24. data/lib/solargraph/parser/parser_gem/class_methods.rb +9 -0
  25. data/lib/solargraph/parser/parser_gem/node_chainer.rb +10 -10
  26. data/lib/solargraph/parser/parser_gem/node_methods.rb +4 -2
  27. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  28. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  29. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  30. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +4 -2
  31. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +23 -2
  32. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  33. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  34. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  35. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  36. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +21 -0
  37. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  38. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  39. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +29 -6
  40. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +41 -0
  41. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +2 -1
  42. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  43. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +28 -16
  44. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +2 -1
  45. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  46. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  47. data/lib/solargraph/parser/parser_gem/node_processors.rb +10 -0
  48. data/lib/solargraph/parser/region.rb +1 -1
  49. data/lib/solargraph/parser.rb +1 -0
  50. data/lib/solargraph/pin/base.rb +34 -5
  51. data/lib/solargraph/pin/base_variable.rb +7 -1
  52. data/lib/solargraph/pin/block.rb +2 -0
  53. data/lib/solargraph/pin/breakable.rb +9 -0
  54. data/lib/solargraph/pin/callable.rb +5 -3
  55. data/lib/solargraph/pin/closure.rb +6 -1
  56. data/lib/solargraph/pin/common.rb +5 -0
  57. data/lib/solargraph/pin/delegated_method.rb +20 -1
  58. data/lib/solargraph/pin/documenting.rb +16 -0
  59. data/lib/solargraph/pin/keyword.rb +7 -2
  60. data/lib/solargraph/pin/local_variable.rb +7 -1
  61. data/lib/solargraph/pin/method.rb +34 -27
  62. data/lib/solargraph/pin/namespace.rb +17 -9
  63. data/lib/solargraph/pin/parameter.rb +17 -5
  64. data/lib/solargraph/pin/proxy_type.rb +12 -6
  65. data/lib/solargraph/pin/reference/override.rb +10 -6
  66. data/lib/solargraph/pin/reference/require.rb +2 -2
  67. data/lib/solargraph/pin/signature.rb +4 -0
  68. data/lib/solargraph/pin/singleton.rb +1 -1
  69. data/lib/solargraph/pin/symbol.rb +3 -2
  70. data/lib/solargraph/pin/until.rb +18 -0
  71. data/lib/solargraph/pin/while.rb +18 -0
  72. data/lib/solargraph/pin.rb +4 -1
  73. data/lib/solargraph/rbs_map/conversions.rb +172 -56
  74. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  75. data/lib/solargraph/rbs_map/core_map.rb +3 -2
  76. data/lib/solargraph/shell.rb +1 -0
  77. data/lib/solargraph/source/chain/array.rb +11 -7
  78. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  79. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  80. data/lib/solargraph/source/chain/call.rb +53 -23
  81. data/lib/solargraph/source/chain/constant.rb +1 -1
  82. data/lib/solargraph/source/chain/hash.rb +4 -3
  83. data/lib/solargraph/source/chain/head.rb +1 -1
  84. data/lib/solargraph/source/chain/if.rb +1 -1
  85. data/lib/solargraph/source/chain/link.rb +2 -0
  86. data/lib/solargraph/source/chain/literal.rb +22 -2
  87. data/lib/solargraph/source/chain/or.rb +1 -1
  88. data/lib/solargraph/source/chain/z_super.rb +1 -1
  89. data/lib/solargraph/source/chain.rb +78 -48
  90. data/lib/solargraph/source/source_chainer.rb +2 -2
  91. data/lib/solargraph/source_map/clip.rb +3 -1
  92. data/lib/solargraph/source_map/mapper.rb +9 -5
  93. data/lib/solargraph/type_checker/checks.rb +4 -0
  94. data/lib/solargraph/type_checker.rb +35 -8
  95. data/lib/solargraph/version.rb +1 -1
  96. data/lib/solargraph/yard_map/mapper/to_constant.rb +4 -2
  97. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -15
  98. data/lib/solargraph/yard_map/mapper/to_namespace.rb +4 -2
  99. data/lib/solargraph/yard_map/mapper.rb +4 -3
  100. data/lib/solargraph/yard_map/to_method.rb +4 -2
  101. data/lib/solargraph.rb +20 -0
  102. data/rbs/fills/tuple.rbs +150 -0
  103. metadata +15 -2
@@ -0,0 +1,227 @@
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
+ source: :flow_sensitive_typing
123
+ )
124
+ locals.push(new_pin)
125
+ end
126
+
127
+ # @param facts_by_pin [Hash{Pin::LocalVariable => Array<Hash{Symbol => String}>}]
128
+ # @param presences [Array<Range>]
129
+ # @return [void]
130
+ def process_facts(facts_by_pin, presences)
131
+ #
132
+ # Add specialized locals for the rest of the block
133
+ #
134
+ facts_by_pin.each_pair do |pin, facts|
135
+ facts.each do |fact|
136
+ downcast_type_name = fact.fetch(:type)
137
+ presences.each do |presence|
138
+ add_downcast_local(pin, downcast_type_name, presence)
139
+ end
140
+ end
141
+ end
142
+ end
143
+
144
+ # @param conditional_node [Parser::AST::Node]
145
+ def process_conditional(conditional_node, true_ranges)
146
+ if conditional_node.type == :send
147
+ process_isa(conditional_node, true_ranges)
148
+ elsif conditional_node.type == :and
149
+ process_and(conditional_node, true_ranges)
150
+ end
151
+ end
152
+
153
+ # @param isa_node [Parser::AST::Node]
154
+ # @return [Array(String, String)]
155
+ def parse_isa(isa_node)
156
+ return unless isa_node&.type == :send && isa_node.children[1] == :is_a?
157
+ # Check if conditional node follows this pattern:
158
+ # s(:send,
159
+ # s(:send, nil, :foo), :is_a?,
160
+ # s(:const, nil, :Baz)),
161
+ isa_receiver = isa_node.children[0]
162
+ isa_type_name = type_name(isa_node.children[2])
163
+ return unless isa_type_name
164
+
165
+ # check if isa_receiver looks like this:
166
+ # s(:send, nil, :foo)
167
+ # and set variable_name to :foo
168
+ if isa_receiver&.type == :send && isa_receiver.children[0].nil? && isa_receiver.children[1].is_a?(Symbol)
169
+ variable_name = isa_receiver.children[1].to_s
170
+ end
171
+ # or like this:
172
+ # (lvar :repr)
173
+ variable_name = isa_receiver.children[0].to_s if isa_receiver&.type == :lvar
174
+ return unless variable_name
175
+
176
+ [isa_type_name, variable_name]
177
+ end
178
+
179
+ def find_local(variable_name, position)
180
+ pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) }
181
+ return unless pins.length == 1
182
+ pins.first
183
+ end
184
+
185
+ def process_isa(isa_node, true_presences)
186
+ isa_type_name, variable_name = parse_isa(isa_node)
187
+ return if variable_name.nil? || variable_name.empty?
188
+ isa_position = Range.from_node(isa_node).start
189
+
190
+ pin = find_local(variable_name, isa_position)
191
+ return unless pin
192
+
193
+ if_true = {}
194
+ if_true[pin] ||= []
195
+ if_true[pin] << { type: isa_type_name }
196
+ process_facts(if_true, true_presences)
197
+ end
198
+
199
+ # @param node [Parser::AST::Node]
200
+ def type_name(node)
201
+ # e.g.,
202
+ # s(:const, nil, :Baz)
203
+ return unless node.type == :const
204
+ module_node = node.children[0]
205
+ class_node = node.children[1]
206
+
207
+ return class_node.to_s if module_node.nil?
208
+
209
+ module_type_name = type_name(module_node)
210
+ return unless module_type_name
211
+
212
+ "#{module_type_name}::#{class_node}"
213
+ end
214
+
215
+ # @todo "return type could not be inferred" should not trigger here
216
+ # @sg-ignore
217
+ # @param clause_node [Parser::AST::Node]
218
+ def always_breaks?(clause_node)
219
+ clause_node&.type == :break
220
+ end
221
+
222
+ attr_reader :locals
223
+
224
+ attr_reader :enclosing_breakable_pin
225
+ end
226
+ end
227
+ end
@@ -78,6 +78,20 @@ module Solargraph
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
@@ -9,7 +9,7 @@ module Solargraph
9
9
  autoload :Base, 'solargraph/parser/node_processor/base'
10
10
 
11
11
  class << self
12
- # @type [Hash<Symbol, Class<NodeProcessor::Base>>]
12
+ # @type [Hash{Symbol => Class<NodeProcessor::Base>}]
13
13
  @@processors ||= {}
14
14
 
15
15
  # Register a processor for a node type.
@@ -31,7 +31,8 @@ module Solargraph
31
31
  if pins.empty?
32
32
  pins.push Pin::Namespace.new(
33
33
  location: region.source.location,
34
- name: ''
34
+ name: '',
35
+ source: :parser,
35
36
  )
36
37
  end
37
38
  return [pins, locals] unless Parser.is_ast_node?(node)
@@ -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
@@ -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,15 +82,15 @@ 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
92
  elsif [:lvar, :lvasgn].include?(n.type)
93
- result.push Chain::Call.new(n.children[0].to_s)
93
+ result.push Chain::Call.new(n.children[0].to_s, Location.from_node(n))
94
94
  elsif [:ivar, :ivasgn].include?(n.type)
95
95
  result.push Chain::InstanceVariable.new(n.children[0].to_s)
96
96
  elsif [:cvar, :cvasgn].include?(n.type)
@@ -124,13 +124,13 @@ module Solargraph
124
124
  end
125
125
  end
126
126
  elsif n.type == :hash
127
- result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
127
+ result.push Chain::Hash.new('::Hash', n, hash_is_splatted?(n))
128
128
  elsif n.type == :array
129
129
  chained_children = n.children.map { |c| NodeChainer.chain(c) }
130
- result.push Source::Chain::Array.new(chained_children)
130
+ result.push Source::Chain::Array.new(chained_children, n)
131
131
  else
132
132
  lit = infer_literal_node_type(n)
133
- result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
133
+ result.push (lit ? Chain::Literal.new(lit, n) : Chain::Link.new)
134
134
  end
135
135
  result
136
136
  end
@@ -12,7 +12,7 @@ require 'ast'
12
12
  # class Node
13
13
  # # New children
14
14
  #
15
- # # @return [Array<self>]
15
+ # # @return [Array<self, Integer, String, Symbol, nil>]
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
@@ -12,7 +12,8 @@ module Solargraph
12
12
  closure: region.closure,
13
13
  name: node.children[0].children[0].to_s,
14
14
  original: node.children[1].children[0].to_s,
15
- scope: region.scope || :instance
15
+ scope: region.scope || :instance,
16
+ source: :parser
16
17
  )
17
18
  process_children
18
19
  end
@@ -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
@@ -21,7 +21,8 @@ module Solargraph
21
21
  assignment: u.children[1],
22
22
  asgn_code: u.children[1] ? region.code_for(u.children[1]) : nil,
23
23
  presence: callable.location.range,
24
- decl: get_decl(u)
24
+ decl: get_decl(u),
25
+ source: :parser
25
26
  )
26
27
  callable.parameters.push locals.last
27
28
  end
@@ -40,7 +41,8 @@ module Solargraph
40
41
  location: loc,
41
42
  closure: callable,
42
43
  presence: region.closure.location.range,
43
- decl: get_decl(node)
44
+ decl: get_decl(node),
45
+ source: :parser
44
46
  )
45
47
  callable.parameters.push locals.last
46
48
  end
@@ -13,7 +13,8 @@ module Solargraph
13
13
  Solargraph::Pin::Namespace.new(
14
14
  location: location,
15
15
  type: :class,
16
- name: unpack_name(node.children[0].children[0])
16
+ name: unpack_name(node.children[0].children[0]),
17
+ source: :parser,
17
18
  )
18
19
  else
19
20
  region.closure
@@ -24,7 +25,8 @@ module Solargraph
24
25
  node: node,
25
26
  receiver: node.children[0],
26
27
  comments: comments_for(node),
27
- scope: region.scope || region.closure.context.scope
28
+ scope: region.scope || region.closure.context.scope,
29
+ source: :parser
28
30
  )
29
31
  process_children region.update(closure: pins.last)
30
32
  end
@@ -8,17 +8,38 @@ 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,
14
25
  name: const_name,
15
26
  comments: comments_for(node),
16
- assignment: node.children[2]
27
+ assignment: node.children[2],
28
+ source: :parser
17
29
  )
18
30
  process_children
19
31
  end
20
32
 
21
- private
33
+ # TODO: Move this out of [CasgnNode] once [Solargraph::Parser::NodeProcessor] supports
34
+ # multiple processors.
35
+ def process_struct_assignment
36
+ processor_klass = Convention::StructDefinition::NodeProcessors::StructNode
37
+ processor = processor_klass.new(node, region, pins, locals)
38
+ processor.process
39
+
40
+ @pins = processor.pins
41
+ @locals = processor.locals
42
+ end
22
43
 
23
44
  # @return [String]
24
45
  def const_name
@@ -12,7 +12,8 @@ module Solargraph
12
12
  closure: region.closure,
13
13
  name: node.children[0].to_s,
14
14
  comments: comments_for(node),
15
- assignment: node.children[1]
15
+ assignment: node.children[1],
16
+ source: :parser
16
17
  )
17
18
  process_children
18
19
  end
@@ -15,7 +15,8 @@ module Solargraph
15
15
  comments: comments_for(node),
16
16
  scope: scope,
17
17
  visibility: scope == :instance && name == 'initialize' ? :private : region.visibility,
18
- node: node
18
+ node: node,
19
+ source: :parser,
19
20
  )
20
21
  if region.visibility == :module_function
21
22
  pins.push Solargraph::Pin::Method.new(
@@ -26,7 +27,8 @@ module Solargraph
26
27
  scope: :class,
27
28
  visibility: :public,
28
29
  parameters: methpin.parameters,
29
- node: methpin.node
30
+ node: methpin.node,
31
+ source: :parser,
30
32
  )
31
33
  pins.push Solargraph::Pin::Method.new(
32
34
  location: methpin.location,
@@ -36,7 +38,8 @@ module Solargraph
36
38
  scope: :instance,
37
39
  visibility: :private,
38
40
  parameters: methpin.parameters,
39
- node: methpin.node
41
+ node: methpin.node,
42
+ source: :parser,
40
43
  )
41
44
  else
42
45
  pins.push methpin
@@ -25,7 +25,8 @@ module Solargraph
25
25
  comments: comments_for(node),
26
26
  scope: :class,
27
27
  visibility: s_visi,
28
- node: node
28
+ node: node,
29
+ source: :parser,
29
30
  )
30
31
  process_children region.update(closure: pins.last, scope: :class)
31
32
  end
@@ -12,7 +12,8 @@ module Solargraph
12
12
  closure: region.closure,
13
13
  name: node.children[0].to_s,
14
14
  comments: comments_for(node),
15
- assignment: node.children[1]
15
+ assignment: node.children[1],
16
+ source: :parser
16
17
  )
17
18
  process_children
18
19
  end
@@ -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
@@ -14,7 +14,8 @@ module Solargraph
14
14
  closure: region.closure,
15
15
  name: node.children[0].to_s,
16
16
  comments: comments_for(node),
17
- assignment: node.children[1]
17
+ assignment: node.children[1],
18
+ source: :parser
18
19
  )
19
20
  if region.visibility == :module_function
20
21
  here = get_node_start_position(node)
@@ -25,7 +26,8 @@ module Solargraph
25
26
  closure: Pin::Namespace.new(type: :module, closure: region.closure.closure, name: region.closure.name),
26
27
  name: node.children[0].to_s,
27
28
  comments: comments_for(node),
28
- assignment: node.children[1]
29
+ assignment: node.children[1],
30
+ source: :parser
29
31
  )
30
32
  end
31
33
  end
@@ -17,7 +17,8 @@ module Solargraph
17
17
  name: node.children[0].to_s,
18
18
  assignment: node.children[1],
19
19
  comments: comments_for(node),
20
- presence: presence
20
+ presence: presence,
21
+ source: :parser
21
22
  )
22
23
  process_children
23
24
  end
@@ -8,10 +8,20 @@ module Solargraph
8
8
  include ParserGem::NodeMethods
9
9
 
10
10
  def process
11
- sc = nil
12
- if node.type == :class and !node.children[1].nil?
13
- sc = unpack_name(node.children[1])
11
+ superclass_name = nil
12
+ superclass_name = unpack_name(node.children[1]) if node.type == :class && node.children[1]&.type == :const
13
+
14
+ if Convention::StructDefinition::StructDefintionNode.valid?(node)
15
+ process_struct_definition
16
+ else
17
+ process_namespace(superclass_name)
14
18
  end
19
+ end
20
+
21
+ private
22
+
23
+ # @param superclass_name [String, nil]
24
+ def process_namespace(superclass_name)
15
25
  loc = get_node_location(node)
16
26
  nspin = Solargraph::Pin::Namespace.new(
17
27
  type: node.type,
@@ -20,18 +30,31 @@ module Solargraph
20
30
  name: unpack_name(node.children[0]),
21
31
  comments: comments_for(node),
22
32
  visibility: :public,
23
- gates: region.closure.gates.freeze
33
+ gates: region.closure.gates.freeze,
34
+ source: :parser
24
35
  )
25
36
  pins.push nspin
26
- unless sc.nil?
37
+ unless superclass_name.nil?
27
38
  pins.push Pin::Reference::Superclass.new(
28
39
  location: loc,
29
40
  closure: pins.last,
30
- name: sc
41
+ name: superclass_name,
42
+ source: :parser
31
43
  )
32
44
  end
33
45
  process_children region.update(closure: nspin, visibility: :public)
34
46
  end
47
+
48
+ # TODO: Move this out of [NamespaceNode] once [Solargraph::Parser::NodeProcessor] supports
49
+ # multiple processors.
50
+ def process_struct_definition
51
+ processor_klass = Convention::StructDefinition::NodeProcessors::StructNode
52
+ processor = processor_klass.new(node, region, pins, locals)
53
+ processor.process
54
+
55
+ @pins = processor.pins
56
+ @locals = processor.locals
57
+ end
35
58
  end
36
59
  end
37
60
  end