solargraph 0.54.4 → 0.57.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 (178) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +125 -0
  3. data/.github/workflows/plugins.yml +149 -5
  4. data/.github/workflows/rspec.yml +39 -4
  5. data/.github/workflows/typecheck.yml +8 -3
  6. data/.gitignore +7 -0
  7. data/.overcommit.yml +72 -0
  8. data/.rspec +1 -0
  9. data/.rubocop.yml +66 -0
  10. data/.rubocop_todo.yml +2627 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +104 -0
  13. data/README.md +20 -6
  14. data/Rakefile +125 -13
  15. data/lib/solargraph/api_map/cache.rb +3 -2
  16. data/lib/solargraph/api_map/constants.rb +218 -0
  17. data/lib/solargraph/api_map/index.rb +44 -42
  18. data/lib/solargraph/api_map/source_to_yard.rb +10 -4
  19. data/lib/solargraph/api_map/store.rb +165 -32
  20. data/lib/solargraph/api_map.rb +319 -243
  21. data/lib/solargraph/bench.rb +18 -1
  22. data/lib/solargraph/complex_type/type_methods.rb +7 -1
  23. data/lib/solargraph/complex_type/unique_type.rb +105 -16
  24. data/lib/solargraph/complex_type.rb +40 -7
  25. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  26. data/lib/solargraph/convention/base.rb +20 -3
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  29. data/lib/solargraph/convention/data_definition.rb +105 -0
  30. data/lib/solargraph/convention/gemspec.rb +3 -2
  31. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  32. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  33. data/lib/solargraph/convention/struct_definition.rb +164 -0
  34. data/lib/solargraph/convention.rb +35 -4
  35. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  36. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -1
  37. data/lib/solargraph/doc_map.rb +313 -65
  38. data/lib/solargraph/environ.rb +9 -2
  39. data/lib/solargraph/gem_pins.rb +60 -38
  40. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  41. data/lib/solargraph/language_server/host/message_worker.rb +13 -7
  42. data/lib/solargraph/language_server/host.rb +14 -3
  43. data/lib/solargraph/language_server/message/base.rb +2 -1
  44. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  45. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  46. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  47. data/lib/solargraph/language_server/message/text_document/definition.rb +2 -0
  48. data/lib/solargraph/language_server/message/text_document/formatting.rb +16 -2
  49. data/lib/solargraph/language_server/message/text_document/type_definition.rb +1 -0
  50. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  51. data/lib/solargraph/language_server/progress.rb +8 -0
  52. data/lib/solargraph/language_server/request.rb +1 -0
  53. data/lib/solargraph/library.rb +53 -32
  54. data/lib/solargraph/location.rb +23 -0
  55. data/lib/solargraph/logging.rb +12 -2
  56. data/lib/solargraph/page.rb +4 -0
  57. data/lib/solargraph/parser/comment_ripper.rb +20 -7
  58. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  59. data/lib/solargraph/parser/node_methods.rb +16 -2
  60. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  61. data/lib/solargraph/parser/node_processor.rb +26 -9
  62. data/lib/solargraph/parser/parser_gem/class_methods.rb +17 -15
  63. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  64. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -11
  65. data/lib/solargraph/parser/parser_gem/node_methods.rb +8 -4
  66. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  67. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +21 -0
  68. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +4 -2
  69. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +7 -4
  70. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  71. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  72. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  73. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  74. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  75. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  76. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  77. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  78. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +7 -1
  79. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  80. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +42 -0
  81. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  82. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  83. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +4 -3
  84. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +63 -30
  85. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  86. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  87. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  88. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  89. data/lib/solargraph/parser/region.rb +4 -1
  90. data/lib/solargraph/parser/snippet.rb +2 -0
  91. data/lib/solargraph/parser.rb +1 -0
  92. data/lib/solargraph/pin/base.rb +360 -30
  93. data/lib/solargraph/pin/base_variable.rb +16 -10
  94. data/lib/solargraph/pin/block.rb +2 -0
  95. data/lib/solargraph/pin/breakable.rb +9 -0
  96. data/lib/solargraph/pin/callable.rb +83 -3
  97. data/lib/solargraph/pin/closure.rb +20 -1
  98. data/lib/solargraph/pin/common.rb +10 -1
  99. data/lib/solargraph/pin/constant.rb +2 -0
  100. data/lib/solargraph/pin/delegated_method.rb +21 -1
  101. data/lib/solargraph/pin/documenting.rb +16 -0
  102. data/lib/solargraph/pin/keyword.rb +7 -2
  103. data/lib/solargraph/pin/local_variable.rb +18 -6
  104. data/lib/solargraph/pin/method.rb +175 -46
  105. data/lib/solargraph/pin/method_alias.rb +3 -0
  106. data/lib/solargraph/pin/namespace.rb +17 -9
  107. data/lib/solargraph/pin/parameter.rb +78 -19
  108. data/lib/solargraph/pin/proxy_type.rb +13 -6
  109. data/lib/solargraph/pin/reference/override.rb +24 -6
  110. data/lib/solargraph/pin/reference/require.rb +2 -2
  111. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  112. data/lib/solargraph/pin/reference.rb +26 -0
  113. data/lib/solargraph/pin/search.rb +3 -1
  114. data/lib/solargraph/pin/signature.rb +44 -0
  115. data/lib/solargraph/pin/singleton.rb +1 -1
  116. data/lib/solargraph/pin/symbol.rb +8 -2
  117. data/lib/solargraph/pin/until.rb +18 -0
  118. data/lib/solargraph/pin/while.rb +18 -0
  119. data/lib/solargraph/pin.rb +4 -1
  120. data/lib/solargraph/pin_cache.rb +245 -0
  121. data/lib/solargraph/position.rb +11 -0
  122. data/lib/solargraph/range.rb +10 -0
  123. data/lib/solargraph/rbs_map/conversions.rb +226 -70
  124. data/lib/solargraph/rbs_map/core_fills.rb +32 -16
  125. data/lib/solargraph/rbs_map/core_map.rb +37 -11
  126. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  127. data/lib/solargraph/rbs_map.rb +88 -18
  128. data/lib/solargraph/shell.rb +20 -18
  129. data/lib/solargraph/source/chain/array.rb +11 -7
  130. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  131. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  132. data/lib/solargraph/source/chain/call.rb +53 -23
  133. data/lib/solargraph/source/chain/constant.rb +1 -1
  134. data/lib/solargraph/source/chain/hash.rb +4 -3
  135. data/lib/solargraph/source/chain/head.rb +1 -1
  136. data/lib/solargraph/source/chain/if.rb +1 -1
  137. data/lib/solargraph/source/chain/link.rb +12 -1
  138. data/lib/solargraph/source/chain/literal.rb +22 -2
  139. data/lib/solargraph/source/chain/or.rb +1 -1
  140. data/lib/solargraph/source/chain/z_super.rb +1 -1
  141. data/lib/solargraph/source/chain.rb +84 -47
  142. data/lib/solargraph/source/change.rb +2 -2
  143. data/lib/solargraph/source/cursor.rb +2 -3
  144. data/lib/solargraph/source/source_chainer.rb +3 -3
  145. data/lib/solargraph/source.rb +5 -2
  146. data/lib/solargraph/source_map/clip.rb +4 -2
  147. data/lib/solargraph/source_map/data.rb +4 -0
  148. data/lib/solargraph/source_map/mapper.rb +13 -7
  149. data/lib/solargraph/source_map.rb +21 -31
  150. data/lib/solargraph/type_checker/checks.rb +4 -0
  151. data/lib/solargraph/type_checker/param_def.rb +2 -0
  152. data/lib/solargraph/type_checker/rules.rb +8 -0
  153. data/lib/solargraph/type_checker.rb +208 -128
  154. data/lib/solargraph/version.rb +1 -1
  155. data/lib/solargraph/views/_method.erb +10 -10
  156. data/lib/solargraph/views/_namespace.erb +3 -3
  157. data/lib/solargraph/views/document.erb +10 -10
  158. data/lib/solargraph/workspace/config.rb +1 -3
  159. data/lib/solargraph/workspace/require_paths.rb +98 -0
  160. data/lib/solargraph/workspace.rb +38 -52
  161. data/lib/solargraph/yard_map/helpers.rb +29 -1
  162. data/lib/solargraph/yard_map/mapper/to_constant.rb +7 -5
  163. data/lib/solargraph/yard_map/mapper/to_method.rb +53 -18
  164. data/lib/solargraph/yard_map/mapper/to_namespace.rb +9 -7
  165. data/lib/solargraph/yard_map/mapper.rb +4 -3
  166. data/lib/solargraph/yard_map/to_method.rb +4 -2
  167. data/lib/solargraph/yardoc.rb +22 -10
  168. data/lib/solargraph.rb +34 -1
  169. data/rbs/fills/tuple.rbs +149 -0
  170. data/rbs_collection.yaml +19 -0
  171. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  172. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  173. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  174. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  175. data/solargraph.gemspec +15 -4
  176. metadata +157 -15
  177. data/lib/.rubocop.yml +0 -22
  178. data/lib/solargraph/cache.rb +0 -77
