solargraph 0.54.0 → 0.58.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 (200) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/linting.yml +127 -0
  3. data/.github/workflows/plugins.yml +184 -6
  4. data/.github/workflows/rspec.yml +55 -5
  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 +1279 -0
  11. data/.yardopts +1 -0
  12. data/CHANGELOG.md +171 -0
  13. data/README.md +20 -6
  14. data/Rakefile +125 -13
  15. data/bin/solargraph +8 -5
  16. data/lib/solargraph/api_map/cache.rb +13 -3
  17. data/lib/solargraph/api_map/constants.rb +279 -0
  18. data/lib/solargraph/api_map/index.rb +193 -0
  19. data/lib/solargraph/api_map/source_to_yard.rb +13 -4
  20. data/lib/solargraph/api_map/store.rb +207 -132
  21. data/lib/solargraph/api_map.rb +394 -261
  22. data/lib/solargraph/bench.rb +18 -1
  23. data/lib/solargraph/complex_type/type_methods.rb +29 -12
  24. data/lib/solargraph/complex_type/unique_type.rb +205 -26
  25. data/lib/solargraph/complex_type.rb +126 -26
  26. data/lib/solargraph/convention/active_support_concern.rb +111 -0
  27. data/lib/solargraph/convention/base.rb +20 -3
  28. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -0
  29. data/lib/solargraph/convention/data_definition/data_definition_node.rb +91 -0
  30. data/lib/solargraph/convention/data_definition.rb +105 -0
  31. data/lib/solargraph/convention/gemspec.rb +3 -2
  32. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +61 -0
  33. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +102 -0
  34. data/lib/solargraph/convention/struct_definition.rb +164 -0
  35. data/lib/solargraph/convention.rb +36 -4
  36. data/lib/solargraph/diagnostics/rubocop.rb +6 -1
  37. data/lib/solargraph/diagnostics/rubocop_helpers.rb +5 -3
  38. data/lib/solargraph/doc_map.rb +316 -64
  39. data/lib/solargraph/environ.rb +9 -2
  40. data/lib/solargraph/equality.rb +34 -0
  41. data/lib/solargraph/gem_pins.rb +64 -38
  42. data/lib/solargraph/language_server/host/dispatch.rb +2 -0
  43. data/lib/solargraph/language_server/host/message_worker.rb +54 -5
  44. data/lib/solargraph/language_server/host.rb +36 -18
  45. data/lib/solargraph/language_server/message/base.rb +20 -12
  46. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +2 -0
  47. data/lib/solargraph/language_server/message/extended/document.rb +5 -2
  48. data/lib/solargraph/language_server/message/extended/document_gems.rb +3 -3
  49. data/lib/solargraph/language_server/message/initialize.rb +3 -1
  50. data/lib/solargraph/language_server/message/text_document/completion.rb +0 -3
  51. data/lib/solargraph/language_server/message/text_document/definition.rb +5 -3
  52. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +3 -3
  53. data/lib/solargraph/language_server/message/text_document/formatting.rb +23 -2
  54. data/lib/solargraph/language_server/message/text_document/hover.rb +1 -1
  55. data/lib/solargraph/language_server/message/text_document/type_definition.rb +4 -3
  56. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +2 -0
  57. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +2 -2
  58. data/lib/solargraph/language_server/progress.rb +27 -2
  59. data/lib/solargraph/language_server/request.rb +4 -1
  60. data/lib/solargraph/library.rb +83 -73
  61. data/lib/solargraph/location.rb +45 -1
  62. data/lib/solargraph/logging.rb +12 -2
  63. data/lib/solargraph/page.rb +3 -0
  64. data/lib/solargraph/parser/comment_ripper.rb +20 -7
  65. data/lib/solargraph/parser/flow_sensitive_typing.rb +255 -0
  66. data/lib/solargraph/parser/node_processor/base.rb +10 -5
  67. data/lib/solargraph/parser/node_processor.rb +26 -8
  68. data/lib/solargraph/parser/parser_gem/class_methods.rb +10 -18
  69. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +1 -0
  70. data/lib/solargraph/parser/parser_gem/node_chainer.rb +13 -11
  71. data/lib/solargraph/parser/parser_gem/node_methods.rb +10 -19
  72. data/lib/solargraph/parser/parser_gem/node_processors/alias_node.rb +2 -1
  73. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -0
  74. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +26 -20
  75. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +7 -4
  76. data/lib/solargraph/parser/parser_gem/node_processors/casgn_node.rb +2 -1
  77. data/lib/solargraph/parser/parser_gem/node_processors/cvasgn_node.rb +2 -1
  78. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +6 -3
  79. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +2 -1
  80. data/lib/solargraph/parser/parser_gem/node_processors/gvasgn_node.rb +2 -1
  81. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +23 -0
  82. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +4 -2
  83. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +2 -1
  84. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +14 -2
  85. data/lib/solargraph/parser/parser_gem/node_processors/namespace_node.rb +8 -7
  86. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -0
  87. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +1 -0
  88. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +3 -1
  89. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +16 -6
  90. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +64 -32
  91. data/lib/solargraph/parser/parser_gem/node_processors/sym_node.rb +3 -1
  92. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -0
  93. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +29 -0
  94. data/lib/solargraph/parser/parser_gem/node_processors.rb +14 -0
  95. data/lib/solargraph/parser/region.rb +4 -1
  96. data/lib/solargraph/parser/snippet.rb +2 -0
  97. data/lib/solargraph/parser.rb +3 -5
  98. data/lib/solargraph/pin/base.rb +417 -42
  99. data/lib/solargraph/pin/base_variable.rb +21 -12
  100. data/lib/solargraph/pin/block.rb +9 -26
  101. data/lib/solargraph/pin/breakable.rb +9 -0
  102. data/lib/solargraph/pin/callable.rb +231 -0
  103. data/lib/solargraph/pin/closure.rb +30 -10
  104. data/lib/solargraph/pin/common.rb +12 -7
  105. data/lib/solargraph/pin/constant.rb +2 -0
  106. data/lib/solargraph/pin/conversions.rb +3 -2
  107. data/lib/solargraph/pin/delegated_method.rb +20 -1
  108. data/lib/solargraph/pin/documenting.rb +16 -0
  109. data/lib/solargraph/pin/instance_variable.rb +2 -2
  110. data/lib/solargraph/pin/keyword.rb +7 -2
  111. data/lib/solargraph/pin/local_variable.rb +15 -7
  112. data/lib/solargraph/pin/method.rb +241 -70
  113. data/lib/solargraph/pin/method_alias.rb +3 -0
  114. data/lib/solargraph/pin/namespace.rb +21 -13
  115. data/lib/solargraph/pin/parameter.rb +94 -32
  116. data/lib/solargraph/pin/proxy_type.rb +17 -7
  117. data/lib/solargraph/pin/reference/override.rb +24 -6
  118. data/lib/solargraph/pin/reference/require.rb +2 -2
  119. data/lib/solargraph/pin/reference/superclass.rb +5 -0
  120. data/lib/solargraph/pin/reference.rb +17 -0
  121. data/lib/solargraph/pin/search.rb +6 -1
  122. data/lib/solargraph/pin/signature.rb +39 -121
  123. data/lib/solargraph/pin/singleton.rb +1 -1
  124. data/lib/solargraph/pin/symbol.rb +8 -2
  125. data/lib/solargraph/pin/until.rb +18 -0
  126. data/lib/solargraph/pin/while.rb +18 -0
  127. data/lib/solargraph/pin.rb +8 -2
  128. data/lib/solargraph/pin_cache.rb +245 -0
  129. data/lib/solargraph/position.rb +19 -0
  130. data/lib/solargraph/range.rb +23 -4
  131. data/lib/solargraph/rbs_map/conversions.rb +315 -99
  132. data/lib/solargraph/rbs_map/core_fills.rb +50 -16
  133. data/lib/solargraph/rbs_map/core_map.rb +41 -11
  134. data/lib/solargraph/rbs_map/stdlib_map.rb +15 -5
  135. data/lib/solargraph/rbs_map.rb +87 -16
  136. data/lib/solargraph/shell.rb +117 -17
  137. data/lib/solargraph/source/chain/array.rb +13 -8
  138. data/lib/solargraph/source/chain/block_symbol.rb +1 -1
  139. data/lib/solargraph/source/chain/block_variable.rb +1 -1
  140. data/lib/solargraph/source/chain/call.rb +135 -66
  141. data/lib/solargraph/source/chain/constant.rb +3 -66
  142. data/lib/solargraph/source/chain/hash.rb +9 -3
  143. data/lib/solargraph/source/chain/head.rb +1 -1
  144. data/lib/solargraph/source/chain/if.rb +7 -2
  145. data/lib/solargraph/source/chain/link.rb +38 -6
  146. data/lib/solargraph/source/chain/literal.rb +27 -2
  147. data/lib/solargraph/source/chain/or.rb +2 -2
  148. data/lib/solargraph/source/chain/z_super.rb +1 -1
  149. data/lib/solargraph/source/chain.rb +140 -63
  150. data/lib/solargraph/source/change.rb +2 -2
  151. data/lib/solargraph/source/cursor.rb +4 -4
  152. data/lib/solargraph/source/source_chainer.rb +3 -3
  153. data/lib/solargraph/source.rb +110 -89
  154. data/lib/solargraph/source_map/clip.rb +22 -28
  155. data/lib/solargraph/source_map/data.rb +34 -0
  156. data/lib/solargraph/source_map/mapper.rb +11 -7
  157. data/lib/solargraph/source_map.rb +50 -43
  158. data/lib/solargraph/type_checker/checks.rb +4 -0
  159. data/lib/solargraph/type_checker/param_def.rb +2 -0
  160. data/lib/solargraph/type_checker/rules.rb +35 -8
  161. data/lib/solargraph/type_checker.rb +331 -189
  162. data/lib/solargraph/version.rb +1 -1
  163. data/lib/solargraph/views/_method.erb +10 -10
  164. data/lib/solargraph/views/_namespace.erb +3 -3
  165. data/lib/solargraph/views/document.erb +10 -10
  166. data/lib/solargraph/views/environment.erb +3 -5
  167. data/lib/solargraph/workspace/config.rb +25 -5
  168. data/lib/solargraph/workspace/require_paths.rb +97 -0
  169. data/lib/solargraph/workspace.rb +53 -72
  170. data/lib/solargraph/yard_map/helpers.rb +29 -1
  171. data/lib/solargraph/yard_map/mapper/to_constant.rb +8 -5
  172. data/lib/solargraph/yard_map/mapper/to_method.rb +55 -19
  173. data/lib/solargraph/yard_map/mapper/to_namespace.rb +11 -7
  174. data/lib/solargraph/yard_map/mapper.rb +5 -3
  175. data/lib/solargraph/yard_map/to_method.rb +6 -3
  176. data/lib/solargraph/yardoc.rb +45 -10
  177. data/lib/solargraph.rb +35 -1
  178. data/rbs/fills/bundler/0/bundler.rbs +4271 -0
  179. data/rbs/fills/open3/0/open3.rbs +172 -0
  180. data/rbs/fills/rubygems/0/basic_specification.rbs +326 -0
  181. data/rbs/fills/rubygems/0/errors.rbs +364 -0
  182. data/rbs/fills/rubygems/0/spec_fetcher.rbs +107 -0
  183. data/rbs/fills/rubygems/0/specification.rbs +1753 -0
  184. data/rbs/fills/tuple/tuple.rbs +149 -0
  185. data/rbs_collection.yaml +19 -0
  186. data/sig/shims/ast/0/node.rbs +5 -0
  187. data/sig/shims/ast/2.4/.rbs_meta.yaml +9 -0
  188. data/sig/shims/ast/2.4/ast.rbs +73 -0
  189. data/sig/shims/parser/3.2.0.1/builders/default.rbs +195 -0
  190. data/sig/shims/parser/3.2.0.1/manifest.yaml +7 -0
  191. data/sig/shims/parser/3.2.0.1/parser.rbs +201 -0
  192. data/sig/shims/parser/3.2.0.1/polyfill.rbs +4 -0
  193. data/sig/shims/thor/1.2.0.1/.rbs_meta.yaml +9 -0
  194. data/sig/shims/thor/1.2.0.1/manifest.yaml +7 -0
  195. data/sig/shims/thor/1.2.0.1/thor.rbs +17 -0
  196. data/solargraph.gemspec +32 -10
  197. metadata +237 -37
  198. data/lib/.rubocop.yml +0 -22
  199. data/lib/solargraph/cache.rb +0 -77
  200. data/lib/solargraph/parser/node_methods.rb +0 -83
