solargraph 0.58.1 → 0.59.0.dev.1
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.
- checksums.yaml +4 -4
- data/.envrc +3 -0
- data/.github/workflows/linting.yml +4 -5
- data/.github/workflows/plugins.yml +40 -36
- data/.github/workflows/rspec.yml +45 -13
- data/.github/workflows/typecheck.yml +2 -2
- data/.rubocop_todo.yml +27 -49
- data/README.md +3 -3
- data/Rakefile +1 -0
- data/lib/solargraph/api_map/cache.rb +110 -110
- data/lib/solargraph/api_map/constants.rb +289 -279
- data/lib/solargraph/api_map/index.rb +204 -193
- data/lib/solargraph/api_map/source_to_yard.rb +109 -97
- data/lib/solargraph/api_map/store.rb +387 -384
- data/lib/solargraph/api_map.rb +1000 -945
- data/lib/solargraph/complex_type/conformance.rb +176 -0
- data/lib/solargraph/complex_type/type_methods.rb +242 -228
- data/lib/solargraph/complex_type/unique_type.rb +632 -482
- data/lib/solargraph/complex_type.rb +549 -444
- data/lib/solargraph/convention/data_definition/data_definition_node.rb +93 -91
- data/lib/solargraph/convention/data_definition.rb +108 -105
- data/lib/solargraph/convention/struct_definition/struct_assignment_node.rb +62 -61
- data/lib/solargraph/convention/struct_definition/struct_definition_node.rb +103 -102
- data/lib/solargraph/convention/struct_definition.rb +168 -164
- data/lib/solargraph/diagnostics/require_not_found.rb +54 -53
- data/lib/solargraph/diagnostics/rubocop.rb +119 -118
- data/lib/solargraph/diagnostics/rubocop_helpers.rb +70 -68
- data/lib/solargraph/diagnostics/type_check.rb +56 -55
- data/lib/solargraph/doc_map.rb +200 -439
- data/lib/solargraph/equality.rb +34 -34
- data/lib/solargraph/gem_pins.rb +97 -98
- data/lib/solargraph/language_server/host/dispatch.rb +131 -130
- data/lib/solargraph/language_server/host/message_worker.rb +113 -112
- data/lib/solargraph/language_server/host/sources.rb +100 -99
- data/lib/solargraph/language_server/host.rb +883 -878
- data/lib/solargraph/language_server/message/extended/check_gem_version.rb +109 -114
- data/lib/solargraph/language_server/message/extended/document.rb +24 -23
- data/lib/solargraph/language_server/message/text_document/completion.rb +58 -56
- data/lib/solargraph/language_server/message/text_document/definition.rb +42 -40
- data/lib/solargraph/language_server/message/text_document/document_symbol.rb +28 -26
- data/lib/solargraph/language_server/message/text_document/formatting.rb +150 -148
- data/lib/solargraph/language_server/message/text_document/hover.rb +60 -58
- data/lib/solargraph/language_server/message/text_document/signature_help.rb +25 -24
- data/lib/solargraph/language_server/message/text_document/type_definition.rb +27 -25
- data/lib/solargraph/language_server/message/workspace/workspace_symbol.rb +25 -23
- data/lib/solargraph/library.rb +729 -683
- data/lib/solargraph/location.rb +87 -82
- data/lib/solargraph/logging.rb +57 -37
- data/lib/solargraph/parser/comment_ripper.rb +76 -69
- data/lib/solargraph/parser/flow_sensitive_typing.rb +483 -255
- data/lib/solargraph/parser/node_processor/base.rb +122 -92
- data/lib/solargraph/parser/node_processor.rb +63 -62
- data/lib/solargraph/parser/parser_gem/class_methods.rb +167 -149
- data/lib/solargraph/parser/parser_gem/node_chainer.rb +191 -166
- data/lib/solargraph/parser/parser_gem/node_methods.rb +506 -486
- data/lib/solargraph/parser/parser_gem/node_processors/and_node.rb +22 -22
- data/lib/solargraph/parser/parser_gem/node_processors/args_node.rb +61 -59
- data/lib/solargraph/parser/parser_gem/node_processors/begin_node.rb +24 -15
- data/lib/solargraph/parser/parser_gem/node_processors/block_node.rb +46 -46
- data/lib/solargraph/parser/parser_gem/node_processors/def_node.rb +60 -53
- data/lib/solargraph/parser/parser_gem/node_processors/if_node.rb +53 -23
- data/lib/solargraph/parser/parser_gem/node_processors/ivasgn_node.rb +41 -40
- data/lib/solargraph/parser/parser_gem/node_processors/lvasgn_node.rb +30 -29
- data/lib/solargraph/parser/parser_gem/node_processors/masgn_node.rb +61 -59
- data/lib/solargraph/parser/parser_gem/node_processors/opasgn_node.rb +98 -98
- data/lib/solargraph/parser/parser_gem/node_processors/or_node.rb +22 -0
- data/lib/solargraph/parser/parser_gem/node_processors/orasgn_node.rb +17 -17
- data/lib/solargraph/parser/parser_gem/node_processors/resbody_node.rb +39 -38
- data/lib/solargraph/parser/parser_gem/node_processors/sclass_node.rb +53 -52
- data/lib/solargraph/parser/parser_gem/node_processors/send_node.rb +296 -291
- data/lib/solargraph/parser/parser_gem/node_processors/when_node.rb +23 -0
- data/lib/solargraph/parser/parser_gem/node_processors/while_node.rb +33 -29
- data/lib/solargraph/parser/parser_gem/node_processors.rb +74 -70
- data/lib/solargraph/parser/region.rb +75 -69
- data/lib/solargraph/parser/snippet.rb +17 -17
- data/lib/solargraph/pin/base.rb +761 -729
- data/lib/solargraph/pin/base_variable.rb +418 -126
- data/lib/solargraph/pin/block.rb +126 -104
- data/lib/solargraph/pin/breakable.rb +13 -9
- data/lib/solargraph/pin/callable.rb +278 -231
- data/lib/solargraph/pin/closure.rb +68 -72
- data/lib/solargraph/pin/common.rb +94 -79
- data/lib/solargraph/pin/compound_statement.rb +55 -0
- data/lib/solargraph/pin/conversions.rb +124 -123
- data/lib/solargraph/pin/delegated_method.rb +131 -120
- data/lib/solargraph/pin/documenting.rb +115 -114
- data/lib/solargraph/pin/instance_variable.rb +38 -34
- data/lib/solargraph/pin/keyword.rb +16 -20
- data/lib/solargraph/pin/local_variable.rb +31 -75
- data/lib/solargraph/pin/method.rb +720 -672
- data/lib/solargraph/pin/method_alias.rb +42 -34
- data/lib/solargraph/pin/namespace.rb +121 -115
- data/lib/solargraph/pin/parameter.rb +338 -275
- data/lib/solargraph/pin/proxy_type.rb +40 -39
- data/lib/solargraph/pin/reference/override.rb +47 -47
- data/lib/solargraph/pin/reference/superclass.rb +17 -15
- data/lib/solargraph/pin/reference.rb +41 -39
- data/lib/solargraph/pin/search.rb +62 -61
- data/lib/solargraph/pin/signature.rb +69 -61
- data/lib/solargraph/pin/symbol.rb +53 -53
- data/lib/solargraph/pin/until.rb +18 -18
- data/lib/solargraph/pin/while.rb +18 -18
- data/lib/solargraph/pin.rb +46 -44
- data/lib/solargraph/pin_cache.rb +665 -245
- data/lib/solargraph/position.rb +118 -119
- data/lib/solargraph/range.rb +112 -112
- data/lib/solargraph/rbs_map/conversions.rb +846 -823
- data/lib/solargraph/rbs_map/core_map.rb +65 -58
- data/lib/solargraph/rbs_map/stdlib_map.rb +72 -43
- data/lib/solargraph/rbs_map.rb +217 -163
- data/lib/solargraph/shell.rb +397 -352
- data/lib/solargraph/source/chain/call.rb +372 -337
- data/lib/solargraph/source/chain/constant.rb +28 -26
- data/lib/solargraph/source/chain/hash.rb +35 -34
- data/lib/solargraph/source/chain/if.rb +29 -28
- data/lib/solargraph/source/chain/instance_variable.rb +34 -13
- data/lib/solargraph/source/chain/literal.rb +53 -48
- data/lib/solargraph/source/chain/or.rb +31 -23
- data/lib/solargraph/source/chain.rb +294 -291
- data/lib/solargraph/source/change.rb +89 -82
- data/lib/solargraph/source/cursor.rb +172 -166
- data/lib/solargraph/source/source_chainer.rb +204 -194
- data/lib/solargraph/source/updater.rb +59 -55
- data/lib/solargraph/source.rb +524 -498
- data/lib/solargraph/source_map/clip.rb +237 -226
- data/lib/solargraph/source_map/data.rb +37 -34
- data/lib/solargraph/source_map/mapper.rb +282 -259
- data/lib/solargraph/source_map.rb +220 -212
- data/lib/solargraph/type_checker/problem.rb +34 -32
- data/lib/solargraph/type_checker/rules.rb +157 -84
- data/lib/solargraph/type_checker.rb +895 -814
- data/lib/solargraph/version.rb +1 -1
- data/lib/solargraph/workspace/config.rb +257 -255
- data/lib/solargraph/workspace/gemspecs.rb +367 -0
- data/lib/solargraph/workspace/require_paths.rb +98 -97
- data/lib/solargraph/workspace.rb +362 -220
- data/lib/solargraph/yard_map/helpers.rb +45 -44
- data/lib/solargraph/yard_map/mapper/to_method.rb +134 -130
- data/lib/solargraph/yard_map/mapper/to_namespace.rb +32 -31
- data/lib/solargraph/yard_map/mapper.rb +84 -79
- data/lib/solargraph/yardoc.rb +97 -87
- data/lib/solargraph.rb +126 -105
- data/rbs/fills/rubygems/0/dependency.rbs +193 -0
- data/rbs/fills/tuple/tuple.rbs +28 -0
- data/rbs/shims/ast/0/node.rbs +5 -0
- data/rbs/shims/diff-lcs/1.5/diff-lcs.rbs +11 -0
- data/rbs_collection.yaml +1 -1
- data/solargraph.gemspec +2 -1
- metadata +22 -17
- data/lib/solargraph/type_checker/checks.rb +0 -124
- data/lib/solargraph/type_checker/param_def.rb +0 -37
- data/lib/solargraph/yard_map/to_method.rb +0 -89
- data/sig/shims/ast/0/node.rbs +0 -5
- /data/{sig → rbs}/shims/ast/2.4/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/ast/2.4/ast.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/builders/default.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/parser.rbs +0 -0
- /data/{sig → rbs}/shims/parser/3.2.0.1/polyfill.rbs +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/.rbs_meta.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/manifest.yaml +0 -0
- /data/{sig → rbs}/shims/thor/1.2.0.1/thor.rbs +0 -0
|
@@ -1,255 +1,483 @@
|
|
|
1
|
-
module Solargraph
|
|
2
|
-
module Parser
|
|
3
|
-
class FlowSensitiveTyping
|
|
4
|
-
include Solargraph::Parser::NodeMethods
|
|
5
|
-
|
|
6
|
-
# @param locals [Array<Solargraph::Pin::LocalVariable
|
|
7
|
-
# @param
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
#
|
|
37
|
-
#
|
|
38
|
-
#
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
# @param
|
|
86
|
-
# @param
|
|
87
|
-
# @param
|
|
88
|
-
#
|
|
89
|
-
# @return [
|
|
90
|
-
def
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
# @
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
#
|
|
178
|
-
# s(:
|
|
179
|
-
# s(:send, nil, :
|
|
180
|
-
# s(:
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
end
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
# @param
|
|
210
|
-
# @param
|
|
211
|
-
#
|
|
212
|
-
# @return [void]
|
|
213
|
-
def
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
pin
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# @param
|
|
228
|
-
#
|
|
229
|
-
#
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
#
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
1
|
+
module Solargraph
|
|
2
|
+
module Parser
|
|
3
|
+
class FlowSensitiveTyping
|
|
4
|
+
include Solargraph::Parser::NodeMethods
|
|
5
|
+
|
|
6
|
+
# @param locals [Array<Solargraph::Pin::LocalVariable>]
|
|
7
|
+
# @param ivars [Array<Solargraph::Pin::InstanceVariable>]
|
|
8
|
+
# @param enclosing_breakable_pin [Solargraph::Pin::Breakable, nil]
|
|
9
|
+
# @param enclosing_compound_statement_pin [Solargraph::Pin::CompoundStatement, nil]
|
|
10
|
+
def initialize(locals, ivars, enclosing_breakable_pin, enclosing_compound_statement_pin)
|
|
11
|
+
@locals = locals
|
|
12
|
+
@ivars = ivars
|
|
13
|
+
@enclosing_breakable_pin = enclosing_breakable_pin
|
|
14
|
+
@enclosing_compound_statement_pin = enclosing_compound_statement_pin
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# @param and_node [Parser::AST::Node]
|
|
18
|
+
# @param true_ranges [Array<Range>]
|
|
19
|
+
# @param false_ranges [Array<Range>]
|
|
20
|
+
#
|
|
21
|
+
# @return [void]
|
|
22
|
+
def process_and(and_node, true_ranges = [], false_ranges = [])
|
|
23
|
+
return unless and_node.type == :and
|
|
24
|
+
|
|
25
|
+
# @type [Parser::AST::Node]
|
|
26
|
+
lhs = and_node.children[0]
|
|
27
|
+
# @type [Parser::AST::Node]
|
|
28
|
+
rhs = and_node.children[1]
|
|
29
|
+
|
|
30
|
+
before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
|
|
31
|
+
before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
|
|
32
|
+
|
|
33
|
+
rhs_presence = Range.new(before_rhs_pos,
|
|
34
|
+
get_node_end_position(rhs))
|
|
35
|
+
|
|
36
|
+
# can't assume if an and is false that every single condition
|
|
37
|
+
# is false, so don't provide any false ranges to assert facts
|
|
38
|
+
# on
|
|
39
|
+
process_expression(lhs, true_ranges + [rhs_presence], [])
|
|
40
|
+
process_expression(rhs, true_ranges, [])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @param or_node [Parser::AST::Node]
|
|
44
|
+
# @param true_ranges [Array<Range>]
|
|
45
|
+
# @param false_ranges [Array<Range>]
|
|
46
|
+
#
|
|
47
|
+
# @return [void]
|
|
48
|
+
def process_or(or_node, true_ranges = [], false_ranges = [])
|
|
49
|
+
return unless or_node.type == :or
|
|
50
|
+
|
|
51
|
+
# @type [Parser::AST::Node]
|
|
52
|
+
lhs = or_node.children[0]
|
|
53
|
+
# @type [Parser::AST::Node]
|
|
54
|
+
rhs = or_node.children[1]
|
|
55
|
+
|
|
56
|
+
before_rhs_loc = rhs.location.expression.adjust(begin_pos: -1)
|
|
57
|
+
before_rhs_pos = Position.new(before_rhs_loc.line, before_rhs_loc.column)
|
|
58
|
+
|
|
59
|
+
rhs_presence = Range.new(before_rhs_pos,
|
|
60
|
+
get_node_end_position(rhs))
|
|
61
|
+
|
|
62
|
+
# can assume if an or is false that every single condition is
|
|
63
|
+
# false, so provide false ranges to assert facts on
|
|
64
|
+
|
|
65
|
+
# can't assume if an or is true that every single condition is
|
|
66
|
+
# true, so don't provide true ranges to assert facts on
|
|
67
|
+
|
|
68
|
+
process_expression(lhs, [], false_ranges + [rhs_presence])
|
|
69
|
+
process_expression(rhs, [], false_ranges)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @param node [Parser::AST::Node]
|
|
73
|
+
# @param true_presences [Array<Range>]
|
|
74
|
+
# @param false_presences [Array<Range>]
|
|
75
|
+
#
|
|
76
|
+
# @return [void]
|
|
77
|
+
def process_calls(node, true_presences, false_presences)
|
|
78
|
+
return unless node.type == :send
|
|
79
|
+
|
|
80
|
+
process_isa(node, true_presences, false_presences)
|
|
81
|
+
process_nilp(node, true_presences, false_presences)
|
|
82
|
+
process_bang(node, true_presences, false_presences)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# @param if_node [Parser::AST::Node]
|
|
86
|
+
# @param true_ranges [Array<Range>]
|
|
87
|
+
# @param false_ranges [Array<Range>]
|
|
88
|
+
#
|
|
89
|
+
# @return [void]
|
|
90
|
+
def process_if(if_node, true_ranges = [], false_ranges = [])
|
|
91
|
+
return if if_node.type != :if
|
|
92
|
+
|
|
93
|
+
#
|
|
94
|
+
# See if we can refine a type based on the result of 'if foo.nil?'
|
|
95
|
+
#
|
|
96
|
+
# [3] pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("if foo.is_a? Baz; then foo; else bar; end")
|
|
97
|
+
# => s(:if,
|
|
98
|
+
# s(:send,
|
|
99
|
+
# s(:send, nil, :foo), :is_a?,
|
|
100
|
+
# s(:const, nil, :Baz)),
|
|
101
|
+
# s(:send, nil, :foo),
|
|
102
|
+
# s(:send, nil, :bar))
|
|
103
|
+
# [4] pry(main)>
|
|
104
|
+
conditional_node = if_node.children[0]
|
|
105
|
+
# @type [Parser::AST::Node, nil]
|
|
106
|
+
then_clause = if_node.children[1]
|
|
107
|
+
# @type [Parser::AST::Node, nil]
|
|
108
|
+
else_clause = if_node.children[2]
|
|
109
|
+
|
|
110
|
+
unless enclosing_breakable_pin.nil?
|
|
111
|
+
rest_of_breakable_body = Range.new(get_node_end_position(if_node),
|
|
112
|
+
get_node_end_position(enclosing_breakable_pin.node))
|
|
113
|
+
|
|
114
|
+
if always_breaks?(then_clause)
|
|
115
|
+
false_ranges << rest_of_breakable_body
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
if always_breaks?(else_clause)
|
|
119
|
+
true_ranges << rest_of_breakable_body
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
unless enclosing_compound_statement_pin.node.nil?
|
|
124
|
+
rest_of_returnable_body = Range.new(get_node_end_position(if_node),
|
|
125
|
+
get_node_end_position(enclosing_compound_statement_pin.node))
|
|
126
|
+
|
|
127
|
+
#
|
|
128
|
+
# if one of the clauses always leaves the compound
|
|
129
|
+
# statement, we can assume things about the rest of the
|
|
130
|
+
# compound statement
|
|
131
|
+
#
|
|
132
|
+
if always_leaves_compound_statement?(then_clause)
|
|
133
|
+
false_ranges << rest_of_returnable_body
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
if always_leaves_compound_statement?(else_clause)
|
|
137
|
+
true_ranges << rest_of_returnable_body
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
unless then_clause.nil?
|
|
142
|
+
#
|
|
143
|
+
# If the condition is true we can assume things about the then clause
|
|
144
|
+
#
|
|
145
|
+
before_then_clause_loc = then_clause.location.expression.adjust(begin_pos: -1)
|
|
146
|
+
before_then_clause_pos = Position.new(before_then_clause_loc.line, before_then_clause_loc.column)
|
|
147
|
+
true_ranges << Range.new(before_then_clause_pos,
|
|
148
|
+
get_node_end_position(then_clause))
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
unless else_clause.nil?
|
|
152
|
+
#
|
|
153
|
+
# If the condition is true we can assume things about the else clause
|
|
154
|
+
#
|
|
155
|
+
before_else_clause_loc = else_clause.location.expression.adjust(begin_pos: -1)
|
|
156
|
+
before_else_clause_pos = Position.new(before_else_clause_loc.line, before_else_clause_loc.column)
|
|
157
|
+
false_ranges << Range.new(before_else_clause_pos,
|
|
158
|
+
get_node_end_position(else_clause))
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
process_expression(conditional_node, true_ranges, false_ranges)
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
# @param while_node [Parser::AST::Node]
|
|
165
|
+
# @param true_ranges [Array<Range>]
|
|
166
|
+
# @param false_ranges [Array<Range>]
|
|
167
|
+
#
|
|
168
|
+
# @return [void]
|
|
169
|
+
def process_while(while_node, true_ranges = [], false_ranges = [])
|
|
170
|
+
return if while_node.type != :while
|
|
171
|
+
|
|
172
|
+
#
|
|
173
|
+
# See if we can refine a type based on the result of 'if foo.nil?'
|
|
174
|
+
#
|
|
175
|
+
# [3] pry(main)> Parser::CurrentRuby.parse("while a; b; c; end")
|
|
176
|
+
# => s(:while,
|
|
177
|
+
# s(:send, nil, :a),
|
|
178
|
+
# s(:begin,
|
|
179
|
+
# s(:send, nil, :b),
|
|
180
|
+
# s(:send, nil, :c)))
|
|
181
|
+
# [4] pry(main)>
|
|
182
|
+
conditional_node = while_node.children[0]
|
|
183
|
+
# @type [Parser::AST::Node, nil]
|
|
184
|
+
do_clause = while_node.children[1]
|
|
185
|
+
|
|
186
|
+
unless do_clause.nil?
|
|
187
|
+
#
|
|
188
|
+
# If the condition is true we can assume things about the do clause
|
|
189
|
+
#
|
|
190
|
+
before_do_clause_loc = do_clause.location.expression.adjust(begin_pos: -1)
|
|
191
|
+
before_do_clause_pos = Position.new(before_do_clause_loc.line, before_do_clause_loc.column)
|
|
192
|
+
true_ranges << Range.new(before_do_clause_pos,
|
|
193
|
+
get_node_end_position(do_clause))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
process_expression(conditional_node, true_ranges, false_ranges)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class << self
|
|
200
|
+
include Logging
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
include Logging
|
|
204
|
+
|
|
205
|
+
private
|
|
206
|
+
|
|
207
|
+
# @param pin [Pin::BaseVariable]
|
|
208
|
+
# @param presence [Range]
|
|
209
|
+
# @param downcast_type [ComplexType, nil]
|
|
210
|
+
# @param downcast_not_type [ComplexType, nil]
|
|
211
|
+
#
|
|
212
|
+
# @return [void]
|
|
213
|
+
def add_downcast_var(pin, presence:, downcast_type:, downcast_not_type:)
|
|
214
|
+
new_pin = pin.downcast(exclude_return_type: downcast_not_type,
|
|
215
|
+
intersection_return_type: downcast_type,
|
|
216
|
+
source: :flow_sensitive_typing,
|
|
217
|
+
presence: presence)
|
|
218
|
+
if pin.is_a?(Pin::LocalVariable)
|
|
219
|
+
locals.push(new_pin)
|
|
220
|
+
elsif pin.is_a?(Pin::InstanceVariable)
|
|
221
|
+
ivars.push(new_pin)
|
|
222
|
+
else
|
|
223
|
+
raise "Tried to add invalid pin type #{pin.class} in FlowSensitiveTyping"
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# @param facts_by_pin [Hash{Pin::BaseVariable => Array<Hash{:type, :not_type => ComplexType}>}]
|
|
228
|
+
# @param presences [Array<Range>]
|
|
229
|
+
#
|
|
230
|
+
# @return [void]
|
|
231
|
+
def process_facts(facts_by_pin, presences)
|
|
232
|
+
#
|
|
233
|
+
# Add specialized vars for the rest of the block
|
|
234
|
+
#
|
|
235
|
+
facts_by_pin.each_pair do |pin, facts|
|
|
236
|
+
facts.each do |fact|
|
|
237
|
+
downcast_type = fact.fetch(:type, nil)
|
|
238
|
+
downcast_not_type = fact.fetch(:not_type, nil)
|
|
239
|
+
presences.each do |presence|
|
|
240
|
+
add_downcast_var(pin,
|
|
241
|
+
presence: presence,
|
|
242
|
+
downcast_type: downcast_type,
|
|
243
|
+
downcast_not_type: downcast_not_type)
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
# @param expression_node [Parser::AST::Node]
|
|
250
|
+
# @param true_ranges [Array<Range>]
|
|
251
|
+
# @param false_ranges [Array<Range>]
|
|
252
|
+
#
|
|
253
|
+
# @return [void]
|
|
254
|
+
def process_expression(expression_node, true_ranges, false_ranges)
|
|
255
|
+
process_calls(expression_node, true_ranges, false_ranges)
|
|
256
|
+
process_and(expression_node, true_ranges, false_ranges)
|
|
257
|
+
process_or(expression_node, true_ranges, false_ranges)
|
|
258
|
+
process_variable(expression_node, true_ranges, false_ranges)
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
# @param call_node [Parser::AST::Node]
|
|
262
|
+
# @param method_name [Symbol]
|
|
263
|
+
# @return [Array(String, String), nil] Tuple of rgument to
|
|
264
|
+
# function, then receiver of function if it's a variable,
|
|
265
|
+
# otherwise nil if no simple variable receiver
|
|
266
|
+
def parse_call(call_node, method_name)
|
|
267
|
+
return unless call_node&.type == :send && call_node.children[1] == method_name
|
|
268
|
+
# Check if conditional node follows this pattern:
|
|
269
|
+
# s(:send,
|
|
270
|
+
# s(:send, nil, :foo), :is_a?,
|
|
271
|
+
# s(:const, nil, :Baz)),
|
|
272
|
+
#
|
|
273
|
+
call_receiver = call_node.children[0]
|
|
274
|
+
call_arg = type_name(call_node.children[2])
|
|
275
|
+
|
|
276
|
+
# check if call_receiver looks like this:
|
|
277
|
+
# s(:send, nil, :foo)
|
|
278
|
+
# and set variable_name to :foo
|
|
279
|
+
if call_receiver&.type == :send && call_receiver.children[0].nil? && call_receiver.children[1].is_a?(Symbol)
|
|
280
|
+
variable_name = call_receiver.children[1].to_s
|
|
281
|
+
end
|
|
282
|
+
# or like this:
|
|
283
|
+
# (lvar :repr)
|
|
284
|
+
# @sg-ignore Need to look at Tuple#include? handling
|
|
285
|
+
variable_name = call_receiver.children[0].to_s if [:lvar, :ivar].include?(call_receiver&.type)
|
|
286
|
+
return unless variable_name
|
|
287
|
+
|
|
288
|
+
[call_arg, variable_name]
|
|
289
|
+
end
|
|
290
|
+
|
|
291
|
+
# @param isa_node [Parser::AST::Node]
|
|
292
|
+
# @return [Array(String, String), nil]
|
|
293
|
+
def parse_isa(isa_node)
|
|
294
|
+
call_type_name, variable_name = parse_call(isa_node, :is_a?)
|
|
295
|
+
|
|
296
|
+
return unless call_type_name
|
|
297
|
+
|
|
298
|
+
[call_type_name, variable_name]
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# @param variable_name [String]
|
|
302
|
+
# @param position [Position]
|
|
303
|
+
#
|
|
304
|
+
# @sg-ignore Solargraph::Parser::FlowSensitiveTyping#find_var
|
|
305
|
+
# return type could not be inferred
|
|
306
|
+
# @return [Solargraph::Pin::LocalVariable, Solargraph::Pin::InstanceVariable, nil]
|
|
307
|
+
def find_var(variable_name, position)
|
|
308
|
+
if variable_name.start_with?('@')
|
|
309
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
310
|
+
ivars.find { |ivar| ivar.name == variable_name && (!ivar.presence || ivar.presence.include?(position)) }
|
|
311
|
+
else
|
|
312
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
313
|
+
locals.find { |pin| pin.name == variable_name && (!pin.presence || pin.presence.include?(position)) }
|
|
314
|
+
end
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
# @param isa_node [Parser::AST::Node]
|
|
318
|
+
# @param true_presences [Array<Range>]
|
|
319
|
+
# @param false_presences [Array<Range>]
|
|
320
|
+
#
|
|
321
|
+
# @return [void]
|
|
322
|
+
def process_isa(isa_node, true_presences, false_presences)
|
|
323
|
+
isa_type_name, variable_name = parse_isa(isa_node)
|
|
324
|
+
return if variable_name.nil? || variable_name.empty?
|
|
325
|
+
# @sg-ignore Need to add nil check here
|
|
326
|
+
isa_position = Range.from_node(isa_node).start
|
|
327
|
+
|
|
328
|
+
pin = find_var(variable_name, isa_position)
|
|
329
|
+
return unless pin
|
|
330
|
+
|
|
331
|
+
# @type Hash{Pin::BaseVariable => Array<Hash{Symbol => ComplexType}>}
|
|
332
|
+
if_true = {}
|
|
333
|
+
if_true[pin] ||= []
|
|
334
|
+
if_true[pin] << { type: ComplexType.parse(isa_type_name) }
|
|
335
|
+
process_facts(if_true, true_presences)
|
|
336
|
+
|
|
337
|
+
# @type Hash{Pin::BaseVariable => Array<Hash{Symbol => ComplexType}>}
|
|
338
|
+
if_false = {}
|
|
339
|
+
if_false[pin] ||= []
|
|
340
|
+
if_false[pin] << { not_type: ComplexType.parse(isa_type_name) }
|
|
341
|
+
process_facts(if_false, false_presences)
|
|
342
|
+
end
|
|
343
|
+
|
|
344
|
+
# @param nilp_node [Parser::AST::Node]
|
|
345
|
+
# @return [Array(String, String), nil]
|
|
346
|
+
def parse_nilp(nilp_node)
|
|
347
|
+
parse_call(nilp_node, :nil?)
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
# @param nilp_node [Parser::AST::Node]
|
|
351
|
+
# @param true_presences [Array<Range>]
|
|
352
|
+
# @param false_presences [Array<Range>]
|
|
353
|
+
#
|
|
354
|
+
# @return [void]
|
|
355
|
+
def process_nilp(nilp_node, true_presences, false_presences)
|
|
356
|
+
nilp_arg, variable_name = parse_nilp(nilp_node)
|
|
357
|
+
return if variable_name.nil? || variable_name.empty?
|
|
358
|
+
# if .nil? got an argument, move on, this isn't the situation
|
|
359
|
+
# we're looking for and typechecking will cover any invalid
|
|
360
|
+
# ones
|
|
361
|
+
return unless nilp_arg.nil?
|
|
362
|
+
# @sg-ignore Need to add nil check here
|
|
363
|
+
nilp_position = Range.from_node(nilp_node).start
|
|
364
|
+
|
|
365
|
+
pin = find_var(variable_name, nilp_position)
|
|
366
|
+
return unless pin
|
|
367
|
+
|
|
368
|
+
# @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
|
|
369
|
+
if_true = {}
|
|
370
|
+
if_true[pin] ||= []
|
|
371
|
+
if_true[pin] << { type: ComplexType::NIL }
|
|
372
|
+
process_facts(if_true, true_presences)
|
|
373
|
+
|
|
374
|
+
# @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
|
|
375
|
+
if_false = {}
|
|
376
|
+
if_false[pin] ||= []
|
|
377
|
+
if_false[pin] << { not_type: ComplexType::NIL }
|
|
378
|
+
process_facts(if_false, false_presences)
|
|
379
|
+
end
|
|
380
|
+
|
|
381
|
+
# @param bang_node [Parser::AST::Node]
|
|
382
|
+
# @return [Array(String, String), nil]
|
|
383
|
+
def parse_bang(bang_node)
|
|
384
|
+
parse_call(bang_node, :!)
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# @param bang_node [Parser::AST::Node]
|
|
388
|
+
# @param true_presences [Array<Range>]
|
|
389
|
+
# @param false_presences [Array<Range>]
|
|
390
|
+
#
|
|
391
|
+
# @return [void]
|
|
392
|
+
def process_bang(bang_node, true_presences, false_presences)
|
|
393
|
+
# pry(main)> require 'parser/current'; Parser::CurrentRuby.parse("!2")
|
|
394
|
+
# => s(:send,
|
|
395
|
+
# s(:int, 2), :!)
|
|
396
|
+
# end
|
|
397
|
+
return unless bang_node.type == :send && bang_node.children[1] == :!
|
|
398
|
+
|
|
399
|
+
receiver = bang_node.children[0]
|
|
400
|
+
|
|
401
|
+
# swap the two presences
|
|
402
|
+
process_expression(receiver, false_presences, true_presences)
|
|
403
|
+
end
|
|
404
|
+
|
|
405
|
+
# @param var_node [Parser::AST::Node]
|
|
406
|
+
#
|
|
407
|
+
# @return [String, nil] Variable name referenced
|
|
408
|
+
def parse_variable(var_node)
|
|
409
|
+
return if var_node.children.length != 1
|
|
410
|
+
|
|
411
|
+
var_node.children[0]&.to_s
|
|
412
|
+
end
|
|
413
|
+
|
|
414
|
+
# @return [void]
|
|
415
|
+
# @param node [Parser::AST::Node]
|
|
416
|
+
# @param true_presences [Array<Range>]
|
|
417
|
+
# @param false_presences [Array<Range>]
|
|
418
|
+
def process_variable(node, true_presences, false_presences)
|
|
419
|
+
return unless [:lvar, :ivar, :cvar, :gvar].include?(node.type)
|
|
420
|
+
|
|
421
|
+
variable_name = parse_variable(node)
|
|
422
|
+
return if variable_name.nil?
|
|
423
|
+
|
|
424
|
+
# @sg-ignore Need to add nil check here
|
|
425
|
+
var_position = Range.from_node(node).start
|
|
426
|
+
|
|
427
|
+
pin = find_var(variable_name, var_position)
|
|
428
|
+
return unless pin
|
|
429
|
+
|
|
430
|
+
# @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
|
|
431
|
+
if_true = {}
|
|
432
|
+
if_true[pin] ||= []
|
|
433
|
+
if_true[pin] << { not_type: ComplexType::NIL }
|
|
434
|
+
process_facts(if_true, true_presences)
|
|
435
|
+
|
|
436
|
+
# @type Hash{Pin::LocalVariable => Array<Hash{Symbol => ComplexType}>}
|
|
437
|
+
if_false = {}
|
|
438
|
+
if_false[pin] ||= []
|
|
439
|
+
if_false[pin] << { type: ComplexType.parse('nil, false') }
|
|
440
|
+
process_facts(if_false, false_presences)
|
|
441
|
+
end
|
|
442
|
+
|
|
443
|
+
# @param node [Parser::AST::Node]
|
|
444
|
+
#
|
|
445
|
+
# @return [String, nil]
|
|
446
|
+
def type_name(node)
|
|
447
|
+
# e.g.,
|
|
448
|
+
# s(:const, nil, :Baz)
|
|
449
|
+
return unless node&.type == :const
|
|
450
|
+
# @type [Parser::AST::Node, nil]
|
|
451
|
+
module_node = node.children[0]
|
|
452
|
+
# @type [Parser::AST::Node, nil]
|
|
453
|
+
class_node = node.children[1]
|
|
454
|
+
|
|
455
|
+
return class_node.to_s if module_node.nil?
|
|
456
|
+
|
|
457
|
+
module_type_name = type_name(module_node)
|
|
458
|
+
return unless module_type_name
|
|
459
|
+
|
|
460
|
+
"#{module_type_name}::#{class_node}"
|
|
461
|
+
end
|
|
462
|
+
|
|
463
|
+
# @param clause_node [Parser::AST::Node, nil]
|
|
464
|
+
# @sg-ignore need boolish support for ? methods
|
|
465
|
+
def always_breaks?(clause_node)
|
|
466
|
+
clause_node&.type == :break
|
|
467
|
+
end
|
|
468
|
+
|
|
469
|
+
# @param clause_node [Parser::AST::Node, nil]
|
|
470
|
+
def always_leaves_compound_statement?(clause_node)
|
|
471
|
+
# https://docs.ruby-lang.org/en/2.2.0/keywords_rdoc.html
|
|
472
|
+
# @sg-ignore Need to look at Tuple#include? handling
|
|
473
|
+
[:return, :raise, :next, :redo, :retry].include?(clause_node&.type)
|
|
474
|
+
end
|
|
475
|
+
|
|
476
|
+
attr_reader :locals
|
|
477
|
+
|
|
478
|
+
attr_reader :ivars
|
|
479
|
+
|
|
480
|
+
attr_reader :enclosing_breakable_pin, :enclosing_compound_statement_pin
|
|
481
|
+
end
|
|
482
|
+
end
|
|
483
|
+
end
|