@@ -0,0 +1,255 @@
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
+ # @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil]
8
+ def initialize(locals, enclosing_breakable_pin = nil)
9
+ @locals = locals
10
+ @enclosing_breakable_pin = enclosing_breakable_pin
11
+ end
12
+
13
+ # @param and_node [Parser::AST::Node]
14
+ # @param true_ranges [Array<Range>]
15
+ #
16
+ # @return [void]
17
+ def process_and(and_node, true_ranges = [])
18
+ # @type [Parser::AST::Node]
19
+ lhs = and_node.children[0]
20
+ # @type [Parser::AST::Node]
21
+ rhs = and_node.children[1]
22
+
23
+ before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
24
+ before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
25
+
26
+ rhs_presence = Range.new(before_rhs_pos,
27
+ get_node_end_position(rhs))
28
+ process_isa(lhs, true_ranges + [rhs_presence])
29
+ end
30
+
31
+ # @param if_node [Parser::AST::Node]
32
+ #
33
+ # @return [void]
34
+ def process_if(if_node)
35
+ #
36
+ # See if we can refine a type based on the result of 'if foo.nil?'
37
+ #
38
+ # [3] pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("if foo.is_a? Baz; then foo; else bar; end")
39
+ # => s(:if,
40
+ # s(:send,
41
+ # s(:send, nil, :foo), :is_a?,
42
+ # s(:const, nil, :Baz)),
43
+ # s(:send, nil, :foo),
44
+ # s(:send, nil, :bar))
45
+ # [4] pry(main)>
46
+ conditional_node = if_node.children[0]
47
+ # @type [Parser::AST::Node]
48
+ then_clause = if_node.children[1]
49
+ # @type [Parser::AST::Node]
50
+ else_clause = if_node.children[2]
51
+
52
+ true_ranges = []
53
+ if always_breaks?(else_clause)
54
+ unless enclosing_breakable_pin.nil?
55
+ rest_of_breakable_body = Range.new(get_node_end_position(if_node),
56
+ get_node_end_position(enclosing_breakable_pin.node))
57
+ true_ranges << rest_of_breakable_body
58
+ end
59
+ end
60
+
61
+ unless then_clause.nil?
62
+ #
63
+ # Add specialized locals for the then clause range
64
+ #
65
+ before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
66
+ before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
67
+ true_ranges << Range.new(before_then_clause_pos,
68
+ get_node_end_position(then_clause))
69
+ end
70
+
71
+ process_conditional(conditional_node, true_ranges)
72
+ end
73
+
74
+ class << self
75
+ include Logging
76
+ end
77
+
78
+ # Find a variable pin by name and where it is used.
79
+ #
80
+ # Resolves our most specific view of this variable's type by
81
+ # preferring pins created by flow-sensitive typing when we have
82
+ # them based on the Closure and Location.
83
+ #
84
+ # @param pins [Array<Pin::LocalVariable>]
85
+ # @param name [String]
86
+ # @param closure [Pin::Closure]
87
+ # @param location [Location]
88
+ #
89
+ # @return [Array<Pin::LocalVariable>]
90
+ def self.visible_pins(pins, name, closure, location)
91
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location})" }
92
+ pins_with_name = pins.select { |p| p.name == name }
93
+ if pins_with_name.empty?
94
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => [] - no pins with name" }
95
+ return []
96
+ end
97
+ pins_with_specific_visibility = pins.select { |p| p.name == name && p.presence && p.visible_at?(closure, location) }
98
+ if pins_with_specific_visibility.empty?
99
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_name} - no pins with specific visibility" }
100
+ return pins_with_name
101
+ end
102
+ visible_pins_specific_to_this_closure = pins_with_specific_visibility.select { |p| p.closure == closure }
103
+ if visible_pins_specific_to_this_closure.empty?
104
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{pins_with_specific_visibility} - no visible pins specific to this closure (#{closure})}" }
105
+ return pins_with_specific_visibility
106
+ end
107
+ flow_defined_pins = pins_with_specific_visibility.select { |p| p.presence_certain? }
108
+ if flow_defined_pins.empty?
109
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{visible_pins_specific_to_this_closure} - no flow-defined pins" }
110
+ return visible_pins_specific_to_this_closure
111
+ end
112
+
113
+ logger.debug { "FlowSensitiveTyping#visible_pins(name=#{name}, closure=#{closure}, location=#{location}) => #{flow_defined_pins}" }
114
+
115
+ flow_defined_pins
116
+ end
117
+
118
+ include Logging
119
+
120
+ private
121
+
122
+ # @param pin [Pin::LocalVariable]
123
+ # @param downcast_type_name [String]
124
+ # @param presence [Range]
125
+ #
126
+ # @return [void]
127
+ def add_downcast_local(pin, downcast_type_name, presence)
128
+ # @todo Create pin#update method
129
+ new_pin = Solargraph::Pin::LocalVariable.new(
130
+ location: pin.location,
131
+ closure: pin.closure,
132
+ name: pin.name,
133
+ assignment: pin.assignment,
134
+ comments: pin.comments,
135
+ presence: presence,
136
+ return_type: ComplexType.try_parse(downcast_type_name),
137
+ presence_certain: true,
138
+ source: :flow_sensitive_typing
139
+ )
140
+ locals.push(new_pin)
141
+ end
142
+
143
+ # @param facts_by_pin [Hash{Pin::LocalVariable => Array<Hash{Symbol => String}>}]
144
+ # @param presences [Array<Range>]
145
+ #
146
+ # @return [void]
147
+ def process_facts(facts_by_pin, presences)
148
+ #
149
+ # Add specialized locals for the rest of the block
150
+ #
151
+ facts_by_pin.each_pair do |pin, facts|
152
+ facts.each do |fact|
153
+ downcast_type_name = fact.fetch(:type)
154
+ presences.each do |presence|
155
+ add_downcast_local(pin, downcast_type_name, presence)
156
+ end
157
+ end
158
+ end
159
+ end
160
+
161
+ # @param conditional_node [Parser::AST::Node]
162
+ # @param true_ranges [Array<Range>]
163
+ #
164
+ # @return [void]
165
+ def process_conditional(conditional_node, true_ranges)
166
+ if conditional_node.type == :send
167
+ process_isa(conditional_node, true_ranges)
168
+ elsif conditional_node.type == :and
169
+ process_and(conditional_node, true_ranges)
170
+ end
171
+ end
172
+
173
+ # @param isa_node [Parser::AST::Node]
174
+ # @return [Array(String, String)]
175
+ def parse_isa(isa_node)
176
+ return unless isa_node&.type == :send && isa_node.children[1] == :is_a?
177
+ # Check if conditional node follows this pattern:
178
+ # s(:send,
179
+ # s(:send, nil, :foo), :is_a?,
180
+ # s(:const, nil, :Baz)),
181
+ isa_receiver = isa_node.children[0]
182
+ isa_type_name = type_name(isa_node.children[2])
183
+ return unless isa_type_name
184
+
185
+ # check if isa_receiver looks like this:
186
+ # s(:send, nil, :foo)
187
+ # and set variable_name to :foo
188
+ if isa_receiver&.type == :send && isa_receiver.children[0].nil? && isa_receiver.children[1].is_a?(Symbol)
189
+ variable_name = isa_receiver.children[1].to_s
190
+ end
191
+ # or like this:
192
+ # (lvar :repr)
193
+ variable_name = isa_receiver.children[0].to_s if isa_receiver&.type == :lvar
194
+ return unless variable_name
195
+
196
+ [isa_type_name, variable_name]
197
+ end
198
+
199
+ # @param variable_name [String]
200
+ # @param position [Position]
201
+ #
202
+ # @return [Solargraph::Pin::LocalVariable, nil]
203
+ def find_local(variable_name, position)
204
+ pins = locals.select { |pin| pin.name == variable_name && pin.presence.include?(position) }
205
+ return unless pins.length == 1
206
+ pins.first
207
+ end
208
+
209
+ # @param isa_node [Parser::AST::Node]
210
+ # @param true_presences [Array<Range>]
211
+ #
212
+ # @return [void]
213
+ def process_isa(isa_node, true_presences)
214
+ isa_type_name, variable_name = parse_isa(isa_node)
215
+ return if variable_name.nil? || variable_name.empty?
216
+ isa_position = Range.from_node(isa_node).start
217
+
218
+ pin = find_local(variable_name, isa_position)
219
+ return unless pin
220
+
221
+ if_true = {}
222
+ if_true[pin] ||= []
223
+ if_true[pin] << { type: isa_type_name }
224
+ process_facts(if_true, true_presences)
225
+ end
226
+
227
+ # @param node [Parser::AST::Node]
228
+ #
229
+ # @return [String, nil]
230
+ def type_name(node)
231
+ # e.g.,
232
+ # s(:const, nil, :Baz)
233
+ return unless node&.type == :const
234
+ module_node = node.children[0]
235
+ class_node = node.children[1]
236
+
237
+ return class_node.to_s if module_node.nil?
238
+
239
+ module_type_name = type_name(module_node)
240
+ return unless module_type_name
241
+
242
+ "#{module_type_name}::#{class_node}"
243
+ end
244
+
245
+ # @param clause_node [Parser::AST::Node]
246
+ def always_breaks?(clause_node)
247
+ clause_node&.type == :break
248
+ end
249
+
250
+ attr_reader :locals
251
+
252
+ attr_reader :enclosing_breakable_pin
253
+ end
254
+ end
255
+ end
@@ -1,6 +1,6 @@
1
1
  module Solargraph
2
2
  module Parser
3
- class NodeMethods
3
+ module NodeMethods
4
4
  module_function
5
5
 
6
6
  # @abstract
@@ -74,10 +74,24 @@ module Solargraph
74
74
 
75
75
  # @abstract
76
76
  # @param node [Parser::AST::Node]
77
- # @return [Hash{Parser::AST::Node => Source::Chain}]
77
+ # @return [Hash{Symbol => 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
@@ -13,7 +13,7 @@ module Solargraph
13
13
  # @return [Array<Pin::Base>]
14
14
  attr_reader :pins
15
15
 
16
- # @return [Array<Pin::BaseVariable>]
16
+ # @return [Array<Pin::LocalVariable>]
17
17
  attr_reader :locals
18
18
 
19
19
  # @param node [Parser::AST::Node]
@@ -30,9 +30,12 @@ module Solargraph
30
30
 
31
31
  # Subclasses should override this method to generate new pins.
32
32
  #
33
- # @return [void]
33
+ # @return [Boolean] continue processing the next processor of the same node type.
34
+ # @return [void] In case there is only one processor registered for the node type, it can be void.
34
35
  def process
35
36
  process_children
37
+
38
+ true
36
39
  end
37
40
 
38
41
  private
@@ -64,7 +67,9 @@ module Solargraph
64
67
  # @param position [Solargraph::Position]
65
68
  # @return [Pin::Closure, nil]
66
69
  def named_path_pin position
67
- pins.select{|pin| pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)}.last
70
+ pins.select do |pin|
71
+ pin.is_a?(Pin::Closure) && pin.path && !pin.path.empty? && pin.location.range.contain?(position)
72
+ end.last
68
73
  end