@@ -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), nil]
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
@@ -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,15 +9,26 @@ module Solargraph
9
9
  autoload :Base, 'solargraph/parser/node_processor/base'
10
10
 
11
11
  class << self
12
+ # @type [Hash{Symbol => Array<Class<NodeProcessor::Base>>}]
12
13
  @@processors ||= {}
13
14
 
14
- # 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.
15
17
  #
16
18
  # @param type [Symbol]
17
19
  # @param cls [Class<NodeProcessor::Base>]
18
- # @return [Class<NodeProcessor::Base>]
20
+ # @return [Array<Class<NodeProcessor::Base>>]
19
21
  def register type, cls
20
- @@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)
21
32
  end
22
33
  end
23
34
 
@@ -30,14 +41,21 @@ module Solargraph
30
41
  if pins.empty?
31
42
  pins.push Pin::Namespace.new(
32
43
  location: region.source.location,
33
- name: ''
44
+ name: '',
45
+ source: :parser,
34
46
  )
35
47
  end
36
48
  return [pins, locals] unless Parser.is_ast_node?(node)
37
- klass = @@processors[node.type] || NodeProcessor::Base
38
- processor = klass.new(node, region, pins, locals)
39
- processor.process
40
- [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]
41
59
  end
42
60
  end
43
61
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'parser/current'
4
- require 'parser/source/buffer'
3
+ require 'prism'
5
4
 
