solargraph 0.58.0 → 0.59.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 (219) hide show
  1. checksums.yaml +4 -4
  2. data/.envrc +3 -0
  3. data/.gitattributes +2 -0
  4. data/.github/workflows/linting.yml +6 -5
  5. data/.github/workflows/plugins.yml +48 -35
  6. data/.github/workflows/rspec.yml +15 -28
  7. data/.github/workflows/typecheck.yml +3 -2
  8. data/.gitignore +1 -0
  9. data/.rubocop.yml +38 -6
  10. data/.rubocop_todo.yml +53 -966
  11. data/CHANGELOG.md +24 -0
  12. data/Gemfile +3 -1
  13. data/README.md +3 -3
  14. data/Rakefile +26 -23
  15. data/bin/solargraph +9 -8
  16. data/lib/solargraph/api_map/cache.rb +110 -110
  17. data/lib/solargraph/api_map/constants.rb +288 -279
  18. data/lib/solargraph/api_map/index.rb +204 -193
  19. data/lib/solargraph/api_map/source_to_yard.rb +110 -97
  20. data/lib/solargraph/api_map/store.rb +395 -384
  21. data/lib/solargraph/api_map.rb +1029 -945
  22. data/lib/solargraph/bench.rb +44 -45
  23. data/lib/solargraph/complex_type/conformance.rb +176 -0
  24. data/lib/solargraph/complex_type/type_methods.rb +240 -228
  25. data/lib/solargraph/complex_type/unique_type.rb +641 -482
  26. data/lib/solargraph/complex_type.rb +557 -444
  27. data/lib/solargraph/convention/data_definition/data_assignment_node.rb +61 -61
  28. data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
  29. data/lib/solargraph/convention/data_definition.rb +108 -105
  30. data/lib/solargraph/convention/gemfile.rb +15 -15
  31. data/lib/solargraph/convention/gemspec.rb +23 -23
  32. data/lib/solargraph/convention/rakefile.rb +17 -17
  33. data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
  34. data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
  35. data/lib/solargraph/convention/struct_definition.rb +168 -164
  36. data/lib/solargraph/convention.rb +78 -78
  37. data/lib/solargraph/converters/dd.rb +19 -17
  38. data/lib/solargraph/converters/dl.rb +17 -15
  39. data/lib/solargraph/converters/dt.rb +17 -15
  40. data/lib/solargraph/converters/misc.rb +3 -1
  41. data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
  42. data/lib/solargraph/diagnostics/rubocop.rb +119 -118
  43. data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
  44. data/lib/solargraph/diagnostics/type_check.rb +56 -55
  45. data/lib/solargraph/diagnostics/update_errors.rb +37 -41
  46. data/lib/solargraph/doc_map.rb +438 -439
  47. data/lib/solargraph/equality.rb +34 -34
  48. data/lib/solargraph/gem_pins.rb +98 -98
  49. data/lib/solargraph/language_server/error_codes.rb +20 -20
  50. data/lib/solargraph/language_server/host/diagnoser.rb +89 -89
  51. data/lib/solargraph/language_server/host/dispatch.rb +130 -130
  52. data/lib/solargraph/language_server/host/message_worker.rb +113 -112
  53. data/lib/solargraph/language_server/host/sources.rb +100 -99
  54. data/lib/solargraph/language_server/host.rb +886 -878
  55. data/lib/solargraph/language_server/message/base.rb +97 -97
  56. data/lib/solargraph/language_server/message/client/register_capability.rb +13 -15
  57. data/lib/solargraph/language_server/message/completion_item/resolve.rb +58 -60
  58. data/lib/solargraph/language_server/message/extended/check_gem_version.rb +108 -114
  59. data/lib/solargraph/language_server/message/extended/document.rb +24 -23
  60. data/lib/solargraph/language_server/message/extended/document_gems.rb +32 -32
  61. data/lib/solargraph/language_server/message/extended/download_core.rb +20 -19
  62. data/lib/solargraph/language_server/message/extended/search.rb +20 -20
  63. data/lib/solargraph/language_server/message/initialize.rb +197 -191
  64. data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
  65. data/lib/solargraph/language_server/message/text_document/definition.rb +49 -40
  66. data/lib/solargraph/language_server/message/text_document/document_highlight.rb +23 -16
  67. data/lib/solargraph/language_server/message/text_document/document_symbol.rb +36 -26
  68. data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
  69. data/lib/solargraph/language_server/message/text_document/hover.rb +58 -58
  70. data/lib/solargraph/language_server/message/text_document/prepare_rename.rb +18 -11
  71. data/lib/solargraph/language_server/message/text_document/references.rb +23 -16
  72. data/lib/solargraph/language_server/message/text_document/rename.rb +26 -19
  73. data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
  74. data/lib/solargraph/language_server/message/text_document/type_definition.rb +33 -25
  75. data/lib/solargraph/language_server/message/workspace/did_change_configuration.rb +41 -35
  76. data/lib/solargraph/language_server/message/workspace/did_change_watched_files.rb +48 -40
  77. data/lib/solargraph/language_server/message/workspace/did_change_workspace_folders.rb +32 -26
  78. data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +33 -23
  79. data/lib/solargraph/language_server/message.rb +94 -94
  80. data/lib/solargraph/language_server/request.rb +29 -27
  81. data/lib/solargraph/language_server/transport/data_reader.rb +72 -74
  82. data/lib/solargraph/language_server/uri_helpers.rb +49 -49
  83. data/lib/solargraph/library.rb +702 -683
  84. data/lib/solargraph/location.rb +85 -82
  85. data/lib/solargraph/logging.rb +55 -37
  86. data/lib/solargraph/page.rb +92 -92
  87. data/lib/solargraph/parser/comment_ripper.rb +84 -69
  88. data/lib/solargraph/parser/flow_sensitive_typing.rb +471 -255
  89. data/lib/solargraph/parser/node_processor/base.rb +122 -92
  90. data/lib/solargraph/parser/node_processor.rb +63 -62
  91. data/lib/solargraph/parser/parser_gem/class_methods.rb +165 -149
  92. data/lib/solargraph/parser/parser_gem/flawed_builder.rb +19 -19
  93. data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
  94. data/lib/solargraph/parser/parser_gem/node_methods.rb +507 -486
  95. data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
  96. data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
  97. data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
  98. data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +45 -46
  99. data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
  100. data/lib/solargraph/parser/parser_gem/node_processors/defs_node.rb +38 -37
  101. data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
  102. data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +42 -40
  103. data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
  104. data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
  105. data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
  106. data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
  107. data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
  108. data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
  109. data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +51 -52
  110. data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +302 -291
  111. data/lib/solargraph/parser/parser_gem/node_processors/until_node.rb +29 -29
  112. data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
  113. data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
  114. data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
  115. data/lib/solargraph/parser/parser_gem.rb +14 -12
  116. data/lib/solargraph/parser/region.rb +75 -69
  117. data/lib/solargraph/parser/snippet.rb +19 -17
  118. data/lib/solargraph/parser.rb +25 -23
  119. data/lib/solargraph/pin/base.rb +773 -729
  120. data/lib/solargraph/pin/base_variable.rb +375 -126
  121. data/lib/solargraph/pin/block.rb +127 -104
  122. data/lib/solargraph/pin/breakable.rb +15 -9
  123. data/lib/solargraph/pin/callable.rb +275 -231
  124. data/lib/solargraph/pin/closure.rb +69 -72
  125. data/lib/solargraph/pin/common.rb +97 -79
  126. data/lib/solargraph/pin/compound_statement.rb +55 -0
  127. data/lib/solargraph/pin/constant.rb +43 -45
  128. data/lib/solargraph/pin/conversions.rb +129 -123
  129. data/lib/solargraph/pin/delegated_method.rb +131 -120
  130. data/lib/solargraph/pin/documenting.rb +116 -114
  131. data/lib/solargraph/pin/instance_variable.rb +38 -34
  132. data/lib/solargraph/pin/keyword.rb +16 -20
  133. data/lib/solargraph/pin/local_variable.rb +31 -75
  134. data/lib/solargraph/pin/method.rb +724 -672
  135. data/lib/solargraph/pin/method_alias.rb +42 -34
  136. data/lib/solargraph/pin/namespace.rb +122 -115
  137. data/lib/solargraph/pin/parameter.rb +339 -275
  138. data/lib/solargraph/pin/proxy_type.rb +42 -39
  139. data/lib/solargraph/pin/reference/override.rb +47 -47
  140. data/lib/solargraph/pin/reference/superclass.rb +17 -15
  141. data/lib/solargraph/pin/reference/type_alias.rb +16 -0
  142. data/lib/solargraph/pin/reference.rb +59 -39
  143. data/lib/solargraph/pin/search.rb +62 -61
  144. data/lib/solargraph/pin/signature.rb +64 -61
  145. data/lib/solargraph/pin/symbol.rb +54 -53
  146. data/lib/solargraph/pin/until.rb +16 -18
  147. data/lib/solargraph/pin/while.rb +16 -18
  148. data/lib/solargraph/pin.rb +46 -44
  149. data/lib/solargraph/pin_cache.rb +248 -245
  150. data/lib/solargraph/position.rb +139 -119
  151. data/lib/solargraph/range.rb +113 -112
  152. data/lib/solargraph/rbs_map/conversions.rb +952 -823
  153. data/lib/solargraph/rbs_map/core_fills.rb +91 -84
  154. data/lib/solargraph/rbs_map/core_map.rb +65 -58
  155. data/lib/solargraph/rbs_map/stdlib_map.rb +71 -43
  156. data/lib/solargraph/rbs_map.rb +207 -163
  157. data/lib/solargraph/server_methods.rb +16 -16
  158. data/lib/solargraph/shell.rb +555 -352
  159. data/lib/solargraph/source/chain/array.rb +39 -37
  160. data/lib/solargraph/source/chain/call.rb +377 -337
  161. data/lib/solargraph/source/chain/class_variable.rb +13 -13
  162. data/lib/solargraph/source/chain/constant.rb +30 -26
  163. data/lib/solargraph/source/chain/global_variable.rb +13 -13
  164. data/lib/solargraph/source/chain/hash.rb +37 -34
  165. data/lib/solargraph/source/chain/if.rb +30 -28
  166. data/lib/solargraph/source/chain/instance_variable.rb +36 -13
  167. data/lib/solargraph/source/chain/link.rb +99 -109
  168. data/lib/solargraph/source/chain/literal.rb +51 -48
  169. data/lib/solargraph/source/chain/or.rb +29 -23
  170. data/lib/solargraph/source/chain/q_call.rb +13 -11
  171. data/lib/solargraph/source/chain/variable.rb +15 -13
  172. data/lib/solargraph/source/chain/z_super.rb +28 -30
  173. data/lib/solargraph/source/chain.rb +304 -291
  174. data/lib/solargraph/source/change.rb +89 -82
  175. data/lib/solargraph/source/cursor.rb +172 -166
  176. data/lib/solargraph/source/encoding_fixes.rb +22 -23
  177. data/lib/solargraph/source/source_chainer.rb +218 -194
  178. data/lib/solargraph/source/updater.rb +59 -55
  179. data/lib/solargraph/source.rb +522 -498
  180. data/lib/solargraph/source_map/clip.rb +245 -226
  181. data/lib/solargraph/source_map/data.rb +37 -34
  182. data/lib/solargraph/source_map/mapper.rb +286 -259
  183. data/lib/solargraph/source_map.rb +224 -212
  184. data/lib/solargraph/type_checker/problem.rb +34 -32
  185. data/lib/solargraph/type_checker/rules.rb +157 -84
  186. data/lib/solargraph/type_checker.rb +888 -814
  187. data/lib/solargraph/version.rb +5 -5
  188. data/lib/solargraph/workspace/config.rb +258 -255
  189. data/lib/solargraph/workspace/gemspecs.rb +367 -0
  190. data/lib/solargraph/workspace/require_paths.rb +98 -97
  191. data/lib/solargraph/workspace.rb +242 -220
  192. data/lib/solargraph/yard_map/helpers.rb +49 -44
  193. data/lib/solargraph/yard_map/mapper/to_method.rb +136 -130
  194. data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
  195. data/lib/solargraph/yard_map/mapper.rb +84 -79
  196. data/lib/solargraph/yard_map.rb +17 -18
  197. data/lib/solargraph/yard_tags.rb +20 -20
  198. data/lib/solargraph/yardoc.rb +90 -87
  199. data/lib/solargraph.rb +128 -105
  200. data/rbs/fills/rubygems/0/dependency.rbs +193 -0
  201. data/rbs/fills/tuple/tuple.rbs +28 -0
  202. data/rbs/shims/ast/0/node.rbs +5 -0
  203. data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
  204. data/rbs_collection.yaml +1 -1
  205. data/solargraph.gemspec +37 -35
  206. metadata +52 -51
  207. data/lib/solargraph/type_checker/checks.rb +0 -124
  208. data/lib/solargraph/type_checker/param_def.rb +0 -37
  209. data/lib/solargraph/yard_map/to_method.rb +0 -89
  210. data/sig/shims/ast/0/node.rbs +0 -5
  211. /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
  212. /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
  213. /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
  214. /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
  215. /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
  216. /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
  217. /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
  218. /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
  219. /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