69
74
 
70
75
  # @todo Candidate for deprecation
@@ -72,14 +77,14 @@ module Solargraph
72
77
  # @return [Pin::Closure, nil]
73
78
  def block_pin position
74
79
  # @todo determine if this can return a Pin::Block
75
- pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last
80
+ pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last
76
81
  end
77
82
 
78
83
  # @todo Candidate for deprecation
79
84
  # @param position [Solargraph::Position]
80
85
  # @return [Pin::Closure, nil]
81
86
  def closure_pin position
82
- pins.select{|pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position)}.last
87
+ pins.select { |pin| pin.is_a?(Pin::Closure) && pin.location.range.contain?(position) }.last
83
88
  end
84
89
  end
85
90
  end
@@ -9,16 +9,26 @@ 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 => Array<Class<NodeProcessor::Base>>}]
13
13
  @@processors ||= {}
14
14
 
15
- # Register a processor for a node type.
15
+ # Register a processor for a node type. You can register multiple processors for the same type.
16
+ # If a node processor returns true, it will skip the next processor of the same node type.
16
17
  #
17
18
  # @param type [Symbol]
18
19
  # @param cls [Class<NodeProcessor::Base>]
19
- # @return [Class<NodeProcessor::Base>]
20
+ # @return [Array<Class<NodeProcessor::Base>>]
20
21
  def register type, cls