6
5
  module Solargraph
7
6
  module Parser
@@ -9,15 +8,11 @@ module Solargraph
9
8
  module ClassMethods
10
9
  # @param code [String]
11
10
  # @param filename [String, nil]
12
- # @return [Array(Parser::AST::Node, Hash{Integer => String})]
11
+ # @return [Array(Parser::AST::Node, Hash{Integer => Solargraph::Parser::Snippet})]
13
12
  def parse_with_comments code, filename = nil
14
- buffer = ::Parser::Source::Buffer.new(filename, 0)
15
- buffer.source = code
16
- node = parser.parse(buffer)
13
+ node = parse(code, filename)
17
14
  comments = CommentRipper.new(code, filename, 0).parse
18
15
  [node, comments]
19
- rescue ::Parser::SyntaxError => e
20
- raise Parser::SyntaxError, e.message
21
16
  end
22
17
 
23
18
  # @param code [String]
@@ -28,18 +23,16 @@ module Solargraph
28
23
  buffer = ::Parser::Source::Buffer.new(filename, line)
29
24
  buffer.source = code
30
25
  parser.parse(buffer)
31
- rescue ::Parser::SyntaxError => e
26
+ rescue ::Parser::SyntaxError, ::Parser::UnknownEncodingInMagicComment => e
32
27
  raise Parser::SyntaxError, e.message
