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,814 +1,895 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Solargraph
|
|
4
|
-
# A static analysis tool for validating data types.
|
|
5
|
-
#
|
|
6
|
-
class TypeChecker
|
|
7
|
-
autoload :Problem, 'solargraph/type_checker/problem'
|
|
8
|
-
autoload :
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
include
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
# @param
|
|
25
|
-
# @param
|
|
26
|
-
# @param
|
|
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
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
end
|
|
199
|
-
|
|
200
|
-
# @
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
result
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
#
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
end
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
# @param
|
|
370
|
-
# @param
|
|
371
|
-
# @param
|
|
372
|
-
# @param
|
|
373
|
-
# @param
|
|
374
|
-
# @
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
# @param
|
|
513
|
-
# @param
|
|
514
|
-
# @param
|
|
515
|
-
#
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
result
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
#
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
# @param
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
end
|
|
733
|
-
|
|
734
|
-
# @param
|
|
735
|
-
# @param
|
|
736
|
-
# @
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
end
|
|
797
|
-
|
|
798
|
-
# @param
|
|
799
|
-
# @
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Solargraph
|
|
4
|
+
# A static analysis tool for validating data types.
|
|
5
|
+
#
|
|
6
|
+
class TypeChecker
|
|
7
|
+
autoload :Problem, 'solargraph/type_checker/problem'
|
|
8
|
+
autoload :Rules, 'solargraph/type_checker/rules'
|
|
9
|
+
|
|
10
|
+
# @!parse
|
|
11
|
+
# include Solargraph::Parser::ParserGem::NodeMethods
|
|
12
|
+
include Parser::NodeMethods
|
|
13
|
+
|
|
14
|
+
# @return [String]
|
|
15
|
+
attr_reader :filename
|
|
16
|
+
|
|
17
|
+
# @return [Rules]
|
|
18
|
+
attr_reader :rules
|
|
19
|
+
|
|
20
|
+
# @return [ApiMap]
|
|
21
|
+
attr_reader :api_map
|
|
22
|
+
|
|
23
|
+
# @param filename [String, nil]
|
|
24
|
+
# @param api_map [ApiMap, nil]
|
|
25
|
+
# @param level [Symbol] Don't complain about anything above this level
|
|
26
|
+
# @param workspace [Workspace, nil] Workspace to use for loading
|
|
27
|
+
# type checker rules modified by user config
|
|
28
|
+
# @param rules [Rules] Type checker rules object
|
|
29
|
+
def initialize filename,
|
|
30
|
+
api_map: nil,
|
|
31
|
+
level: :normal,
|
|
32
|
+
workspace: filename ? Workspace.new(File.dirname(filename)) : nil,
|
|
33
|
+
rules: workspace ? workspace.rules(level) : Rules.new(level, {})
|
|
34
|
+
@filename = filename
|
|
35
|
+
# @todo Smarter directory resolution
|
|
36
|
+
@api_map = api_map || Solargraph::ApiMap.load(File.dirname(filename),
|
|
37
|
+
loose_unions: !rules.require_all_unique_types_support_call?)
|
|
38
|
+
@rules = rules
|
|
39
|
+
# @type [Array<Range>]
|
|
40
|
+
@marked_ranges = []
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# @return [SourceMap]
|
|
44
|
+
def source_map
|
|
45
|
+
@source_map ||= api_map.source_map(filename)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# @return [Source]
|
|
49
|
+
def source
|
|
50
|
+
source_map.source
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# @param inferred [ComplexType, ComplexType::UniqueType]
|
|
54
|
+
# @param expected [ComplexType, ComplexType::UniqueType]
|
|
55
|
+
def return_type_conforms_to?(inferred, expected)
|
|
56
|
+
conforms_to?(inferred, expected, :return_type)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @param inferred [ComplexType, ComplexType::UniqueType]
|
|
60
|
+
# @param expected [ComplexType, ComplexType::UniqueType]
|
|
61
|
+
def arg_conforms_to?(inferred, expected)
|
|
62
|
+
conforms_to?(inferred, expected, :method_call)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# @param inferred [ComplexType, ComplexType::UniqueType]
|
|
66
|
+
# @param expected [ComplexType, ComplexType::UniqueType]
|
|
67
|
+
def assignment_conforms_to?(inferred, expected)
|
|
68
|
+
conforms_to?(inferred, expected, :assignment)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# @param inferred [ComplexType, ComplexType::UniqueType]
|
|
72
|
+
# @param expected [ComplexType, ComplexType::UniqueType]
|
|
73
|
+
# @param scenario [Symbol]
|
|
74
|
+
def conforms_to?(inferred, expected, scenario)
|
|
75
|
+
rules_arr = []
|
|
76
|
+
rules_arr << :allow_empty_params unless rules.require_inferred_type_params?
|
|
77
|
+
rules_arr << :allow_any_match unless rules.require_all_unique_types_match_expected?
|
|
78
|
+
rules_arr << :allow_undefined unless rules.require_no_undefined_args?
|
|
79
|
+
rules_arr << :allow_unresolved_generic unless rules.require_generics_resolved?
|
|
80
|
+
rules_arr << :allow_unmatched_interface unless rules.require_interfaces_resolved?
|
|
81
|
+
rules_arr << :allow_reverse_match unless rules.require_downcasts?
|
|
82
|
+
inferred.conforms_to?(api_map, expected, scenario,
|
|
83
|
+
rules_arr)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @return [Array<Problem>]
|
|
87
|
+
def problems
|
|
88
|
+
@problems ||= begin
|
|
89
|
+
all = method_tag_problems
|
|
90
|
+
.concat(variable_type_tag_problems)
|
|
91
|
+
.concat(const_problems)
|
|
92
|
+
.concat(call_problems)
|
|
93
|
+
unignored = without_ignored(all)
|
|
94
|
+
unignored.concat(unneeded_sgignore_problems)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
class << self
|
|
99
|
+
# @param filename [String]
|
|
100
|
+
# @param level [Symbol]
|
|
101
|
+
# @return [self]
|
|
102
|
+
def load filename, level = :normal
|
|
103
|
+
source = Solargraph::Source.load(filename)
|
|
104
|
+
rules = Rules.new(level, {})
|
|
105
|
+
api_map = Solargraph::ApiMap.new(loose_unions:
|
|
106
|
+
!rules.require_all_unique_types_support_call?)
|
|
107
|
+
api_map.map(source)
|
|
108
|
+
new(filename, api_map: api_map, level: level, rules: rules)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# @param code [String]
|
|
112
|
+
# @param filename [String, nil]
|
|
113
|
+
# @param level [Symbol]
|
|
114
|
+
# @param api_map [Solargraph::ApiMap, nil]
|
|
115
|
+
# @return [self]
|
|
116
|
+
def load_string code, filename = nil, level = :normal, api_map: nil
|
|
117
|
+
source = Solargraph::Source.load_string(code, filename)
|
|
118
|
+
rules = Rules.new(level, {})
|
|
119
|
+
api_map ||= Solargraph::ApiMap.new(loose_unions:
|
|
120
|
+
!rules.require_all_unique_types_support_call?)
|
|
121
|
+
# @sg-ignore flow sensitive typing needs better handling of ||= on lvars
|
|
122
|
+
api_map.map(source)
|
|
123
|
+
new(filename, api_map: api_map, level: level, rules: rules)
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
private
|
|
128
|
+
|
|
129
|
+
# @return [Array<Problem>]
|
|
130
|
+
def method_tag_problems
|
|
131
|
+
result = []
|
|
132
|
+
# @param pin [Pin::Method]
|
|
133
|
+
source_map.pins_by_class(Pin::Method).each do |pin|
|
|
134
|
+
result.concat method_return_type_problems_for(pin)
|
|
135
|
+
result.concat method_param_type_problems_for(pin)
|
|
136
|
+
end
|
|
137
|
+
result
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @param pin [Pin::Method]
|
|
141
|
+
# @return [Array<Problem>]
|
|
142
|
+
def method_return_type_problems_for pin
|
|
143
|
+
return [] if pin.is_a?(Pin::MethodAlias)
|
|
144
|
+
result = []
|
|
145
|
+
declared = pin.typify(api_map).self_to_type(pin.full_context).qualify(api_map, *pin.gates)
|
|
146
|
+
if declared.undefined?
|
|
147
|
+
# @sg-ignore Need to add nil check here
|
|
148
|
+
if pin.return_type.undefined? && rules.require_type_tags?
|
|
149
|
+
if pin.attribute?
|
|
150
|
+
inferred = pin.probe(api_map).self_to_type(pin.full_context)
|
|
151
|
+
result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin) unless inferred.defined?
|
|
152
|
+
else
|
|
153
|
+
result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
|
|
154
|
+
end
|
|
155
|
+
# @sg-ignore Need to add nil check here
|
|
156
|
+
elsif pin.return_type.defined? && !resolved_constant?(pin)
|
|
157
|
+
result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin)
|
|
158
|
+
elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined?
|
|
159
|
+
result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
|
|
160
|
+
end
|
|
161
|
+
elsif rules.validate_tags?
|
|
162
|
+
unless pin.node.nil? || declared.void? || virtual_pin?(pin) || abstract?(pin)
|
|
163
|
+
inferred = pin.probe(api_map).self_to_type(pin.full_context)
|
|
164
|
+
if inferred.undefined?
|
|
165
|
+
unless rules.ignore_all_undefined? || external?(pin)
|
|
166
|
+
result.push Problem.new(pin.location, "#{pin.path} return type could not be inferred", pin: pin)
|
|
167
|
+
end
|
|
168
|
+
else
|
|
169
|
+
unless return_type_conforms_to?(inferred, declared)
|
|
170
|
+
result.push Problem.new(pin.location, "Declared return type #{declared.rooted_tags} does not match inferred type #{inferred.rooted_tags} for #{pin.path}", pin: pin)
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
result
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# @todo This is not optimal. A better solution would probably be to mix
|
|
179
|
+
# namespace alias into types at the ApiMap level.
|
|
180
|
+
#
|
|
181
|
+
# @param pin [Pin::Base]
|
|
182
|
+
# @return [Boolean]
|
|
183
|
+
def resolved_constant? pin
|
|
184
|
+
return true if pin.typify(api_map).defined?
|
|
185
|
+
constant_pins = api_map.get_constants('', *pin.closure.gates)
|
|
186
|
+
.select { |p| p.name == pin.return_type.namespace }
|
|
187
|
+
return true if constant_pins.find { |p| p.typify(api_map).defined? }
|
|
188
|
+
# will need to probe when a constant name is assigned to a
|
|
189
|
+
# class/module (alias)
|
|
190
|
+
return true if constant_pins.find { |p| p.probe(api_map).defined? }
|
|
191
|
+
false
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# @param pin [Pin::Base]
|
|
195
|
+
def virtual_pin? pin
|
|
196
|
+
# @sg-ignore Need to add nil check here
|
|
197
|
+
pin.location && source.comment_at?(pin.location.range.ending)
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# @param pin [Pin::Method]
|
|
201
|
+
# @return [Array<Problem>]
|
|
202
|
+
def method_param_type_problems_for pin
|
|
203
|
+
stack = api_map.get_method_stack(pin.namespace, pin.name, scope: pin.scope)
|
|
204
|
+
result = []
|
|
205
|
+
pin.signatures.each do |sig|
|
|
206
|
+
params = param_details_from_stack(sig, stack)
|
|
207
|
+
if rules.require_type_tags?
|
|
208
|
+
sig.parameters.each do |par|
|
|
209
|
+
break if par.decl == :restarg || par.decl == :kwrestarg || par.decl == :blockarg
|
|
210
|
+
unless params[par.name]
|
|
211
|
+
if pin.attribute?
|
|
212
|
+
inferred = pin.probe(api_map).self_to_type(pin.full_context)
|
|
213
|
+
if inferred.undefined?
|
|
214
|
+
result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
|
|
215
|
+
end
|
|
216
|
+
else
|
|
217
|
+
result.push Problem.new(pin.location, "Missing @param tag for #{par.name} on #{pin.path}", pin: pin)
|
|
218
|
+
end
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
# @param name [String]
|
|
223
|
+
# @param data [Hash{Symbol => BasicObject}]
|
|
224
|
+
params.each_pair do |name, data|
|
|
225
|
+
# @type [ComplexType]
|
|
226
|
+
type = data[:qualified]
|
|
227
|
+
if type.undefined?
|
|
228
|
+
result.push Problem.new(pin.location, "Unresolved type #{data[:tagged]} for #{name} param on #{pin.path}", pin: pin)
|
|
229
|
+
end
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
result
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
# @return [Array<Pin::Base>]
|
|
236
|
+
def ignored_pins
|
|
237
|
+
@ignored_pins ||= []
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# @return [Array<Problem>]
|
|
241
|
+
def variable_type_tag_problems
|
|
242
|
+
result = []
|
|
243
|
+
all_variables.each do |pin|
|
|
244
|
+
# @sg-ignore Need to add nil check here
|
|
245
|
+
if pin.return_type.defined?
|
|
246
|
+
declared = pin.typify(api_map)
|
|
247
|
+
next if declared.duck_type?
|
|
248
|
+
if declared.defined? && pin.assignment
|
|
249
|
+
if rules.validate_tags?
|
|
250
|
+
inferred = pin.probe(api_map)
|
|
251
|
+
if inferred.undefined?
|
|
252
|
+
next if rules.ignore_all_undefined?
|
|
253
|
+
if declared_externally?(pin)
|
|
254
|
+
ignored_pins.push pin
|
|
255
|
+
else
|
|
256
|
+
result.push Problem.new(pin.location, "Variable type could not be inferred for #{pin.name}", pin: pin)
|
|
257
|
+
end
|
|
258
|
+
else
|
|
259
|
+
unless assignment_conforms_to?(inferred, declared)
|
|
260
|
+
result.push Problem.new(pin.location, "Declared type #{declared} does not match inferred type #{inferred} for variable #{pin.name}", pin: pin)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
elsif declared_externally?(pin)
|
|
264
|
+
ignored_pins.push pin
|
|
265
|
+
end
|
|
266
|
+
elsif !pin.is_a?(Pin::Parameter) && !resolved_constant?(pin)
|
|
267
|
+
result.push Problem.new(pin.location, "Unresolved type #{pin.return_type} for variable #{pin.name}", pin: pin)
|
|
268
|
+
end
|
|
269
|
+
elsif pin.assignment
|
|
270
|
+
inferred = pin.probe(api_map)
|
|
271
|
+
if inferred.undefined? && declared_externally?(pin)
|
|
272
|
+
ignored_pins.push pin
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
result
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# @return [Array<Pin::BaseVariable>]
|
|
280
|
+
def all_variables
|
|
281
|
+
source_map.pins_by_class(Pin::BaseVariable) + source_map.locals.select { |pin| pin.is_a?(Pin::LocalVariable) }
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# @return [Array<Problem>]
|
|
285
|
+
def const_problems
|
|
286
|
+
return [] unless rules.validate_consts?
|
|
287
|
+
result = []
|
|
288
|
+
Solargraph::Parser::NodeMethods.const_nodes_from(source.node).each do |const|
|
|
289
|
+
rng = Solargraph::Range.from_node(const)
|
|
290
|
+
chain = Solargraph::Parser.chain(const, filename)
|
|
291
|
+
# @sg-ignore Need to add nil check here
|
|
292
|
+
closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column)
|
|
293
|
+
closure_pin.rebind(api_map)
|
|
294
|
+
# @sg-ignore Need to add nil check here
|
|
295
|
+
location = Location.new(filename, rng)
|
|
296
|
+
locals = source_map.locals_at(location)
|
|
297
|
+
pins = chain.define(api_map, closure_pin, locals)
|
|
298
|
+
if pins.empty?
|
|
299
|
+
result.push Problem.new(location, "Unresolved constant #{Solargraph::Parser::NodeMethods.unpack_name(const)}")
|
|
300
|
+
@marked_ranges.push location.range
|
|
301
|
+
end
|
|
302
|
+
end
|
|
303
|
+
result
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# @return [Array<Problem>]
|
|
307
|
+
def call_problems
|
|
308
|
+
result = []
|
|
309
|
+
Solargraph::Parser::NodeMethods.call_nodes_from(source.node).each do |call|
|
|
310
|
+
rng = Solargraph::Range.from_node(call)
|
|
311
|
+
# @sg-ignore Need to add nil check here
|
|
312
|
+
next if @marked_ranges.any? { |d| d.contain?(rng.start) }
|
|
313
|
+
chain = Solargraph::Parser.chain(call, filename)
|
|
314
|
+
# @sg-ignore Need to add nil check here
|
|
315
|
+
closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column)
|
|
316
|
+
namespace_pin = closure_pin
|
|
317
|
+
if call.type == :block
|
|
318
|
+
# blocks in the AST include the method call as well, so the
|
|
319
|
+
# node returned by #call_nodes_from needs to be backed out
|
|
320
|
+
# one closure
|
|
321
|
+
# @todo Need to add nil check here
|
|
322
|
+
# @todo Should warn on nil deference here
|
|
323
|
+
closure_pin = closure_pin.closure
|
|
324
|
+
end
|
|
325
|
+
# @sg-ignore Need to add nil check here
|
|
326
|
+
closure_pin.rebind(api_map)
|
|
327
|
+
# @sg-ignore Need to add nil check here
|
|
328
|
+
location = Location.new(filename, rng)
|
|
329
|
+
locals = source_map.locals_at(location)
|
|
330
|
+
# @sg-ignore Need to add nil check here
|
|
331
|
+
type = chain.infer(api_map, closure_pin, locals)
|
|
332
|
+
if type.undefined? && !rules.ignore_all_undefined?
|
|
333
|
+
base = chain
|
|
334
|
+
missing = chain
|
|
335
|
+
# @type [Solargraph::Pin::Base, nil]
|
|
336
|
+
found = nil
|
|
337
|
+
# @type [Array<Solargraph::Pin::Base>]
|
|
338
|
+
all_found = []
|
|
339
|
+
closest = ComplexType::UNDEFINED
|
|
340
|
+
until base.links.first.undefined?
|
|
341
|
+
# @sg-ignore Need to add nil check here
|
|
342
|
+
all_found = base.define(api_map, closure_pin, locals)
|
|
343
|
+
found = all_found.first
|
|
344
|
+
break if found
|
|
345
|
+
missing = base
|
|
346
|
+
base = base.base
|
|
347
|
+
end
|
|
348
|
+
all_closest = all_found.map { |pin| pin.typify(api_map) }
|
|
349
|
+
closest = ComplexType.new(all_closest.flat_map(&:items).uniq)
|
|
350
|
+
# @todo remove the internal_or_core? check at a higher-than-strict level
|
|
351
|
+
if !found || found.is_a?(Pin::BaseVariable) || (closest.defined? && internal_or_core?(found))
|
|
352
|
+
# @sg-ignore Need to add nil check here
|
|
353
|
+
unless closest.generic? || ignored_pins.include?(found)
|
|
354
|
+
if closest.defined?
|
|
355
|
+
result.push Problem.new(location, "Unresolved call to #{missing.links.last.word} on #{closest}")
|
|
356
|
+
else
|
|
357
|
+
result.push Problem.new(location, "Unresolved call to #{missing.links.last.word}")
|
|
358
|
+
end
|
|
359
|
+
@marked_ranges.push rng
|
|
360
|
+
end
|
|
361
|
+
end
|
|
362
|
+
end
|
|
363
|
+
# @sg-ignore Need to add nil check here
|
|
364
|
+
result.concat argument_problems_for(chain, api_map, closure_pin, locals, location)
|
|
365
|
+
end
|
|
366
|
+
result
|
|
367
|
+
end
|
|
368
|
+
|
|
369
|
+
# @param chain [Solargraph::Source::Chain]
|
|
370
|
+
# @param api_map [Solargraph::ApiMap]
|
|
371
|
+
# @param closure_pin [Solargraph::Pin::Closure]
|
|
372
|
+
# @param locals [Array<Solargraph::Pin::LocalVariable>]
|
|
373
|
+
# @param location [Solargraph::Location]
|
|
374
|
+
# @return [Array<Problem>]
|
|
375
|
+
def argument_problems_for chain, api_map, closure_pin, locals, location
|
|
376
|
+
result = []
|
|
377
|
+
base = chain
|
|
378
|
+
last_base_link = base.links.last
|
|
379
|
+
return [] unless last_base_link.is_a?(Solargraph::Source::Chain::Call)
|
|
380
|
+
|
|
381
|
+
arguments = last_base_link.arguments
|
|
382
|
+
|
|
383
|
+
pins = base.define(api_map, closure_pin, locals)
|
|
384
|
+
|
|
385
|
+
first_pin = pins.first
|
|
386
|
+
if first_pin.is_a?(Pin::DelegatedMethod) && !first_pin.resolvable?(api_map)
|
|
387
|
+
# Do nothing, as we can't find the actual method implementation
|
|
388
|
+
elsif first_pin.is_a?(Pin::Method)
|
|
389
|
+
# @type [Pin::Method]
|
|
390
|
+
pin = first_pin
|
|
391
|
+
ap = if base.links.last.is_a?(Solargraph::Source::Chain::ZSuper)
|
|
392
|
+
arity_problems_for(pin, fake_args_for(closure_pin), location)
|
|
393
|
+
elsif pin.path == 'Class#new'
|
|
394
|
+
fqns = if base.links.one?
|
|
395
|
+
closure_pin.namespace
|
|
396
|
+
else
|
|
397
|
+
base.base.infer(api_map, closure_pin, locals).namespace
|
|
398
|
+
end
|
|
399
|
+
init = api_map.get_method_stack(fqns, 'initialize').first
|
|
400
|
+
|
|
401
|
+
# @type [::Array<Solargraph::TypeChecker::Problem>]
|
|
402
|
+
init ? arity_problems_for(init, arguments, location) : []
|
|
403
|
+
else
|
|
404
|
+
arity_problems_for(pin, arguments, location)
|
|
405
|
+
end
|
|
406
|
+
return ap unless ap.empty?
|
|
407
|
+
return [] if !rules.validate_calls? || base.links.first.is_a?(Solargraph::Source::Chain::ZSuper)
|
|
408
|
+
|
|
409
|
+
all_errors = []
|
|
410
|
+
pin.signatures.sort { |sig| sig.parameters.length }.each do |sig|
|
|
411
|
+
params = param_details_from_stack(sig, pins)
|
|
412
|
+
|
|
413
|
+
signature_errors = signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin
|
|
414
|
+
|
|
415
|
+
if signature_errors.empty?
|
|
416
|
+
# we found a signature that works - meaning errors from
|
|
417
|
+
# other signatures don't matter.
|
|
418
|
+
return []
|
|
419
|
+
end
|
|
420
|
+
all_errors.concat signature_errors
|
|
421
|
+
end
|
|
422
|
+
result.concat all_errors
|
|
423
|
+
end
|
|
424
|
+
result
|
|
425
|
+
end
|
|
426
|
+
|
|
427
|
+
# @param location [Location]
|
|
428
|
+
# @param locals [Array<Pin::LocalVariable>]
|
|
429
|
+
# @param closure_pin [Pin::Closure]
|
|
430
|
+
# @param params [Hash{String => undefined}]
|
|
431
|
+
# @param arguments [Array<Source::Chain>]
|
|
432
|
+
# @param sig [Pin::Signature]
|
|
433
|
+
# @param pin [Pin::Method]
|
|
434
|
+
# @param pins [Array<Pin::Method>]
|
|
435
|
+
#
|
|
436
|
+
# @return [Array<Problem>]
|
|
437
|
+
def signature_argument_problems_for location, locals, closure_pin, params, arguments, sig, pin
|
|
438
|
+
errors = []
|
|
439
|
+
# @todo add logic mapping up restarg parameters with
|
|
440
|
+
# arguments (including restarg arguments). Use tuples
|
|
441
|
+
# when possible, and when not, ensure provably
|
|
442
|
+
# incorrect situations are detected.
|
|
443
|
+
sig.parameters.each_with_index do |par, idx|
|
|
444
|
+
return errors if par.decl == :restarg # bail out and assume the rest is valid pending better arg processing
|
|
445
|
+
argchain = arguments[idx]
|
|
446
|
+
if argchain.nil?
|
|
447
|
+
if par.decl == :arg
|
|
448
|
+
final_arg = arguments.last
|
|
449
|
+
if final_arg && final_arg.node.type == :splat
|
|
450
|
+
argchain = final_arg
|
|
451
|
+
return errors
|
|
452
|
+
else
|
|
453
|
+
errors.push Problem.new(location, "Not enough arguments to #{pin.path}")
|
|
454
|
+
end
|
|
455
|
+
else
|
|
456
|
+
final_arg = arguments.last
|
|
457
|
+
argchain = final_arg if final_arg && [:kwsplat, :hash].include?(final_arg.node.type)
|
|
458
|
+
end
|
|
459
|
+
end
|
|
460
|
+
if argchain
|
|
461
|
+
if par.decl != :arg
|
|
462
|
+
errors.concat kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx
|
|
463
|
+
next
|
|
464
|
+
else
|
|
465
|
+
if argchain.node.type == :splat && argchain == arguments.last
|
|
466
|
+
final_arg = argchain
|
|
467
|
+
end
|
|
468
|
+
if (final_arg && final_arg.node.type == :splat)
|
|
469
|
+
# The final argument given has been seen and was a
|
|
470
|
+
# splat, which doesn't give us useful types or
|
|
471
|
+
# arities against positional parameters, so let's
|
|
472
|
+
# continue on in case there are any required
|
|
473
|
+
# kwargs we should warn about
|
|
474
|
+
next
|
|
475
|
+
end
|
|
476
|
+
if argchain.node.type == :splat && par != sig.parameters.last
|
|
477
|
+
# we have been given a splat and there are more
|
|
478
|
+
# arguments to come.
|
|
479
|
+
|
|
480
|
+
# @todo Improve this so that we can skip past the
|
|
481
|
+
# rest of the positional parameters here but still
|
|
482
|
+
# process the kwargs
|
|
483
|
+
return errors
|
|
484
|
+
end
|
|
485
|
+
ptype = params.key?(par.name) ? params[par.name][:qualified] : ComplexType::UNDEFINED
|
|
486
|
+
ptype = ptype.self_to_type(par.context)
|
|
487
|
+
if ptype.nil?
|
|
488
|
+
# @todo Some level (strong, I guess) should require the param here
|
|
489
|
+
else
|
|
490
|
+
argtype = argchain.infer(api_map, closure_pin, locals)
|
|
491
|
+
argtype = argtype.self_to_type(closure_pin.context)
|
|
492
|
+
if argtype.defined? && ptype.defined? && !arg_conforms_to?(argtype, ptype)
|
|
493
|
+
errors.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
|
494
|
+
return errors
|
|
495
|
+
end
|
|
496
|
+
end
|
|
497
|
+
end
|
|
498
|
+
elsif par.decl == :kwarg
|
|
499
|
+
errors.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
|
500
|
+
next
|
|
501
|
+
end
|
|
502
|
+
end
|
|
503
|
+
errors
|
|
504
|
+
end
|
|
505
|
+
|
|
506
|
+
# @param sig [Pin::Signature]
|
|
507
|
+
# @param argchain [Solargraph::Source::Chain]
|
|
508
|
+
# @param api_map [ApiMap]
|
|
509
|
+
# @param closure_pin [Pin::Closure]
|
|
510
|
+
# @param locals [Array<Pin::LocalVariable>]
|
|
511
|
+
# @param location [Location]
|
|
512
|
+
# @param pin [Pin::Method]
|
|
513
|
+
# @param params [Hash{String => Hash{Symbol => undefined}}]
|
|
514
|
+
# @param idx [Integer]
|
|
515
|
+
#
|
|
516
|
+
# @return [Array<Problem>]
|
|
517
|
+
def kwarg_problems_for sig, argchain, api_map, closure_pin, locals, location, pin, params, idx
|
|
518
|
+
result = []
|
|
519
|
+
kwargs = convert_hash(argchain.node)
|
|
520
|
+
par = sig.parameters[idx]
|
|
521
|
+
# @type [Solargraph::Source::Chain]
|
|
522
|
+
argchain = kwargs[par.name.to_sym]
|
|
523
|
+
if par.decl == :kwrestarg || (par.decl == :optarg && idx == pin.parameters.length - 1 && par.asgn_code == '{}')
|
|
524
|
+
result.concat kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs)
|
|
525
|
+
else
|
|
526
|
+
if argchain
|
|
527
|
+
data = params[par.name]
|
|
528
|
+
if data.nil?
|
|
529
|
+
# @todo Some level (strong, I guess) should require the param here
|
|
530
|
+
else
|
|
531
|
+
# @type [ComplexType, ComplexType::UniqueType]
|
|
532
|
+
ptype = data[:qualified]
|
|
533
|
+
ptype = ptype.self_to_type(pin.context)
|
|
534
|
+
unless ptype.undefined?
|
|
535
|
+
# @type [ComplexType]
|
|
536
|
+
argtype = argchain.infer(api_map, closure_pin, locals).self_to_type(closure_pin.context)
|
|
537
|
+
# @todo Unresolved call to defined?
|
|
538
|
+
if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype)
|
|
539
|
+
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{par.name} expected #{ptype}, received #{argtype}")
|
|
540
|
+
end
|
|
541
|
+
end
|
|
542
|
+
end
|
|
543
|
+
elsif par.decl == :kwarg
|
|
544
|
+
result.push Problem.new(location, "Call to #{pin.path} is missing keyword argument #{par.name}")
|
|
545
|
+
end
|
|
546
|
+
end
|
|
547
|
+
result
|
|
548
|
+
end
|
|
549
|
+
|
|
550
|
+
# @param api_map [ApiMap]
|
|
551
|
+
# @param closure_pin [Pin::Closure]
|
|
552
|
+
# @param locals [Array<Pin::LocalVariable>]
|
|
553
|
+
# @param location [Location]
|
|
554
|
+
# @param pin [Pin::Method]
|
|
555
|
+
# @param params [Hash{String => [nil, Hash]}]
|
|
556
|
+
# @param kwargs [Hash{Symbol => Source::Chain}]
|
|
557
|
+
# @return [Array<Problem>]
|
|
558
|
+
def kwrestarg_problems_for(api_map, closure_pin, locals, location, pin, params, kwargs)
|
|
559
|
+
result = []
|
|
560
|
+
kwargs.each_pair do |pname, argchain|
|
|
561
|
+
next unless params.key?(pname.to_s)
|
|
562
|
+
# @type [ComplexType]
|
|
563
|
+
ptype = params[pname.to_s][:qualified]
|
|
564
|
+
ptype = ptype.self_to_type(pin.context)
|
|
565
|
+
argtype = argchain.infer(api_map, closure_pin, locals)
|
|
566
|
+
argtype = argtype.self_to_type(closure_pin.context)
|
|
567
|
+
if argtype.defined? && ptype && !arg_conforms_to?(argtype, ptype)
|
|
568
|
+
result.push Problem.new(location, "Wrong argument type for #{pin.path}: #{pname} expected #{ptype}, received #{argtype}")
|
|
569
|
+
end
|
|
570
|
+
end
|
|
571
|
+
result
|
|
572
|
+
end
|
|
573
|
+
|
|
574
|
+
# @param param_details [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
575
|
+
# @param pin [Pin::Method, Pin::Signature]
|
|
576
|
+
# @param relevant_pin [Pin::Method, Pin::Signature] the pin which is under inspection
|
|
577
|
+
# @return [void]
|
|
578
|
+
def add_restkwarg_param_tag_details(param_details, pin, relevant_pin)
|
|
579
|
+
# see if we have additional tags to pay attention to from YARD -
|
|
580
|
+
# e.g., kwargs in a **restkwargs splat
|
|
581
|
+
tags = pin.docstring.tags(:param)
|
|
582
|
+
tags.each do |tag|
|
|
583
|
+
next if param_details.key? tag.name.to_s
|
|
584
|
+
next if tag.types.nil?
|
|
585
|
+
details = {
|
|
586
|
+
tagged: tag.types.join(', '),
|
|
587
|
+
qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, pin.full_context.namespace)
|
|
588
|
+
}
|
|
589
|
+
# don't complain about a param that didn't come from the pin we're looking at anyway
|
|
590
|
+
if details[:qualified].defined? ||
|
|
591
|
+
relevant_pin.parameter_names.include?(tag.name.to_s)
|
|
592
|
+
param_details[tag.name.to_s] = details
|
|
593
|
+
end
|
|
594
|
+
end
|
|
595
|
+
end
|
|
596
|
+
|
|
597
|
+
# @param pin [Pin::Signature]
|
|
598
|
+
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
599
|
+
def signature_param_details(pin)
|
|
600
|
+
# @type [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
601
|
+
result = {}
|
|
602
|
+
pin.parameters.each do |param|
|
|
603
|
+
type = param.typify(api_map)
|
|
604
|
+
next if type.nil? || type.undefined?
|
|
605
|
+
result[param.name.to_s] = {
|
|
606
|
+
tagged: type.tags,
|
|
607
|
+
qualified: type
|
|
608
|
+
}
|
|
609
|
+
end
|
|
610
|
+
# see if we have additional tags to pay attention to from YARD -
|
|
611
|
+
# e.g., kwargs in a **restkwargs splat
|
|
612
|
+
tags = pin.docstring.tags(:param)
|
|
613
|
+
tags.each do |tag|
|
|
614
|
+
next if result.key? tag.name.to_s
|
|
615
|
+
next if tag.types.nil?
|
|
616
|
+
result[tag.name.to_s] = {
|
|
617
|
+
tagged: tag.types.join(', '),
|
|
618
|
+
# @sg-ignore need to add a nil check here
|
|
619
|
+
qualified: Solargraph::ComplexType.try_parse(*tag.types).qualify(api_map, *pin.closure.gates)
|
|
620
|
+
}
|
|
621
|
+
end
|
|
622
|
+
result
|
|
623
|
+
end
|
|
624
|
+
|
|
625
|
+
# The original signature defines the parameters, but other
|
|
626
|
+
# signatures and method pins can help by adding type information
|
|
627
|
+
#
|
|
628
|
+
# @param param_details [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
629
|
+
# @param param_names [Array<String>]
|
|
630
|
+
# @param new_param_details [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
631
|
+
#
|
|
632
|
+
# @return [void]
|
|
633
|
+
def add_to_param_details(param_details, param_names, new_param_details)
|
|
634
|
+
new_param_details.each do |param_name, details|
|
|
635
|
+
next unless param_names.include?(param_name)
|
|
636
|
+
|
|
637
|
+
param_details[param_name] ||= {}
|
|
638
|
+
param_details[param_name][:tagged] ||= details[:tagged]
|
|
639
|
+
param_details[param_name][:qualified] ||= details[:qualified]
|
|
640
|
+
end
|
|
641
|
+
end
|
|
642
|
+
|
|
643
|
+
# @param signature [Pin::Signature]
|
|
644
|
+
# @param method_pin_stack [Array<Pin::Method>]
|
|
645
|
+
# @return [Hash{String => Hash{Symbol => String, ComplexType}}]
|
|
646
|
+
def param_details_from_stack(signature, method_pin_stack)
|
|
647
|
+
signature_type = signature.typify(api_map)
|
|
648
|
+
signature = signature.proxy signature_type
|
|
649
|
+
param_details = signature_param_details(signature)
|
|
650
|
+
param_names = signature.parameter_names
|
|
651
|
+
|
|
652
|
+
method_pin_stack.each do |method_pin|
|
|
653
|
+
add_restkwarg_param_tag_details(param_details, method_pin, signature)
|
|
654
|
+
|
|
655
|
+
# documentation of types in superclasses should fail back to
|
|
656
|
+
# subclasses if the subclass hasn't documented something
|
|
657
|
+
method_pin.signatures.each do |sig|
|
|
658
|
+
add_restkwarg_param_tag_details(param_details, sig, signature)
|
|
659
|
+
add_to_param_details param_details, param_names, signature_param_details(sig)
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
param_details
|
|
663
|
+
end
|
|
664
|
+
|
|
665
|
+
# @param pin [Pin::Base]
|
|
666
|
+
def internal? pin
|
|
667
|
+
return false if pin.nil?
|
|
668
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
669
|
+
pin.location && api_map.bundled?(pin.location.filename)
|
|
670
|
+
end
|
|
671
|
+
|
|
672
|
+
# True if the pin is either internal (part of the workspace) or from the core/stdlib
|
|
673
|
+
# @param pin [Pin::Base]
|
|
674
|
+
def internal_or_core? pin
|
|
675
|
+
# @todo RBS pins are not necessarily core/stdlib pins
|
|
676
|
+
internal?(pin) || pin.source == :rbs
|
|
677
|
+
end
|
|
678
|
+
|
|
679
|
+
# @param pin [Pin::Base]
|
|
680
|
+
def external? pin
|
|
681
|
+
!internal? pin
|
|
682
|
+
end
|
|
683
|
+
|
|
684
|
+
# @param pin [Pin::BaseVariable]
|
|
685
|
+
def declared_externally? pin
|
|
686
|
+
raise "No assignment found" if pin.assignment.nil?
|
|
687
|
+
|
|
688
|
+
chain = Solargraph::Parser.chain(pin.assignment, filename)
|
|
689
|
+
# @sg-ignore flow sensitive typing needs to handle attrs
|
|
690
|
+
rng = Solargraph::Range.from_node(pin.assignment)
|
|
691
|
+
# @sg-ignore Need to add nil check here
|
|
692
|
+
closure_pin = source_map.locate_closure_pin(rng.start.line, rng.start.column)
|
|
693
|
+
# @sg-ignore flow sensitive typing needs to handle "if foo.nil?"
|
|
694
|
+
location = Location.new(filename, Range.from_node(pin.assignment))
|
|
695
|
+
locals = source_map.locals_at(location)
|
|
696
|
+
type = chain.infer(api_map, closure_pin, locals)
|
|
697
|
+
if type.undefined? && !rules.ignore_all_undefined?
|
|
698
|
+
base = chain
|
|
699
|
+
missing = chain
|
|
700
|
+
# @type [Solargraph::Pin::Base, nil]
|
|
701
|
+
found = nil
|
|
702
|
+
# @type [Array<Solargraph::Pin::Base>]
|
|
703
|
+
all_found = []
|
|
704
|
+
closest = ComplexType::UNDEFINED
|
|
705
|
+
until base.links.first.undefined?
|
|
706
|
+
all_found = base.define(api_map, closure_pin, locals)
|
|
707
|
+
found = all_found.first
|
|
708
|
+
break if found
|
|
709
|
+
missing = base
|
|
710
|
+
base = base.base
|
|
711
|
+
end
|
|
712
|
+
all_closest = all_found.map { |pin| pin.typify(api_map) }
|
|
713
|
+
closest = ComplexType.new(all_closest.flat_map(&:items).uniq)
|
|
714
|
+
if !found || closest.defined? || internal?(found)
|
|
715
|
+
return false
|
|
716
|
+
end
|
|
717
|
+
end
|
|
718
|
+
true
|
|
719
|
+
end
|
|
720
|
+
|
|
721
|
+
# @param pin [Pin::Method]
|
|
722
|
+
# @param arguments [Array<Source::Chain>]
|
|
723
|
+
# @param location [Location]
|
|
724
|
+
# @return [Array<Problem>]
|
|
725
|
+
def arity_problems_for pin, arguments, location
|
|
726
|
+
results = pin.signatures.map do |sig|
|
|
727
|
+
r = parameterized_arity_problems_for(pin, sig.parameters, arguments, location)
|
|
728
|
+
return [] if r.empty?
|
|
729
|
+
r
|
|
730
|
+
end
|
|
731
|
+
results.first
|
|
732
|
+
end
|
|
733
|
+
|
|
734
|
+
# @param pin [Pin::Method]
|
|
735
|
+
# @param parameters [Array<Pin::Parameter>]
|
|
736
|
+
# @param arguments [Array<Source::Chain>]
|
|
737
|
+
# @param location [Location]
|
|
738
|
+
# @return [Array<Problem>]
|
|
739
|
+
def parameterized_arity_problems_for(pin, parameters, arguments, location)
|
|
740
|
+
return [] unless pin.explicit?
|
|
741
|
+
return [] if parameters.empty? && arguments.empty?
|
|
742
|
+
return [] if pin.anon_splat?
|
|
743
|
+
unchecked = arguments.dup # creates copy of and unthaws array
|
|
744
|
+
add_params = 0
|
|
745
|
+
if unchecked.empty? && parameters.any? { |param| param.decl == :kwarg }
|
|
746
|
+
return [Problem.new(location, "Missing keyword arguments to #{pin.path}")]
|
|
747
|
+
end
|
|
748
|
+
settled_kwargs = 0
|
|
749
|
+
unless unchecked.empty?
|
|
750
|
+
if any_splatted_call?(unchecked.map(&:node))
|
|
751
|
+
settled_kwargs = parameters.count(&:keyword?)
|
|
752
|
+
else
|
|
753
|
+
kwargs = convert_hash(unchecked.last.node)
|
|
754
|
+
if parameters.any? { |param| [:kwarg, :kwoptarg].include?(param.decl) || param.kwrestarg? }
|
|
755
|
+
if kwargs.empty?
|
|
756
|
+
add_params += 1
|
|
757
|
+
else
|
|
758
|
+
unchecked.pop
|
|
759
|
+
parameters.each do |param|
|
|
760
|
+
next unless param.keyword?
|
|
761
|
+
if kwargs.key?(param.name.to_sym)
|
|
762
|
+
kwargs.delete param.name.to_sym
|
|
763
|
+
settled_kwargs += 1
|
|
764
|
+
elsif param.decl == :kwarg
|
|
765
|
+
last_arg_last_link = arguments.last.links.last
|
|
766
|
+
return [] if last_arg_last_link.is_a?(Solargraph::Source::Chain::Hash) && last_arg_last_link.splatted?
|
|
767
|
+
return [Problem.new(location, "Missing keyword argument #{param.name} to #{pin.path}")]
|
|
768
|
+
end
|
|
769
|
+
end
|
|
770
|
+
kwargs.clear if parameters.any?(&:kwrestarg?)
|
|
771
|
+
unless kwargs.empty?
|
|
772
|
+
return [Problem.new(location, "Unrecognized keyword argument #{kwargs.keys.first} to #{pin.path}")]
|
|
773
|
+
end
|
|
774
|
+
end
|
|
775
|
+
end
|
|
776
|
+
end
|
|
777
|
+
end
|
|
778
|
+
req = required_param_count(parameters)
|
|
779
|
+
if req + add_params < unchecked.length
|
|
780
|
+
return [] if parameters.any?(&:rest?)
|
|
781
|
+
opt = optional_param_count(parameters)
|
|
782
|
+
return [] if unchecked.length <= req + opt
|
|
783
|
+
if req + add_params + 1 == unchecked.length && any_splatted_call?(unchecked.map(&:node)) && (parameters.map(&:decl) & [:kwarg, :kwoptarg, :kwrestarg]).any?
|
|
784
|
+
return []
|
|
785
|
+
end
|
|
786
|
+
return [] if arguments.length - req == parameters.select { |p| [:optarg, :kwoptarg].include?(p.decl) }.length
|
|
787
|
+
return [Problem.new(location, "Too many arguments to #{pin.path}")]
|
|
788
|
+
elsif unchecked.length < req - settled_kwargs && (arguments.empty? || (!arguments.last.splat? && !arguments.last.links.last.is_a?(Solargraph::Source::Chain::Hash)))
|
|
789
|
+
# HACK: Kernel#raise signature is incorrect in Ruby 2.7 core docs.
|
|
790
|
+
# See https://github.com/castwide/solargraph/issues/418
|
|
791
|
+
unless arguments.empty? && pin.path == 'Kernel#raise'
|
|
792
|
+
return [Problem.new(location, "Not enough arguments to #{pin.path}")]
|
|
793
|
+
end
|
|
794
|
+
end
|
|
795
|
+
[]
|
|
796
|
+
end
|
|
797
|
+
|
|
798
|
+
# @param parameters [Enumerable<Pin::Parameter>]
|
|
799
|
+
# @todo need to use generic types in method to choose correct
|
|
800
|
+
# signature and generate Integer as return type
|
|
801
|
+
# @return [Integer]
|
|
802
|
+
def required_param_count(parameters)
|
|
803
|
+
parameters.sum { |param| %i[arg kwarg].include?(param.decl) ? 1 : 0 }
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
# @param parameters [Enumerable<Pin::Parameter>]
|
|
807
|
+
# @param pin [Pin::Method]
|
|
808
|
+
# @return [Integer]
|
|
809
|
+
def optional_param_count(parameters)
|
|
810
|
+
parameters.select { |p| p.decl == :optarg }.length
|
|
811
|
+
end
|
|
812
|
+
|
|
813
|
+
# @param pin [Pin::Method]
|
|
814
|
+
# @sg-ignore need boolish support for ? methods
|
|
815
|
+
def abstract? pin
|
|
816
|
+
pin.docstring.has_tag?('abstract') ||
|
|
817
|
+
# @sg-ignore of low sensitive typing needs to handle ivars
|
|
818
|
+
(pin.closure && pin.closure.docstring.has_tag?('abstract'))
|
|
819
|
+
end
|
|
820
|
+
|
|
821
|
+
# @param pin [Pin::Method]
|
|
822
|
+
# @return [Array<Source::Chain>]
|
|
823
|
+
def fake_args_for(pin)
|
|
824
|
+
args = []
|
|
825
|
+
with_opts = false
|
|
826
|
+
with_block = false
|
|
827
|
+
# @param pin [Pin::Parameter]
|
|
828
|
+
pin.parameters.each do |pin|
|
|
829
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
830
|
+
if [:kwarg, :kwoptarg, :kwrestarg].include?(pin.decl)
|
|
831
|
+
with_opts = true
|
|
832
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
833
|
+
elsif pin.decl == :block
|
|
834
|
+
with_block = true
|
|
835
|
+
# @sg-ignore flow sensitive typing should be able to handle redefinition
|
|
836
|
+
elsif pin.decl == :restarg
|
|
837
|
+
args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)], nil, true)
|
|
838
|
+
else
|
|
839
|
+
args.push Solargraph::Source::Chain.new([Solargraph::Source::Chain::Variable.new(pin.name)])
|
|
840
|
+
end
|
|
841
|
+
end
|
|
842
|
+
pin_location = pin.location
|
|
843
|
+
starting_line = pin_location ? pin_location.range.start.line : 0
|
|
844
|
+
args.push Solargraph::Parser.chain_string('{}', filename, starting_line) if with_opts
|
|
845
|
+
args.push Solargraph::Parser.chain_string('&', filename, starting_line) if with_block
|
|
846
|
+
args
|
|
847
|
+
end
|
|
848
|
+
|
|
849
|
+
# @return [Set<Integer>]
|
|
850
|
+
def sg_ignore_lines_processed
|
|
851
|
+
@sg_ignore_lines_processed ||= Set.new
|
|
852
|
+
end
|
|
853
|
+
|
|
854
|
+
# @return [Set<Integer>]
|
|
855
|
+
def all_sg_ignore_lines
|
|
856
|
+
source.associated_comments.select do |_line, text|
|
|
857
|
+
# @sg-ignore Need to add nil check here
|
|
858
|
+
text.include?('@sg-ignore')
|
|
859
|
+
end.keys.to_set
|
|
860
|
+
end
|
|
861
|
+
|
|
862
|
+
# @return [Array<Integer>]
|
|
863
|
+
def unprocessed_sg_ignore_lines
|
|
864
|
+
(all_sg_ignore_lines - sg_ignore_lines_processed).to_a.sort
|
|
865
|
+
end
|
|
866
|
+
|
|
867
|
+
# @return [Array<Problem>]
|
|
868
|
+
def unneeded_sgignore_problems
|
|
869
|
+
return [] unless rules.validate_sg_ignores?
|
|
870
|
+
|
|
871
|
+
unprocessed_sg_ignore_lines.map do |line|
|
|
872
|
+
Problem.new(
|
|
873
|
+
Location.new(filename, Range.from_to(line, 0, line, 0)),
|
|
874
|
+
'Unneeded @sg-ignore comment'
|
|
875
|
+
)
|
|
876
|
+
end
|
|
877
|
+
end
|
|
878
|
+
|
|
879
|
+
# @param problems [Array<Problem>]
|
|
880
|
+
# @return [Array<Problem>]
|
|
881
|
+
def without_ignored problems
|
|
882
|
+
problems.reject do |problem|
|
|
883
|
+
node = source.node_at(problem.location.range.start.line, problem.location.range.start.column)
|
|
884
|
+
ignored = node && source.comments_for(node)&.include?('@sg-ignore')
|
|
885
|
+
unless !ignored || all_sg_ignore_lines.include?(problem.location.range.start.line)
|
|
886
|
+
# :nocov:
|
|
887
|
+
Solargraph.assert_or_log(:sg_ignore) { "@sg-ignore accounting issue - node is #{node}" }
|
|
888
|
+
# :nocov:
|
|
889
|
+
end
|
|
890
|
+
sg_ignore_lines_processed.add problem.location.range.start.line if ignored
|
|
891
|
+
ignored
|
|
892
|
+
end
|
|
893
|
+
end
|
|
894
|
+
end
|
|
895
|
+
end
|