21
- @@processors[type] = cls
22
+ @@processors[type] ||= []
23
+ @@processors[type] << cls
24
+ end
25
+
26
+ # @param type [Symbol]
27
+ # @param cls [Class<NodeProcessor::Base>]
28
+ #
29
+ # @return [void]
30
+ def deregister type, cls
31
+ @@processors[type].delete(cls)
22
32
  end
23
33
  end
24
34
 
@@ -31,14 +41,21 @@ module Solargraph
31
41
  if pins.empty?
32
42
  pins.push Pin::Namespace.new(
33
43
  location: region.source.location,
34
- name: ''
44
+ name: '',
45
+ source: :parser,
35
46
  )
36
47
  end
37
48
  return [pins, locals] unless Parser.is_ast_node?(node)
38
- klass = @@processors[node.type] || NodeProcessor::Base
39
- processor = klass.new(node, region, pins, locals)
40
- processor.process
41
- [processor.pins, processor.locals]
49
+ node_processor_classes = @@processors[node.type] || [NodeProcessor::Base]
50
+
51
+ node_processor_classes.each do |klass|
52
+ processor = klass.new(node, region, pins, locals)
53
+ process_next = processor.process
54
+
55
+ break unless process_next
56
+ end
57
+
58
+ [pins, locals]
42
59
  end