33
28
  end
34
29
 
35
30
  # @return [::Parser::Base]
36
31
  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
32
+ @parser ||= Prism::Translation::Parser.new(FlawedBuilder.new).tap do |parser|
33
+ parser.diagnostics.all_errors_are_fatal = true
34
+ parser.diagnostics.ignore_warnings = true
35
+ end
43
36
  end
44
37
 
45
38
  # @param source [Source]
@@ -82,6 +75,7 @@ module Solargraph
82
75
  # @param top [AST::Node]
83
76
  # @return [Array<AST::Node>]
84
77
  def inner_node_references name, top
78
+ # @type [Array<AST::Node>]
85
79
  result = []
86
80
  if top.is_a?(AST::Node) && top.to_s.include?(":#{name}")
87
81
  result.push top if top.children.any? { |c| c.to_s == name }
@@ -135,9 +129,7 @@ module Solargraph
135
129
  def string_ranges node
136
130
  return [] unless is_ast_node?(node)
137
131
  result = []
138
- if node.type == :str
139
- result.push Range.from_node(node)
140
- end
132
+ result.push Range.from_node(node) if node.type == :str
141
133
  node.children.each do |child|
142
134
  result.concat string_ranges(child)
143
135
  end
@@ -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
@@ -3,20 +3,6 @@
3
3
  require 'parser'
4
4
  require 'ast'
5
5
 