@@ -1,486 +1,507 @@
1
- # frozen_string_literal: true
2
-
3
- require 'parser'
4
- require 'ast'
5
-
6
- # https://github.com/whitequark/parser
7
- module Solargraph
8
- module Parser
9
- module ParserGem
10
- module NodeMethods
11
- module_function
12
-
13
- # @param node [Parser::AST::Node]
14
- # @return [String]
15
- def unpack_name(node)
16
- pack_name(node).join("::")
17
- end
18
-
19
- # @param node [Parser::AST::Node]
20
- # @return [Array<String>]
21
- def pack_name(node)
22
- # @type [Array<String>]
23
- parts = []
24
- if node.is_a?(AST::Node)
25
- node.children.each { |n|
26
- if n.is_a?(AST::Node)
27
- if n.type == :cbase
28
- parts = [''] + pack_name(n)
29
- elsif n.type == :const
30
- parts += pack_name(n)
31
- end
32
- else
33
- parts.push n unless n.nil?
34
- end
35
- }
36
- end
37
- parts
38
- end
39
-
40
- # @param node [Parser::AST::Node]
41
- # @return [String, nil]
42
- def infer_literal_node_type node
43
- return nil unless node.is_a?(AST::Node)
44
- if node.type == :str || node.type == :dstr
45
- return '::String'
46
- elsif node.type == :array
47
- return '::Array'
48
- elsif node.type == :nil
49
- return '::NilClass'
50
- elsif node.type == :hash
51
- return '::Hash'
52
- elsif node.type == :int
53
- return '::Integer'
54
- elsif node.type == :float
55
- return '::Float'
56
- elsif node.type == :sym || node.type == :dsym
57
- return '::Symbol'
58
- elsif node.type == :regexp
59
- return '::Regexp'
60
- elsif node.type == :irange
61
- return '::Range'
62
- elsif node.type == :true || node.type == :false
63
- return '::Boolean'
64
- # @todo Support `nil` keyword in types
65
- # elsif node.type == :nil
66
- # return 'NilClass'
67
- end
68
- nil
69
- end
70
-
71
- # @param node [Parser::AST::Node]
72
- # @return [Position]
73
- def get_node_start_position(node)
74
- Position.new(node.loc.line, node.loc.column)
75
- end
76
-
77
- # @param node [Parser::AST::Node]
78
- # @return [Position]
79
- def get_node_end_position(node)
80
- Position.new(node.loc.last_line, node.loc.last_column)
81
- end
82
-
83
- # @param node [Parser::AST::Node]
84
- # @param signature [String]
85
- #
86
- # @return [String]
87
- def drill_signature node, signature
88
- return signature unless node.is_a?(AST::Node)
89
- if node.type == :const or node.type == :cbase
90
- unless node.children[0].nil?
91
- signature += drill_signature(node.children[0], signature)
92
- end
93
- signature += '::' unless signature.empty?
94
- signature += node.children[1].to_s
95
- elsif node.type == :lvar or node.type == :ivar or node.type == :cvar
96
- signature += '.' unless signature.empty?
97
- signature += node.children[0].to_s
98
- elsif node.type == :send
99
- unless node.children[0].nil?
100
- signature += drill_signature(node.children[0], signature)
101
- end
102
- signature += '.' unless signature.empty?
103
- signature += node.children[1].to_s
104
- end
105
- signature
106
- end
107
-
108
- # @param node [Parser::AST::Node]
109
- # @return [Hash{Symbol => Chain}]
110
- def convert_hash node
111
- return {} unless Parser.is_ast_node?(node)
112
- return convert_hash(node.children[0]) if node.type == :kwsplat
113
- return convert_hash(node.children[0]) if Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat
114
- return {} unless node.type == :hash
115
- result = {}
116
- node.children.each do |pair|
117
- result[pair.children[0].children[0]] = Solargraph::Parser.chain(pair.children[1])
118
- end
119
- result
120
- end
121
-
122
- # @sg-ignore Wrong argument type for AST::Node.new: type expected AST::_ToSym, received :nil
123
- NIL_NODE = ::Parser::AST::Node.new(:nil)
124
-
125
- # @param node [Parser::AST::Node]
126
- #
127
- # @return [Array<Parser::AST::Node>]
128
- def const_nodes_from node
129
- return [] unless Parser.is_ast_node?(node)
130
- result = []
131
- if node.type == :const
132
- result.push node
133
- else
134
- node.children.each { |child| result.concat const_nodes_from(child) }
135
- end
136
- result
137
- end
138
-
139
- # @param node [Parser::AST::Node]
140
- def splatted_hash? node
141
- Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat
142
- end
143
-
144
- # @param node [Parser::AST::Node]
145
- def splatted_call? node
146
- return false unless Parser.is_ast_node?(node)
147
- Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat && node.children[0].children[0].type != :hash
148
- end
149
-
150
- # @param nodes [Enumerable<Parser::AST::Node>]
151
- def any_splatted_call?(nodes)
152
- nodes.any? { |n| splatted_call?(n) }
153
- end
154
-
155
- # @todo Temporarily here for testing. Move to Solargraph::Parser.
156
- # @param node [Parser::AST::Node]
157
- # @return [Array<Parser::AST::Node>]
158
- def call_nodes_from node
159
- return [] unless node.is_a?(::Parser::AST::Node)
160
- result = []
161
- if node.type == :block
162
- result.push node
163
- if Parser.is_ast_node?(node.children[0]) && node.children[0].children.length > 2
164
- node.children[0].children[2..-1].each { |child| result.concat call_nodes_from(child) }
165
- end
166
- node.children[1..-1].each { |child| result.concat call_nodes_from(child) }
167
- elsif node.type == :send
168
- result.push node
169
- result.concat call_nodes_from(node.children.first)
170
- node.children[2..-1].each { |child| result.concat call_nodes_from(child) }
171
- elsif [:super, :zsuper].include?(node.type)
172
- result.push node
173
- node.children.each { |child| result.concat call_nodes_from(child) }
174
- else
175
- node.children.each { |child| result.concat call_nodes_from(child) }
176
- end
177
- result
178
- end
179
-
180
- # Find all the nodes within the provided node that potentially return a
181
- # value.
182
- #
183
- # The node parameter typically represents a method's logic, e.g., the
184
- # second child (after the :args node) of a :def node. A simple one-line
185
- # method would typically return itself, while a node with conditions
186
- # would return the resulting node from each conditional branch. Nodes
187
- # that follow a :return node are assumed to be unreachable. Nil values
188
- # are converted to nil node types.
189
- #
190
- # @param node [Parser::AST::Node]
191
- # @return [Array<Parser::AST::Node>]
192
- def returns_from_method_body node
193
- # @todo is the || NIL_NODE necessary?
194
- # STDERR.puts("Evaluating expression: #{node.inspect}")
195
- DeepInference.from_method_body(node).map { |n| n || NIL_NODE }
196
- end
197
-
198
- # @param node [Parser::AST::Node]
199
- # @return [Array<AST::Node>] low-level value nodes in
200
- # value position. Does not include explicit return
201
- # statements
202
- def value_position_nodes_only(node)
203
- DeepInference.value_position_nodes_only(node).map { |n| n || NIL_NODE }
204
- end
205
-
206
- # @param cursor [Solargraph::Source::Cursor]
207
- # @return [Parser::AST::Node, nil]
208
- def find_recipient_node cursor
209
- return repaired_find_recipient_node(cursor) if cursor.source.repaired? && cursor.source.code[cursor.offset - 1] == '('
210
- source = cursor.source
211
- position = cursor.position
212
- offset = cursor.offset
213
- tree = if source.synchronized?
214
- match = source.code[0..offset-1].match(/,\s*\z/)
215
- if match
216
- source.tree_at(position.line, position.column - match[0].length)
217
- else
218
- source.tree_at(position.line, position.column)
219
- end
220
- else
221
- source.tree_at(position.line, position.column - 1)
222
- end
223
- # @type [AST::Node, nil]
224
- prev = nil
225
- tree.each do |node|
226
- if node.type == :send
227
- args = node.children[2..-1]
228
- if !args.empty?
229
- return node if prev && args.include?(prev)
230
- else
231
- if source.synchronized?
232
- return node if source.code[0..offset-1] =~ /\(\s*\z/ && source.code[offset..-1] =~ /^\s*\)/
233
- else
234
- return node if source.code[0..offset-1] =~ /\([^(]*\z/
235
- end
236
- end
237
- end
238
- prev = node
239
- end
240
- nil
241
- end
242
-
243
- # @param cursor [Solargraph::Source::Cursor]
244
- # @return [Parser::AST::Node, nil]
245
- def repaired_find_recipient_node cursor
246
- cursor = cursor.source.cursor_at([cursor.position.line, cursor.position.column - 1])
247
- node = cursor.source.tree_at(cursor.position.line, cursor.position.column).first
248
- return node if node && node.type == :send
249
- end
250
-
251
- #
252
- # Concepts:
253
- #
254
- # - statement - one single node in the AST. Generally used
255
- # synonymously with how the Parser gem uses the term
256
- # 'expression'. This may have side effects (e.g.,
257
- # registering a method in the namespace, modifying
258
- # variables or doing I/O). It may encapsulate multiple
259
- # other statements (see compound statement).
260
- #
261
- # - value - something that can be assigned to a variable by
262
- # evaluating a statement
263
- #
264
- # - value node - the 'lowest level' AST node whose return
265
- # type is a subset of the value type of the overall
266
- # statement. Might be a literal, a method call, etc - the
267
- # goal is to find the lowest level node, which we can use
268
- # Chains and Pins later on to determine the type of.
269
- #
270
- # e.g., if the node 'b ? 123 : 456' were a return value, we
271
- # know the actual return values possible are 123 and 456,
272
- # and can disregard the rest.
273
- #
274
- # - value type - the type representing the multiple possible
275
- # values that can result from evaluation of the statement.
276
- #
277
- # - return type - the type describing the values a statement
278
- # might evaluate to. When used with a method, the term
279
- # describes the values that may result from the method
280
- # being called, and includes explicit return statements
281
- # within the method body's closure.
282
- #
283
- # - method body - a compound statement with parameters whose
284
- # return value type must account both for the explicit
285
- # 'return' statemnts as well as the final statements
286
- # executed in any given control flow through the method.
287
- #
288
- # - explicit return statement - a statement which, when part of a
289
- # method body, is a possible value of a call to that method -
290
- # e.g., "return 123"
291
- #
292
- # - compound statement - a statement which can be expanded to
293
- # be multiple statements in a row, executed in the context
294
- # of a method which can be explicitly returned from.
295
- #
296
- # - value position - the positions in the AST where the
297
- # return type of the statement would be one of the return
298
- # types of any compound statements it is a part of. For a
299
- # compound statement, the last of the child statements
300
- # would be in return position. This concept can be applied
301
- # recursively through e.g. conditionals to find a list of
302
- # statements in value positions.
303
- module DeepInference
304
- class << self
305
- CONDITIONAL_ALL_BUT_FIRST = [:if, :unless]
306
- CONDITIONAL_ALL = [:or]
307
- ONLY_ONE_CHILD = [:return]
308
- FIRST_TWO_CHILDREN = [:rescue]
309
- COMPOUND_STATEMENTS = [:begin, :kwbegin]
310
- SKIPPABLE = [:def, :defs, :class, :sclass, :module]
311
- FUNCTION_VALUE = [:block]
312
- CASE_STATEMENT = [:case]
313
-
314
- # @param node [AST::Node] a method body compound statement
315
- # @param include_explicit_returns [Boolean] If true,
316
- # include the value nodes of the parameter of the
317
- # 'return' statements in the type returned.
318
- # @return [Array<AST::Node>] low-level value nodes from
319
- # both nodes in value position as well as explicit
320
- # return statements in the method's closure.
321
- def from_method_body node
322
- from_value_position_statement(node, include_explicit_returns: true)
323
- end
324
-
325
- # @param node [AST::Node] an individual statement, to be
326
- # evaluated outside the context of a containing method
327
- # @return [Array<AST::Node>] low-level value nodes in
328
- # value position. Does not include explicit return
329
- # statements
330
- def value_position_nodes_only(node)
331
- from_value_position_statement(node, include_explicit_returns: false)
332
- end
333
-
334
- # Look at known control statements and use them to find
335
- # more specific return nodes.
336
- #
337
- # @param node [Parser::AST::Node] Statement which is in
338
- # value position for a method body
339
- # @param include_explicit_returns [Boolean] If true,
340
- # include the value nodes of the parameter of the
341
- # 'return' statements in the type returned.
342
- # @return [Array<Parser::AST::Node>]
343
- def from_value_position_statement node, include_explicit_returns: true
344
- # STDERR.puts("from_expression called on #{node.inspect}")
345
- return [] unless node.is_a?(::Parser::AST::Node)
346
- # @type [Array<Parser::AST::Node>]
347
- result = []
348
- if COMPOUND_STATEMENTS.include?(node.type)
349
- result.concat from_value_position_compound_statement node
350
- elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type)
351
- result.concat reduce_to_value_nodes(node.children[1..-1])
352
- # result.push NIL_NODE unless node.children[2]
353
- elsif CONDITIONAL_ALL.include?(node.type)
354
- result.concat reduce_to_value_nodes(node.children)
355
- elsif ONLY_ONE_CHILD.include?(node.type)
356
- result.concat reduce_to_value_nodes([node.children[0]])
357
- elsif FIRST_TWO_CHILDREN.include?(node.type)
358
- result.concat reduce_to_value_nodes([node.children[0], node.children[1]])
359
- elsif FUNCTION_VALUE.include?(node.type)
360
- # the block itself is a first class value that could be returned
361
- result.push node
362
- # @todo any explicit returns actually return from
363
- # scope in which the proc is run. This asssumes
364
- # that the function is executed here.
365
- result.concat explicit_return_values_from_compound_statement(node.children[2]) if include_explicit_returns
366
- elsif CASE_STATEMENT.include?(node.type)
367
- node.children[1..-1].each do |cc|
368
- if cc.nil?
369
- result.push NIL_NODE
370
- elsif cc.type == :when
371
- result.concat reduce_to_value_nodes([cc.children.last])
372
- else
373
- # else clause in case
374
- result.concat reduce_to_value_nodes([cc])
375
- end
376
- end
377
- elsif node.type == :resbody
378
- result.concat reduce_to_value_nodes([node.children[2]])
379
- else
380
- result.push node
381
- end
382
- result
383
- end
384
-
385
- # Treat parent as as a begin block and use the last node's
386
- # return node plus any explicit return nodes' return nodes. e.g.,
387
- #
388
- # 123
389
- # 456
390
- # return 'a' if foo == bar
391
- # 789
392
- #
393
- # would return 'a' and 789.
394
- #
395
- # @param parent [Parser::AST::Node]
396
- #
397
- # @return [Array<Parser::AST::Node>]
398
- def from_value_position_compound_statement parent
399
- result = []
400
- nodes = parent.children.select{|n| n.is_a?(AST::Node)}
401
- nodes.each_with_index do |node, idx|
402
- if node.type == :block
403
- result.concat explicit_return_values_from_compound_statement(node.children[2])
404
- elsif node.type == :rescue
405
- # body statements
406
- result.concat from_value_position_statement(node.children[0])
407
- # rescue statements
408
- result.concat from_value_position_statement(node.children[1])
409
- elsif SKIPPABLE.include?(node.type)
410
- next
411
- elsif node.type == :resbody
412
- result.concat reduce_to_value_nodes([node.children[2]])
413
- elsif node.type == :return
414
- result.concat reduce_to_value_nodes([node.children[0]])
415
- # Return here because the rest of the code is
416
- # unreachable and shouldn't be looked at
417
- return result
418
- else
419
- result.concat explicit_return_values_from_compound_statement(node)
420
- end
421
- # handle last line of compound statements, which is in
422
- # value position. we already have the explicit values
423
- # from above; now we need to also gather the value
424
- # position nodes
425
- result.concat from_value_position_statement(nodes.last, include_explicit_returns: false) if idx == nodes.length - 1
426
- end
427
- result
428
- end
429
-
430
- private
431
-
432
- # Useful when this statement isn't in value position, but
433
- # we care explicit return statements nonetheless.
434
- #
435
- # @param parent [Parser::AST::Node]
436
- #
437
- # @return [Array<Parser::AST::Node>]
438
- def explicit_return_values_from_compound_statement parent
439
- return [] unless parent.is_a?(::Parser::AST::Node)
440
- result = []
441
- nodes = parent.children.select{|n| n.is_a?(::Parser::AST::Node)}
442
- nodes.each do |node|
443
- next if SKIPPABLE.include?(node.type)
444
- if node.type == :return
445
- result.concat reduce_to_value_nodes([node.children[0]])
446
- # Return the result here because the rest of the code is
447
- # unreachable
448
- return result
449
- else
450
- result.concat explicit_return_values_from_compound_statement(node)
451
- end
452
- end
453
- result
454
- end
455
-
456
- # @param nodes [Enumerable<Parser::AST::Node, BasicObject>]
457
- # @return [Array<Parser::AST::Node, nil>]
458
- def reduce_to_value_nodes nodes
459
- result = []
460
- nodes.each do |node|
461
- if !node.is_a?(::Parser::AST::Node)
462
- result.push nil
463
- elsif COMPOUND_STATEMENTS.include?(node.type)
464
- result.concat from_value_position_compound_statement(node)
465
- elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type)
466
- result.concat reduce_to_value_nodes(node.children[1..-1])
467
- elsif node.type == :return
468
- result.concat reduce_to_value_nodes([node.children[0]])
469
- elsif node.type == :or
470
- result.concat reduce_to_value_nodes(node.children)
471
- elsif node.type == :block
472
- result.concat explicit_return_values_from_compound_statement(node.children[2])
473
- elsif node.type == :resbody
474
- result.concat reduce_to_value_nodes([node.children[2]])
475
- else
476
- result.push node
477
- end
478
- end
479
- result
480
- end
481
- end
482
- end
483
- end
484
- end
485
- end
486
- end
1
+ # frozen_string_literal: true
2
+
3
+ require 'parser'
4
+ require 'ast'
5
+
6
+ # https://github.com/whitequark/parser
7
+ module Solargraph
8
+ module Parser
9
+ module ParserGem
10
+ module NodeMethods
11
+ module_function
12
+
13
+ # @param node [Parser::AST::Node]
14
+ # @return [String]
15
+ def unpack_name node
16
+ pack_name(node).join('::')
17
+ end
18
+
19
+ # @param node [Parser::AST::Node]
20
+ # @return [Array<String>]
21
+ def pack_name node
22
+ # @type [Array<String>]
23
+ parts = []
24
+ if node.is_a?(AST::Node)
25
+ node.children.each do |n|
26
+ if n.is_a?(AST::Node)
27
+ if n.type == :cbase
28
+ parts = [''] + pack_name(n)
29
+ elsif n.type == :const
30
+ parts += pack_name(n)
31
+ end
32
+ else
33
+ parts.push n unless n.nil?
34
+ end
35
+ end
36
+ end
37
+ parts
38
+ end
39
+
40
+ # @param node [Parser::AST::Node, nil]
41
+ # @return [String, nil]
42
+ def infer_literal_node_type node
43
+ return nil unless node.is_a?(AST::Node)
44
+ if %i[str dstr].include?(node.type)
45
+ return '::String'
46
+ elsif node.type == :array
47
+ return '::Array'
48
+ elsif node.type == :nil
49
+ return '::NilClass'
50
+ elsif node.type == :hash
51
+ return '::Hash'
52
+ elsif node.type == :int
53
+ return '::Integer'
54
+ elsif node.type == :float
55
+ return '::Float'
56
+ elsif %i[sym dsym].include?(node.type)
57
+ return '::Symbol'
58
+ elsif node.type == :regexp
59
+ return '::Regexp'
60
+ elsif node.type == :irange
61
+ return '::Range'
62
+ elsif %i[true false].include?(node.type)
63
+ return '::Boolean'
64
+ # @todo Support `nil` keyword in types
65
+ # elsif node.type == :nil
66
+ # return 'NilClass'
67
+ end
68
+ nil
69
+ end
70
+
71
+ # @param node [Parser::AST::Node]
72
+ # @return [Position]
73
+ def get_node_start_position node
74
+ Position.new(node.loc.line, node.loc.column)
75
+ end
76
+
77
+ # @param node [Parser::AST::Node]
78
+ # @return [Position]
79
+ def get_node_end_position node
80
+ Position.new(node.loc.last_line, node.loc.last_column)
81
+ end
82
+
83
+ # @param node [Parser::AST::Node]
84
+ # @param signature [String]
85
+ #
86
+ # @return [String]
87
+ def drill_signature node, signature
88
+ return signature unless node.is_a?(AST::Node)
89
+ if %i[const cbase].include?(node.type)
90
+ signature += drill_signature(node.children[0], signature) unless node.children[0].nil?
91
+ signature += '::' unless signature.empty?
92
+ signature += node.children[1].to_s
93
+ elsif %i[lvar ivar cvar].include?(node.type)
94
+ signature += '.' unless signature.empty?
95
+ signature += node.children[0].to_s
96
+ elsif node.type == :send
97
+ signature += drill_signature(node.children[0], signature) unless node.children[0].nil?
98
+ signature += '.' unless signature.empty?
99
+ signature += node.children[1].to_s
100
+ end
101
+ signature
102
+ end
103
+
104
+ # @param node [Parser::AST::Node, nil]
105
+ # @return [Hash{Symbol => Chain}]
106
+ def convert_hash node
107
+ return {} unless Parser.is_ast_node?(node)
108
+ # @sg-ignore Translate to something flow sensitive typing understands
109
+ return convert_hash(node.children[0]) if node.type == :kwsplat
110
+ # @sg-ignore Translate to something flow sensitive typing understands
111
+ if Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat
112
+ # @sg-ignore Translate to something flow sensitive typing understands
113
+ return convert_hash(node.children[0])
114
+ end
115
+ # @sg-ignore Translate to something flow sensitive typing understands
116
+ return {} unless node.type == :hash
117
+ result = {}
118
+ # @sg-ignore Translate to something flow sensitive typing understands
119
+ node.children.each do |pair|
120
+ result[pair.children[0].children[0]] = Solargraph::Parser.chain(pair.children[1])
121
+ end
122
+ result
123
+ end
124
+
125
+ NIL_NODE = ::Parser::AST::Node.new(:nil)
126
+
127
+ # @param node [Parser::AST::Node]
128
+ #
129
+ # @return [Array<Parser::AST::Node>]
130
+ def const_nodes_from node
131
+ return [] unless Parser.is_ast_node?(node)
132
+ result = []
133
+ if node.type == :const
134
+ result.push node
135
+ else
136
+ node.children.each { |child| result.concat const_nodes_from(child) }
137
+ end
138
+ result
139
+ end
140
+
141
+ # @param node [Parser::AST::Node]
142
+ def splatted_hash? node
143
+ Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat
144
+ end
145
+
146
+ # @param node [Parser::AST::Node]
147
+ def splatted_call? node
148
+ return false unless Parser.is_ast_node?(node)
149
+ Parser.is_ast_node?(node.children[0]) && node.children[0].type == :kwsplat && node.children[0].children[0].type != :hash
150
+ end
151
+
152
+ # @param nodes [Enumerable<Parser::AST::Node>]
153
+ def any_splatted_call? nodes
154
+ nodes.any? { |n| splatted_call?(n) }
155
+ end
156
+
157
+ # @todo Temporarily here for testing. Move to Solargraph::Parser.
158
+ # @param node [Parser::AST::Node]
159
+ # @return [Array<Parser::AST::Node>]
160
+ def call_nodes_from node
161
+ return [] unless node.is_a?(::Parser::AST::Node)
162
+ result = []
163
+ if node.type == :block
164
+ result.push node
165
+ if Parser.is_ast_node?(node.children[0]) && node.children[0].children.length > 2
166
+ # @sg-ignore Need to add nil check here
167
+ node.children[0].children[2..].each { |child| result.concat call_nodes_from(child) }
168
+ end
169
+ # @sg-ignore Need to add nil check here
170
+ node.children[1..].each { |child| result.concat call_nodes_from(child) }
171
+ elsif node.type == :send
172
+ result.push node
173
+ result.concat call_nodes_from(node.children.first)
174
+ # @sg-ignore Need to add nil check here
175
+ node.children[2..].each { |child| result.concat call_nodes_from(child) }
176
+ elsif %i[super zsuper].include?(node.type)
177
+ result.push node
178
+ node.children.each { |child| result.concat call_nodes_from(child) }
179
+ else
180
+ node.children.each { |child| result.concat call_nodes_from(child) }
181
+ end
182
+ result
183
+ end
184
+
185
+ # Find all the nodes within the provided node that potentially return a
186
+ # value.
187
+ #
188
+ # The node parameter typically represents a method's logic, e.g., the
189
+ # second child (after the :args node) of a :def node. A simple one-line
190
+ # method would typically return itself, while a node with conditions
191
+ # would return the resulting node from each conditional branch. Nodes
192
+ # that follow a :return node are assumed to be unreachable. Nil values
193
+ # are converted to nil node types.
194
+ #
195
+ # @param node [Parser::AST::Node]
196
+ # @return [Array<Parser::AST::Node>]
197
+ def returns_from_method_body node
198
+ # @todo is the || NIL_NODE necessary?
199
+ # STDERR.puts("Evaluating expression: #{node.inspect}")
200
+ DeepInference.from_method_body(node).map { |n| n || NIL_NODE }
201
+ end
202
+
203
+ # @param node [Parser::AST::Node]
204
+ # @return [Array<AST::Node>] low-level value nodes in
205
+ # value position. Does not include explicit return
206
+ # statements
207
+ def value_position_nodes_only node
208
+ DeepInference.value_position_nodes_only(node).map { |n| n || NIL_NODE }
209
+ end
210
+
211
+ # @param cursor [Solargraph::Source::Cursor]
212
+ # @return [Parser::AST::Node, nil]
213
+ def find_recipient_node cursor
214
+ if cursor.source.repaired? && cursor.source.code[cursor.offset - 1] == '('
215
+ return repaired_find_recipient_node(cursor)
216
+ end
217
+ source = cursor.source
218
+ position = cursor.position
219
+ offset = cursor.offset
220
+ tree = if source.synchronized?
221
+ # @sg-ignore Need to add nil check here
222
+ match = source.code[0..(offset - 1)].match(/,\s*\z/)
223
+ if match
224
+ # @sg-ignore Need to add nil check here
225
+ source.tree_at(position.line, position.column - match[0].length)
226
+ else
227
+ source.tree_at(position.line, position.column)
228
+ end
229
+ else
230
+ source.tree_at(position.line, position.column - 1)
231
+ end
232
+ # @type [AST::Node, nil]
233
+ prev = nil
234
+ tree.each do |node|
235
+ if node.type == :send
236
+ args = node.children[2..]
237
+ # @sg-ignore Need to add nil check here
238
+ if !args.empty?
239
+ # @sg-ignore Need to add nil check here
240
+ return node if prev && args.include?(prev)
241
+ elsif source.synchronized?
242
+ return node if source.code[0..(offset - 1)] =~ /\(\s*\z/ && source.code[offset..] =~ /^\s*\)/
243
+ elsif source.code[0..(offset - 1)] =~ /\([^(]*\z/
244
+ return node
245
+ end
246
+ end
247
+ prev = node
248
+ end
249
+ nil
250
+ end
251
+
252
+ # @param cursor [Solargraph::Source::Cursor]
253
+ # @return [Parser::AST::Node, nil]
254
+ def repaired_find_recipient_node cursor
255
+ cursor = cursor.source.cursor_at([cursor.position.line, cursor.position.column - 1])
256
+ node = cursor.source.tree_at(cursor.position.line, cursor.position.column).first
257
+ node if node && node.type == :send
258
+ end
259
+
260
+ #
261
+ # Concepts:
262
+ #
263
+ # - statement - one single node in the AST. Generally used
264
+ # synonymously with how the Parser gem uses the term
265
+ # 'expression'. This may have side effects (e.g.,
266
+ # registering a method in the namespace, modifying
267
+ # variables or doing I/O). It may encapsulate multiple
268
+ # other statements (see compound statement).
269
+ #
270
+ # - value - something that can be assigned to a variable by
271
+ # evaluating a statement
272
+ #
273
+ # - value node - the 'lowest level' AST node whose return
274
+ # type is a subset of the value type of the overall
275
+ # statement. Might be a literal, a method call, etc - the
276
+ # goal is to find the lowest level node, which we can use
277
+ # Chains and Pins later on to determine the type of.
278
+ #
279
+ # e.g., if the node 'b ? 123 : 456' were a return value, we
280
+ # know the actual return values possible are 123 and 456,
281
+ # and can disregard the rest.
282
+ #
283
+ # - value type - the type representing the multiple possible
284
+ # values that can result from evaluation of the statement.
285
+ #
286
+ # - return type - the type describing the values a statement
287
+ # might evaluate to. When used with a method, the term
288
+ # describes the values that may result from the method
289
+ # being called, and includes explicit return statements
290
+ # within the method body's closure.
291
+ #
292
+ # - method body - a compound statement with parameters whose
293
+ # return value type must account both for the explicit
294
+ # 'return' statemnts as well as the final statements
295
+ # executed in any given control flow through the method.
296
+ #
297
+ # - explicit return statement - a statement which, when part of a
298
+ # method body, is a possible value of a call to that method -
299
+ # e.g., "return 123"
300
+ #
301
+ # - compound statement - a statement which can be expanded to
302
+ # be multiple statements in a row, executed in the context
303
+ # of a method which can be explicitly returned from.
304
+ #
305
+ # - value position - the positions in the AST where the
306
+ # return type of the statement would be one of the return
307
+ # types of any compound statements it is a part of. For a
308
+ # compound statement, the last of the child statements
309
+ # would be in return position. This concept can be applied
310
+ # recursively through e.g. conditionals to find a list of
311
+ # statements in value positions.
312
+ module DeepInference
313
+ class << self
314
+ CONDITIONAL_ALL_BUT_FIRST = %i[if unless].freeze
315
+ ONLY_ONE_CHILD = [:return].freeze
316
+ FIRST_TWO_CHILDREN = [:rescue].freeze
317
+ COMPOUND_STATEMENTS = %i[begin kwbegin].freeze
318
+ SKIPPABLE = %i[def defs class sclass module].freeze
319
+ FUNCTION_VALUE = [:block].freeze
320
+ CASE_STATEMENT = [:case].freeze
321
+
322
+ # @param node [AST::Node] a method body compound statement
323
+ # @return [Array<AST::Node>] low-level value nodes from
324
+ # both nodes in value position as well as explicit
325
+ # return statements in the method's closure.
326
+ def from_method_body node
327
+ from_value_position_statement(node, include_explicit_returns: true)
328
+ end
329
+
330
+ # @param node [AST::Node] an individual statement, to be
331
+ # evaluated outside the context of a containing method
332
+ # @return [Array<AST::Node>] low-level value nodes in
333
+ # value position. Does not include explicit return
334
+ # statements
335
+ def value_position_nodes_only node
336
+ from_value_position_statement(node, include_explicit_returns: false)
337
+ end
338
+
339
+ # Look at known control statements and use them to find
340
+ # more specific return nodes.
341
+ #
342
+ # @param node [AST::Node] Statement which is in
343
+ # value position for a method body
344
+ # @param include_explicit_returns [Boolean] If true,
345
+ # include the value nodes of the parameter of the
346
+ # 'return' statements in the type returned.
347
+ # @return [Array<Parser::AST::Node>]
348
+ def from_value_position_statement node, include_explicit_returns: true
349
+ # STDERR.puts("from_expression called on #{node.inspect}")
350
+ return [] unless node.is_a?(::Parser::AST::Node)
351
+ # @type [Array<Parser::AST::Node>]
352
+ result = []
353
+ if COMPOUND_STATEMENTS.include?(node.type)
354
+ result.concat from_value_position_compound_statement node
355
+ elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type)
356
+ # @sg-ignore Need to add nil check here
357
+ result.concat reduce_to_value_nodes(node.children[1..])
358
+ # result.push NIL_NODE unless node.children[2]
359
+ elsif ONLY_ONE_CHILD.include?(node.type)
360
+ result.concat reduce_to_value_nodes([node.children[0]])
361
+ elsif FIRST_TWO_CHILDREN.include?(node.type)
362
+ result.concat reduce_to_value_nodes([node.children[0], node.children[1]])
363
+ elsif FUNCTION_VALUE.include?(node.type)
364
+ # the block itself is a first class value that could be returned
365
+ result.push node
366
+ # @todo any explicit returns actually return from
367
+ # scope in which the proc is run. This asssumes
368
+ # that the function is executed here.
369
+ if include_explicit_returns
370
+ result.concat explicit_return_values_from_compound_statement(node.children[2])
371
+ end
372
+ elsif CASE_STATEMENT.include?(node.type)
373
+ # @sg-ignore Need to add nil check here
374
+ node.children[1..].each do |cc|
375
+ if cc.nil?
376
+ result.push NIL_NODE
377
+ elsif cc.type == :when
378
+ result.concat reduce_to_value_nodes([cc.children.last])
379
+ else
380
+ # else clause in case
381
+ result.concat reduce_to_value_nodes([cc])
382
+ end
383
+ end
384
+ elsif node.type == :resbody
385
+ result.concat reduce_to_value_nodes([node.children[2]])
386
+ else
387
+ result.push node
388
+ end
389
+ result
390
+ end
391
+
392
+ # Treat parent as as a begin block and use the last node's
393
+ # return node plus any explicit return nodes' return nodes. e.g.,
394
+ #
395
+ # 123
396
+ # 456
397
+ # return 'a' if foo == bar
398
+ # 789
399
+ #
400
+ # would return 'a' and 789.
401
+ #
402
+ # @param parent [Parser::AST::Node]
403
+ #
404
+ # @return [Array<Parser::AST::Node>]
405
+ def from_value_position_compound_statement parent
406
+ result = []
407
+ nodes = parent.children.select { |n| n.is_a?(AST::Node) }
408
+ nodes.each_with_index do |node, idx|
409
+ if node.type == :block
410
+ result.concat explicit_return_values_from_compound_statement(node.children[2])
411
+ elsif node.type == :rescue
412
+ # body statements
413
+ result.concat from_value_position_statement(node.children[0])
414
+ # rescue statements
415
+ result.concat from_value_position_statement(node.children[1])
416
+ elsif SKIPPABLE.include?(node.type)
417
+ next
418
+ elsif node.type == :resbody
419
+ result.concat reduce_to_value_nodes([node.children[2]])
420
+ elsif node.type == :return
421
+ result.concat reduce_to_value_nodes([node.children[0]])
422
+ # Return here because the rest of the code is
423
+ # unreachable and shouldn't be looked at
424
+ return result
425
+ else
426
+ result.concat explicit_return_values_from_compound_statement(node)
427
+ end
428
+ # handle last line of compound statements, which is in
429
+ # value position. we already have the explicit values
430
+ # from above; now we need to also gather the value
431
+ # position nodes
432
+ if idx == nodes.length - 1
433
+ result.concat from_value_position_statement(nodes.last,
434
+ include_explicit_returns: false)
435
+ end
436
+ end
437
+ result
438
+ end
439
+
440
+ private
441
+
442
+ # Useful when this statement isn't in value position, but
443
+ # we care explicit return statements nonetheless.
444
+ #
445
+ # @param parent [Parser::AST::Node]
446
+ #
447
+ # @return [Array<Parser::AST::Node>]
448
+ def explicit_return_values_from_compound_statement parent
449
+ return [] unless parent.is_a?(::Parser::AST::Node)
450
+ result = []
451
+ nodes = parent.children.select { |n| n.is_a?(::Parser::AST::Node) }
452
+ nodes.each do |node|
453
+ next if SKIPPABLE.include?(node.type)
454
+ if node.type == :return
455
+ result.concat reduce_to_value_nodes([node.children[0]])
456
+ # Return the result here because the rest of the code is
457
+ # unreachable
458
+ return result
459
+ else
460
+ result.concat explicit_return_values_from_compound_statement(node)
461
+ end
462
+ end
463
+ result
464
+ end
465
+
466
+ # @param nodes [Enumerable<Parser::AST::Node, BasicObject>]
467
+ # @return [Array<Parser::AST::Node, nil>]
468
+ def reduce_to_value_nodes nodes
469
+ result = []
470
+ nodes.each do |node|
471
+ if !node.is_a?(::Parser::AST::Node)
472
+ result.push nil
473
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
474
+ elsif COMPOUND_STATEMENTS.include?(node.type)
475
+ result.concat from_value_position_compound_statement(node)
476
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
477
+ elsif CONDITIONAL_ALL_BUT_FIRST.include?(node.type)
478
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
479
+ result.concat reduce_to_value_nodes(node.children[1..])
480
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
481
+ elsif node.type == :return
482
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
483
+ result.concat reduce_to_value_nodes([node.children[0]])
484
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
485
+ elsif node.type == :or
486
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
487
+ result.concat reduce_to_value_nodes(node.children)
488
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
489
+ elsif node.type == :block
490
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
491
+ result.concat explicit_return_values_from_compound_statement(node.children[2])
492
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
493
+ elsif node.type == :resbody
494
+ # @sg-ignore flow sensitive typing needs to narrow down type with an if is_a? check
495
+ result.concat reduce_to_value_nodes([node.children[2]])
496
+ else
497
+ result.push node
498
+ end
499
+ end
500
+ result
501
+ end
502
+ end
503
+ end
504
+ end
505
+ end
506
+ end
507
+ end