43
60
  end
44
61
  end
@@ -1,7 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parser/current'
4
- require 'parser/source/buffer'
3
+ require 'prism'
4
+
5
+ # Awaiting ability to use a version containing https://github.com/whitequark/parser/pull/1076
6
+ #
7
+ # @!parse
8
+ # class ::Parser::Base < ::Parser::Builder
9
+ # # @return [Integer]
10
+ # def version; end
11
+ # end
12
+ # class ::Parser::CurrentRuby < ::Parser::Base; end
5
13
 
6
14
  module Solargraph
7
15
  module Parser
@@ -9,15 +17,11 @@ module Solargraph
9
17
  module ClassMethods
10
18
  # @param code [String]
11
19
  # @param filename [String, nil]
12
- # @return [Array(Parser::AST::Node, Hash{Integer => String})]
20
+ # @return [Array(Parser::AST::Node, Hash{Integer => Solargraph::Parser::Snippet})]
13
21
  def parse_with_comments code, filename = nil
14
- buffer = ::Parser::Source::Buffer.new(filename, 0)
15
- buffer.source = code
16
- node = parser.parse(buffer)
22
+ node = parse(code, filename)
17
23
  comments = CommentRipper.new(code, filename, 0).parse
18
24
  [node, comments]
19
- rescue ::Parser::SyntaxError => e
20
- raise Parser::SyntaxError, e.message
21
25
  end