6
- # Teach AST::Node#children about its generic type
7
- #
8
- # @todo contribute back to https://github.com/ruby/gem_rbs_collection/blob/main/gems/ast/2.4/ast.rbs
9
- #
10
- # @!parse
11
- # module ::AST
12
- # class Node
13
- # # New children
14
- #
15
- # # @return [Array<AST::Node>]
16
- # attr_reader :children
17
- # end
18
- # end
19
-
20
6
  # https://github.com/whitequark/parser
21
7
  module Solargraph
22
8
  module Parser
@@ -40,7 +26,7 @@ module Solargraph
40
26
  if n.is_a?(AST::Node)
41
27
  if n.type == :cbase
42
28
  parts = [''] + pack_name(n)
43
- else
29
+ elsif n.type == :const
44
30
  parts += pack_name(n)
45
31
  end
46
32
  else
@@ -59,6 +45,8 @@ module Solargraph
59
45
  return '::String'
60
46
  elsif node.type == :array
61
47
  return '::Array'
48
+ elsif node.type == :nil
49
+ return '::NilClass'
62
50
  elsif node.type == :hash
63
51
  return '::Hash'
64
52
  elsif node.type == :int
@@ -118,7 +106,7 @@ module Solargraph
118
106
  end
119
107
 
120
108
  # @param node [Parser::AST::Node]
121
- # @return [Hash{Parser::AST::Node => Chain}]
109
+ # @return [Hash{Symbol => Chain}]
122
110
  def convert_hash node
123
111
  return {} unless Parser.is_ast_node?(node)
124
112
  return convert_hash(node.children[0]) if node.type == :kwsplat
@@ -131,6 +119,7 @@ module Solargraph
131
119
  result
132
120
  end
133
121
 
122
+ # @sg-ignore Wrong argument type for AST::Node.new: type expected AST::_ToSym, received :nil
134
123
  NIL_NODE = ::Parser::AST::Node.new(:nil)
135
124
 
136
125
  # @param node [Parser::AST::Node]
@@ -177,6 +166,7 @@ module Solargraph
177
166
  node.children[1..-1].each { |child| result.concat call_nodes_from(child) }
178
167
  elsif node.type == :send
179
168
  result.push node
169
+ result.concat call_nodes_from(node.children.first)
180
170
  node.children[2..-1].each { |child| result.concat call_nodes_from(child) }
181
171
  elsif [:super, :zsuper].include?(node.type)
182
172
  result.push node
@@ -230,6 +220,7 @@ module Solargraph
230
220
  else
231
221
  source.tree_at(position.line, position.column - 1)
232
222
  end
223
+ # @type [AST::Node, nil]
233
224
  prev = nil
234
225
  tree.each do |node|
235
226
  if node.type == :send
@@ -240,7 +231,7 @@ module Solargraph
240
231
  if source.synchronized?
241
232
  return node if source.code[0..offset-1] =~ /\(\s*\z/ && source.code[offset..-1] =~ /^\s*\)/
242
233
  else
243
- return node if source.code[0..offset-1] =~ /\([^\(]*\z/
234
+ return node if source.code[0..offset-1] =~ /\([^(]*\z/
244
235
  end
245
236
  end
246
237
  end
@@ -311,7 +302,7 @@ module Solargraph
311
302
  # statements in value positions.
312
303
  module DeepInference
313
304
  class << self
314
- CONDITIONAL_ALL_BUT_FIRST = [:if, :unless, :or_asgn]
305
+ CONDITIONAL_ALL_BUT_FIRST = [:if, :unless]
315
306
  CONDITIONAL_ALL = [:or]
316
307
  ONLY_ONE_CHILD = [:return]
317
308
  FIRST_TWO_CHILDREN = [:rescue]
@@ -462,7 +453,7 @@ module Solargraph
462
453
  result
463
454
  end
464
455
 
465
- # @param nodes [Enumerable<Parser::AST::Node, BaseObject>]
456
+ # @param nodes [Enumerable<Parser::AST::Node, BasicObject>]
466
457
  # @return [Array<Parser::AST::Node, nil>]
467
458
  def reduce_to_value_nodes nodes
468
459
  result = []
@@ -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,22 @@
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
+ # @sg-ignore https://github.com/castwide/solargraph/pull/1114
15
+ enclosing_breakable_pin = pins.select{|pin| pin.is_a?(Pin::Breakable) && pin.location.range.contain?(position)}.last
16
+ FlowSensitiveTyping.new(locals, enclosing_breakable_pin).process_and(node)
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end