22
26
 
23
27
  # @param code [String]
@@ -28,18 +32,16 @@ module Solargraph
28
32
  buffer = ::Parser::Source::Buffer.new(filename, line)
29
33
  buffer.source = code
30
34
  parser.parse(buffer)
31
- rescue ::Parser::SyntaxError => e
35
+ rescue ::Parser::SyntaxError, ::Parser::UnknownEncodingInMagicComment => e
32
36
  raise Parser::SyntaxError, e.message
33
37
  end
34
38
 
35
39
  # @return [::Parser::Base]
36
40
  def parser
37
- # @todo Consider setting an instance variable. We might not need to
38
- # recreate the parser every time we use it.
39
- parser = ::Parser::CurrentRuby.new(FlawedBuilder.new)
40
- parser.diagnostics.all_errors_are_fatal = true
41
- parser.diagnostics.ignore_warnings = true
42
- parser
41
+ @parser ||= Prism::Translation::Parser.new(FlawedBuilder.new).tap do |parser|
42
+ parser.diagnostics.all_errors_are_fatal = true
43
+ parser.diagnostics.ignore_warnings = true
44
+ end
43
45
  end
44
46
 
45
47
  # @param source [Source]
@@ -9,6 +9,7 @@ module Solargraph
9
9
  class FlawedBuilder < ::Parser::Builders::Default
10
10
  # @param token [::Parser::AST::Node]
11
11
  # @return [String]
12
+ # @sg-ignore
12
13
  def string_value(token)
13
14
  value(token)
14
15
  end
@@ -7,6 +7,7 @@ module Solargraph
7
7
  #
8
8
  class NodeChainer
9
9
  include NodeMethods
10
+
10
11
  Chain = Source::Chain
11
12
 
12
13
  # @param node [Parser::AST::Node]
@@ -57,22 +58,22 @@ module Solargraph
57
58
  elsif n.type == :send
58
59
  if n.children[0].is_a?(::Parser::AST::Node)
59
60
  result.concat generate_links(n.children[0])
60
- result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
61
+ result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n))
61
62
  elsif n.children[0].nil?
62
63
  args = []
63
64
  n.children[2..-1].each do |c|
64
65
  args.push NodeChainer.chain(c, @filename, n)
65
66
  end
66
- result.push Chain::Call.new(n.children[1].to_s, node_args(n), passed_block(n))
67
+ result.push Chain::Call.new(n.children[1].to_s, Location.from_node(n), node_args(n), passed_block(n))
67
68
  else
68
69
  raise "No idea what to do with #{n}"
69
70
  end
70
71
  elsif n.type == :csend
71
72
  if n.children[0].is_a?(::Parser::AST::Node)
72
73
  result.concat generate_links(n.children[0])
73
- result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
74
+ result.push Chain::QCall.new(n.children[1].to_s, Location.from_node(n), node_args(n))
74
75
  elsif n.children[0].nil?
75
- result.push Chain::QCall.new(n.children[1].to_s, node_args(n))
76
+ result.push Chain::QCall.new(n.children[1].to_s, Location.from_node(n), node_args(n))
76
77
  else
77
78
  raise "No idea what to do with #{n}"
78
79
  end
@@ -82,15 +83,15 @@ module Solargraph
82
83
  result.push Chain::ZSuper.new('super')
83
84
  elsif n.type == :super
84
85
  args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
85
- result.push Chain::Call.new('super', args)
86
+ result.push Chain::Call.new('super', Location.from_node(n), args)
86
87
  elsif n.type == :yield
87
88
  args = n.children.map { |c| NodeChainer.chain(c, @filename, n) }
88
- result.push Chain::Call.new('yield', args)
89
+ result.push Chain::Call.new('yield', Location.from_node(n), args)
89
90
  elsif n.type == :const
90
91
  const = unpack_name(n)
91
92
  result.push Chain::Constant.new(const)
92
93
  elsif [:lvar, :lvasgn].include?(n.type)
93
- result.push Chain::Call.new(n.children[0].to_s)
94
+ result.push Chain::Call.new(n.children[0].to_s, Location.from_node(n))
94
95
  elsif [:ivar, :ivasgn].include?(n.type)
95
96
  result.push Chain::InstanceVariable.new(n.children[0].to_s)
96
97
  elsif [:cvar, :cvasgn].include?(n.type)
@@ -98,7 +99,8 @@ module Solargraph
98
99
  elsif [:gvar, :gvasgn].include?(n.type)
99
100
  result.push Chain::GlobalVariable.new(n.children[0].to_s)
100
101
  elsif n.type == :or_asgn
101
- result.concat generate_links n.children[1]
102
+ new_node = n.updated(n.children[0].type, n.children[0].children + [n.children[1]])
103
+ result.concat generate_links new_node
102
104
  elsif [:class, :module, :def, :defs].include?(n.type)
103
105
  # @todo Undefined or what?
104
106
  result.push Chain::UNDEFINED_CALL
@@ -124,13 +126,13 @@ module Solargraph
124
126
  end
125
127
  end
126
128
  elsif n.type == :hash
127
- result.push Chain::Hash.new('::Hash', hash_is_splatted?(n))
129
+ result.push Chain::Hash.new('::Hash', n, hash_is_splatted?(n))
128
130
  elsif n.type == :array
129
131
  chained_children = n.children.map { |c| NodeChainer.chain(c) }
130
- result.push Source::Chain::Array.new(chained_children)
132
+ result.push Source::Chain::Array.new(chained_children, n)
131
133
  else
132
134
  lit = infer_literal_node_type(n)
133
- result.push (lit ? Chain::Literal.new(lit) : Chain::Link.new)
135
+ result.push (lit ? Chain::Literal.new(lit, n) : Chain::Link.new)
134
136
  end
135
137
  result
136
138
  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
@@ -118,7 +120,7 @@ module Solargraph
118
120
  end
119
121
 
120
122
  # @param node [Parser::AST::Node]
121
- # @return [Hash{Parser::AST::Node => Chain}]
123
+ # @return [Hash{Symbol => Chain}]
122
124
  def convert_hash node
123
125
  return {} unless Parser.is_ast_node?(node)
124
126
  return convert_hash(node.children[0]) if node.type == :kwsplat
@@ -177,6 +179,7 @@ module Solargraph
177
179
  node.children[1..-1].each { |child| result.concat call_nodes_from(child) }
178
180
  elsif node.type == :send
179
181
  result.push node
182
+ result.concat call_nodes_from(node.children.first)
180
183
  node.children[2..-1].each { |child| result.concat call_nodes_from(child) }
181
184
  elsif [:super, :zsuper].include?(node.type)
182
185
  result.push node
@@ -230,6 +233,7 @@ module Solargraph
230
233
  else
231
234
  source.tree_at(position.line, position.column - 1)
232
235
  end
236
+ # @type [AST::Node, nil]
233
237
  prev = nil
234
238
  tree.each do |node|
235
239
  if node.type == :send
@@ -240,7 +244,7 @@ module Solargraph
240
244
  if source.synchronized?
241
245
  return node if source.code[0..offset-1] =~ /\(\s*\z/ && source.code[offset..-1] =~ /^\s*\)/
242
246
  else
243
- return node if source.code[0..offset-1] =~ /\([^\(]*\z/
247
+ return node if source.code[0..offset-1] =~ /\([^(]*\z/
244
248
  end
245
249
  end
246
250
  end
@@ -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