type-guessr 0.0.2 → 0.0.4
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/README.md +3 -3
- data/lib/ruby_lsp/type_guessr/addon.rb +4 -5
- data/lib/ruby_lsp/type_guessr/code_index_adapter.rb +18 -1
- data/lib/ruby_lsp/type_guessr/{graph_builder.rb → debug_graph_builder.rb} +3 -3
- data/lib/ruby_lsp/type_guessr/debug_server.rb +2 -2
- data/lib/ruby_lsp/type_guessr/dsl/activerecord_adapter.rb +404 -0
- data/lib/ruby_lsp/type_guessr/dsl/ar_schema_watcher.rb +96 -0
- data/lib/ruby_lsp/type_guessr/dsl/ar_type_mapper.rb +51 -0
- data/lib/ruby_lsp/type_guessr/dsl.rb +3 -0
- data/lib/ruby_lsp/type_guessr/dsl_type_registrar.rb +60 -0
- data/lib/ruby_lsp/type_guessr/hover.rb +46 -40
- data/lib/ruby_lsp/type_guessr/rails_server_addon.rb +83 -0
- data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +90 -16
- data/lib/type-guessr.rb +2 -13
- data/lib/type_guessr/core/cache/gem_signature_cache.rb +3 -2
- data/lib/type_guessr/core/cache.rb +5 -0
- data/lib/{ruby_lsp/type_guessr → type_guessr/core}/config.rb +2 -2
- data/lib/type_guessr/core/converter/call_converter.rb +161 -0
- data/lib/type_guessr/core/converter/container_mutation_converter.rb +241 -0
- data/lib/type_guessr/core/converter/context.rb +144 -0
- data/lib/type_guessr/core/converter/control_flow_converter.rb +425 -0
- data/lib/type_guessr/core/converter/definition_converter.rb +312 -0
- data/lib/type_guessr/core/converter/literal_converter.rb +217 -0
- data/lib/type_guessr/core/converter/prism_converter.rb +9 -1682
- data/lib/type_guessr/core/converter/rbs_converter.rb +15 -1
- data/lib/type_guessr/core/converter/registration.rb +100 -0
- data/lib/type_guessr/core/converter/variable_converter.rb +225 -0
- data/lib/type_guessr/core/converter.rb +4 -0
- data/lib/type_guessr/core/index.rb +3 -0
- data/lib/type_guessr/core/inference/resolver.rb +206 -208
- data/lib/type_guessr/core/inference.rb +4 -0
- data/lib/type_guessr/core/ir.rb +3 -0
- data/lib/type_guessr/core/logger.rb +3 -5
- data/lib/type_guessr/core/registry/method_registry.rb +9 -0
- data/lib/type_guessr/core/registry/signature_registry.rb +73 -16
- data/lib/type_guessr/core/registry.rb +6 -0
- data/lib/type_guessr/core/type_serializer.rb +18 -14
- data/lib/type_guessr/core/type_simplifier.rb +5 -5
- data/lib/type_guessr/core/types.rb +64 -22
- data/lib/type_guessr/core.rb +29 -0
- data/lib/type_guessr/mcp/server.rb +55 -46
- data/lib/type_guessr/mcp/standalone_runtime.rb +70 -110
- data/lib/type_guessr/version.rb +1 -1
- metadata +24 -4
- data/.mcp.json +0 -9
|
@@ -1,12 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "../ir
|
|
3
|
+
require_relative "../ir"
|
|
4
4
|
require_relative "../types"
|
|
5
5
|
require_relative "../type_simplifier"
|
|
6
|
-
require_relative "../registry
|
|
7
|
-
require_relative "../registry/instance_variable_registry"
|
|
8
|
-
require_relative "../registry/class_variable_registry"
|
|
9
|
-
require_relative "../registry/signature_registry"
|
|
6
|
+
require_relative "../registry"
|
|
10
7
|
require_relative "result"
|
|
11
8
|
|
|
12
9
|
module TypeGuessr
|
|
@@ -110,16 +107,12 @@ module TypeGuessr
|
|
|
110
107
|
case node
|
|
111
108
|
when IR::LiteralNode
|
|
112
109
|
infer_literal(node)
|
|
113
|
-
when IR::LocalWriteNode
|
|
114
|
-
|
|
110
|
+
when IR::LocalWriteNode, IR::InstanceVariableWriteNode, IR::ClassVariableWriteNode
|
|
111
|
+
infer_variable_write(node)
|
|
115
112
|
when IR::LocalReadNode
|
|
116
113
|
infer_local_read(node)
|
|
117
|
-
when IR::InstanceVariableWriteNode
|
|
118
|
-
infer_instance_variable_write(node)
|
|
119
114
|
when IR::InstanceVariableReadNode
|
|
120
115
|
infer_instance_variable_read(node)
|
|
121
|
-
when IR::ClassVariableWriteNode
|
|
122
|
-
infer_class_variable_write(node)
|
|
123
116
|
when IR::ClassVariableReadNode
|
|
124
117
|
infer_class_variable_read(node)
|
|
125
118
|
when IR::ParamNode
|
|
@@ -151,8 +144,7 @@ module TypeGuessr
|
|
|
151
144
|
Result.new(node.type, "literal", :literal)
|
|
152
145
|
end
|
|
153
146
|
|
|
154
|
-
private def
|
|
155
|
-
# Early return: variable declared but no value assigned (shouldn't happen in valid Ruby)
|
|
147
|
+
private def infer_variable_write(node)
|
|
156
148
|
return Result.new(Types::Unknown.instance, "unassigned variable", :unknown) unless node.value
|
|
157
149
|
|
|
158
150
|
dep_result = infer(node.value)
|
|
@@ -182,14 +174,6 @@ module TypeGuessr
|
|
|
182
174
|
write_result
|
|
183
175
|
end
|
|
184
176
|
|
|
185
|
-
private def infer_instance_variable_write(node)
|
|
186
|
-
# Early return: @var declared but no value assigned (shouldn't happen in valid Ruby)
|
|
187
|
-
return Result.new(Types::Unknown.instance, "unassigned instance variable", :unknown) unless node.value
|
|
188
|
-
|
|
189
|
-
dep_result = infer(node.value)
|
|
190
|
-
Result.new(dep_result.type, "assigned from #{dep_result.reason}", dep_result.source)
|
|
191
|
-
end
|
|
192
|
-
|
|
193
177
|
private def infer_instance_variable_read(node)
|
|
194
178
|
write_node = node.write_node
|
|
195
179
|
|
|
@@ -202,14 +186,6 @@ module TypeGuessr
|
|
|
202
186
|
infer(write_node)
|
|
203
187
|
end
|
|
204
188
|
|
|
205
|
-
private def infer_class_variable_write(node)
|
|
206
|
-
# Early return: @@var declared but no value assigned (shouldn't happen in valid Ruby)
|
|
207
|
-
return Result.new(Types::Unknown.instance, "unassigned class variable", :unknown) unless node.value
|
|
208
|
-
|
|
209
|
-
dep_result = infer(node.value)
|
|
210
|
-
Result.new(dep_result.type, "assigned from #{dep_result.reason}", dep_result.source)
|
|
211
|
-
end
|
|
212
|
-
|
|
213
189
|
private def infer_class_variable_read(node)
|
|
214
190
|
write_node = node.write_node
|
|
215
191
|
|
|
@@ -296,204 +272,222 @@ module TypeGuessr
|
|
|
296
272
|
end
|
|
297
273
|
|
|
298
274
|
private def infer_call(node)
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
result = infer_class_method_call(class_name, node)
|
|
309
|
-
# Early return: ClassName.method resolved successfully
|
|
275
|
+
return infer_constant_receiver_call(node) if node.receiver.is_a?(IR::ConstantNode)
|
|
276
|
+
return infer_no_receiver_call(node) unless node.receiver
|
|
277
|
+
|
|
278
|
+
receiver_type = infer(node.receiver).type
|
|
279
|
+
|
|
280
|
+
case receiver_type
|
|
281
|
+
when Types::SingletonType
|
|
282
|
+
result = infer_class_method_call(receiver_type.name, node)
|
|
310
283
|
return result if result
|
|
284
|
+
when Types::ClassInstance then return infer_class_instance_call(node, receiver_type)
|
|
285
|
+
when Types::ArrayType then return infer_array_call(node, receiver_type)
|
|
286
|
+
when Types::TupleType then return infer_tuple_call(node, receiver_type)
|
|
287
|
+
when Types::HashShape then return infer_hash_shape_call(node, receiver_type)
|
|
288
|
+
when Types::RangeType then return infer_range_call(node, receiver_type)
|
|
289
|
+
when Types::HashType then return infer_hash_type_call(node, receiver_type)
|
|
290
|
+
when Types::Unknown then return infer_unknown_receiver_call(node, receiver_type)
|
|
311
291
|
end
|
|
312
292
|
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
receiver_result = infer(node.receiver)
|
|
316
|
-
receiver_type = receiver_result.type
|
|
317
|
-
|
|
318
|
-
# Query for method return type: project first, then RBS
|
|
319
|
-
case receiver_type
|
|
320
|
-
when Types::SingletonType
|
|
321
|
-
result = infer_class_method_call(receiver_type.name, node)
|
|
322
|
-
# Early return: singleton.method resolved successfully
|
|
323
|
-
return result if result
|
|
324
|
-
when Types::ClassInstance
|
|
325
|
-
# 1. Try project methods first
|
|
326
|
-
def_node = @method_registry.lookup(receiver_type.name, node.method.to_s)
|
|
327
|
-
# Early return: project method found - use project inference
|
|
328
|
-
if def_node
|
|
329
|
-
return_result = infer(def_node)
|
|
330
|
-
return Result.new(
|
|
331
|
-
return_result.type,
|
|
332
|
-
"#{receiver_type.name}##{node.method} (project)",
|
|
333
|
-
:project
|
|
334
|
-
)
|
|
335
|
-
end
|
|
336
|
-
|
|
337
|
-
# 2. Fall back to RBS signature provider
|
|
338
|
-
arg_types = node.args.map { |arg| infer(arg).type }
|
|
339
|
-
return_type = @signature_registry.get_method_return_type(
|
|
340
|
-
receiver_type.name,
|
|
341
|
-
node.method.to_s,
|
|
342
|
-
arg_types
|
|
343
|
-
)
|
|
293
|
+
infer_fallback_call(node, receiver_type)
|
|
294
|
+
end
|
|
344
295
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
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
|
-
# Handle Range methods with element type substitution
|
|
417
|
-
substitutions = build_substitutions(receiver_type)
|
|
418
|
-
add_method_type_var_substitutions(substitutions, node, "Range", node.method.to_s)
|
|
419
|
-
raw_return_type = @signature_registry.get_method_return_type("Range", node.method.to_s)
|
|
420
|
-
return_type = raw_return_type.substitute(substitutions)
|
|
421
|
-
return Result.new(
|
|
422
|
-
return_type,
|
|
423
|
-
"Range[#{receiver_type.element_type}]##{node.method}",
|
|
424
|
-
:stdlib
|
|
425
|
-
)
|
|
426
|
-
when Types::HashType
|
|
427
|
-
# Handle generic HashType
|
|
428
|
-
substitutions = build_substitutions(receiver_type)
|
|
429
|
-
add_method_type_var_substitutions(substitutions, node, "Hash", node.method.to_s)
|
|
430
|
-
raw_return_type = @signature_registry.get_method_return_type("Hash", node.method.to_s)
|
|
431
|
-
return_type = raw_return_type.substitute(substitutions)
|
|
432
|
-
# Early return: HashType RBS lookup with type variable substitution
|
|
296
|
+
# ClassName.method — resolve constant alias, then class method lookup
|
|
297
|
+
private def infer_constant_receiver_call(node)
|
|
298
|
+
receiver_result = infer(node.receiver)
|
|
299
|
+
class_name = case receiver_result.type
|
|
300
|
+
when Types::SingletonType then receiver_result.type.name
|
|
301
|
+
else node.receiver.name
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
result = infer_class_method_call(class_name, node)
|
|
305
|
+
return result if result
|
|
306
|
+
|
|
307
|
+
# Constant receiver but class method not found — fall through to no-receiver fallback
|
|
308
|
+
infer_no_receiver_call(node)
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# receiver.method where receiver is a ClassInstance — project method → DSL/RBS → Object fallback
|
|
312
|
+
private def infer_class_instance_call(node, receiver_type)
|
|
313
|
+
# 1. Try project methods first
|
|
314
|
+
def_node = @method_registry.lookup(receiver_type.name, node.method.to_s)
|
|
315
|
+
if def_node
|
|
316
|
+
return_result = infer(def_node)
|
|
317
|
+
return Result.new(
|
|
318
|
+
return_result.type,
|
|
319
|
+
"#{receiver_type.name}##{node.method} (project)",
|
|
320
|
+
:project
|
|
321
|
+
)
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# 2. Fall back to SignatureRegistry (DSL or RBS)
|
|
325
|
+
arg_types = node.args.map { |arg| infer(arg).type }
|
|
326
|
+
entry = @signature_registry.lookup(receiver_type.name, node.method.to_s)
|
|
327
|
+
source = entry.is_a?(Registry::SignatureRegistry::GemMethodEntry) && entry.skip_stdlib_rbs? ? :dsl : :stdlib
|
|
328
|
+
|
|
329
|
+
return_type = @signature_registry.get_method_return_type(
|
|
330
|
+
receiver_type.name,
|
|
331
|
+
node.method.to_s,
|
|
332
|
+
arg_types
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
# Fall back to Object if class-specific lookup returns Unknown
|
|
336
|
+
if return_type.is_a?(Types::Unknown) && receiver_type.name != "Object"
|
|
337
|
+
return_type = @signature_registry.get_method_return_type(
|
|
338
|
+
"Object",
|
|
339
|
+
node.method.to_s,
|
|
340
|
+
arg_types
|
|
341
|
+
)
|
|
342
|
+
source = :stdlib
|
|
343
|
+
end
|
|
344
|
+
|
|
345
|
+
# Substitute class-level type vars, self, block return type, and remaining type variables
|
|
346
|
+
substitutions = build_substitutions(receiver_type)
|
|
347
|
+
add_method_type_var_substitutions(substitutions, node, receiver_type.name, node.method.to_s, arg_types)
|
|
348
|
+
return_type = return_type.substitute(substitutions)
|
|
349
|
+
|
|
350
|
+
Result.new(
|
|
351
|
+
return_type,
|
|
352
|
+
"#{receiver_type.name}##{node.method}",
|
|
353
|
+
source
|
|
354
|
+
)
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
# Unknown receiver — infer receiver type from method uniqueness, then retry
|
|
358
|
+
private def infer_unknown_receiver_call(node, receiver_type)
|
|
359
|
+
cm = IR::CalledMethod.new(name: node.method, positional_count: nil, keywords: [])
|
|
360
|
+
inferred_receiver = resolve_called_methods([cm])
|
|
361
|
+
|
|
362
|
+
if inferred_receiver.is_a?(Types::ClassInstance)
|
|
363
|
+
# Try project methods with inferred receiver type
|
|
364
|
+
def_node = @method_registry.lookup(inferred_receiver.name, node.method.to_s)
|
|
365
|
+
if def_node
|
|
366
|
+
return_result = infer(def_node)
|
|
433
367
|
return Result.new(
|
|
434
|
-
|
|
435
|
-
"
|
|
436
|
-
:
|
|
368
|
+
return_result.type,
|
|
369
|
+
"#{inferred_receiver.name}##{node.method} (inferred receiver)",
|
|
370
|
+
:project
|
|
437
371
|
)
|
|
438
372
|
end
|
|
439
373
|
|
|
440
|
-
#
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
)
|
|
456
|
-
end
|
|
457
|
-
|
|
458
|
-
# Early return: inferred receiver → RBS lookup
|
|
459
|
-
arg_types = node.args.map { |arg| infer(arg).type }
|
|
460
|
-
return_type = @signature_registry.get_method_return_type(
|
|
461
|
-
inferred_receiver.name,
|
|
462
|
-
node.method.to_s,
|
|
463
|
-
arg_types
|
|
464
|
-
)
|
|
465
|
-
substitutions = { self: inferred_receiver }
|
|
466
|
-
add_method_type_var_substitutions(substitutions, node, inferred_receiver.name, node.method.to_s, arg_types)
|
|
467
|
-
return_type = return_type.substitute(substitutions)
|
|
468
|
-
return Result.new(
|
|
469
|
-
return_type,
|
|
470
|
-
"#{inferred_receiver.name}##{node.method} (inferred receiver)",
|
|
471
|
-
:stdlib
|
|
472
|
-
)
|
|
473
|
-
end
|
|
474
|
-
end
|
|
374
|
+
# RBS lookup with inferred receiver
|
|
375
|
+
arg_types = node.args.map { |arg| infer(arg).type }
|
|
376
|
+
return_type = @signature_registry.get_method_return_type(
|
|
377
|
+
inferred_receiver.name,
|
|
378
|
+
node.method.to_s,
|
|
379
|
+
arg_types
|
|
380
|
+
)
|
|
381
|
+
substitutions = build_substitutions(inferred_receiver)
|
|
382
|
+
add_method_type_var_substitutions(substitutions, node, inferred_receiver.name, node.method.to_s, arg_types)
|
|
383
|
+
return_type = return_type.substitute(substitutions)
|
|
384
|
+
return Result.new(
|
|
385
|
+
return_type,
|
|
386
|
+
"#{inferred_receiver.name}##{node.method} (inferred receiver)",
|
|
387
|
+
:stdlib
|
|
388
|
+
)
|
|
475
389
|
end
|
|
476
390
|
|
|
477
|
-
|
|
478
|
-
|
|
391
|
+
infer_fallback_call(node, receiver_type)
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# No receiver — top-level method → Object RBS → Unknown
|
|
395
|
+
private def infer_no_receiver_call(node)
|
|
396
|
+
infer_fallback_call(node, nil)
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
# Shared fallback: top-level method → Object RBS → Unknown
|
|
400
|
+
private def infer_fallback_call(node, receiver_type)
|
|
401
|
+
# Try top-level method first
|
|
479
402
|
def_node = @method_registry.lookup("", node.method.to_s)
|
|
480
|
-
# Early return: top-level project method found
|
|
481
403
|
if def_node
|
|
482
404
|
return_type = infer(def_node.return_node)
|
|
483
405
|
return Result.new(return_type.type, "top-level method #{node.method}", :project)
|
|
484
406
|
end
|
|
485
407
|
|
|
486
|
-
#
|
|
408
|
+
# Object RBS for common methods (==, to_s, etc.)
|
|
487
409
|
arg_types = node.args.map { |arg| infer(arg).type }
|
|
488
410
|
return_type = @signature_registry.get_method_return_type("Object", node.method.to_s, arg_types)
|
|
489
|
-
# Substitute self with receiver type if available (e.g., Object#dup returns self)
|
|
490
411
|
return_type = return_type.substitute({ self: receiver_type }) if receiver_type
|
|
491
|
-
# Early return: Object common method found in RBS
|
|
492
412
|
return Result.new(return_type, "Object##{node.method}", :stdlib) unless return_type.is_a?(Types::Unknown)
|
|
493
413
|
|
|
494
414
|
Result.new(Types::Unknown.instance, "call #{node.method} on unknown receiver", :unknown)
|
|
495
415
|
end
|
|
496
416
|
|
|
417
|
+
private def infer_array_call(node, receiver_type)
|
|
418
|
+
substitutions = build_substitutions(receiver_type)
|
|
419
|
+
add_method_type_var_substitutions(substitutions, node, "Array", node.method.to_s)
|
|
420
|
+
raw_return_type = @signature_registry.get_method_return_type("Array", node.method.to_s)
|
|
421
|
+
return_type = raw_return_type.substitute(substitutions)
|
|
422
|
+
Result.new(
|
|
423
|
+
return_type,
|
|
424
|
+
"Array[#{receiver_type.element_type || "untyped"}]##{node.method}",
|
|
425
|
+
:stdlib
|
|
426
|
+
)
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
private def infer_tuple_call(node, receiver_type)
|
|
430
|
+
# Handle indexed access with integer literal
|
|
431
|
+
if node.method == :[] && node.args.size == 1
|
|
432
|
+
tuple_result = infer_tuple_access(receiver_type, node.args.first)
|
|
433
|
+
return tuple_result if tuple_result
|
|
434
|
+
end
|
|
435
|
+
|
|
436
|
+
# Fall back to Array RBS for other methods
|
|
437
|
+
substitutions = build_substitutions(receiver_type)
|
|
438
|
+
add_method_type_var_substitutions(substitutions, node, "Array", node.method.to_s)
|
|
439
|
+
raw_return_type = @signature_registry.get_method_return_type("Array", node.method.to_s)
|
|
440
|
+
return_type = raw_return_type.substitute(substitutions)
|
|
441
|
+
Result.new(
|
|
442
|
+
return_type,
|
|
443
|
+
"[#{receiver_type.element_types.join(", ")}]##{node.method}",
|
|
444
|
+
:stdlib
|
|
445
|
+
)
|
|
446
|
+
end
|
|
447
|
+
|
|
448
|
+
private def infer_hash_shape_call(node, receiver_type)
|
|
449
|
+
# Handle HashShape field access with [] method
|
|
450
|
+
if node.method == :[] && node.args.size == 1
|
|
451
|
+
key_result = infer_hash_shape_access(receiver_type, node.args.first)
|
|
452
|
+
return key_result if key_result
|
|
453
|
+
end
|
|
454
|
+
|
|
455
|
+
# Fall back to Hash RBS for other methods
|
|
456
|
+
substitutions = build_substitutions(receiver_type)
|
|
457
|
+
add_method_type_var_substitutions(substitutions, node, "Hash", node.method.to_s)
|
|
458
|
+
raw_return_type = @signature_registry.get_method_return_type("Hash", node.method.to_s)
|
|
459
|
+
return_type = raw_return_type.substitute(substitutions)
|
|
460
|
+
Result.new(
|
|
461
|
+
return_type,
|
|
462
|
+
"HashShape##{node.method}",
|
|
463
|
+
:stdlib
|
|
464
|
+
)
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
private def infer_range_call(node, receiver_type)
|
|
468
|
+
substitutions = build_substitutions(receiver_type)
|
|
469
|
+
add_method_type_var_substitutions(substitutions, node, "Range", node.method.to_s)
|
|
470
|
+
raw_return_type = @signature_registry.get_method_return_type("Range", node.method.to_s)
|
|
471
|
+
return_type = raw_return_type.substitute(substitutions)
|
|
472
|
+
Result.new(
|
|
473
|
+
return_type,
|
|
474
|
+
"Range[#{receiver_type.element_type}]##{node.method}",
|
|
475
|
+
:stdlib
|
|
476
|
+
)
|
|
477
|
+
end
|
|
478
|
+
|
|
479
|
+
private def infer_hash_type_call(node, receiver_type)
|
|
480
|
+
substitutions = build_substitutions(receiver_type)
|
|
481
|
+
add_method_type_var_substitutions(substitutions, node, "Hash", node.method.to_s)
|
|
482
|
+
raw_return_type = @signature_registry.get_method_return_type("Hash", node.method.to_s)
|
|
483
|
+
return_type = raw_return_type.substitute(substitutions)
|
|
484
|
+
Result.new(
|
|
485
|
+
return_type,
|
|
486
|
+
"Hash[#{receiver_type.key_type}, #{receiver_type.value_type}]##{node.method}",
|
|
487
|
+
:stdlib
|
|
488
|
+
)
|
|
489
|
+
end
|
|
490
|
+
|
|
497
491
|
private def infer_block_param_slot(node)
|
|
498
492
|
# Early return: top-level block (each { } without receiver class context)
|
|
499
493
|
return Result.new(Types::Unknown.instance, "block param without type info", :unknown) unless node.call_node.receiver
|
|
@@ -692,20 +686,24 @@ module TypeGuessr
|
|
|
692
686
|
end
|
|
693
687
|
end
|
|
694
688
|
|
|
695
|
-
# Fall back to
|
|
689
|
+
# Fall back to SignatureRegistry (DSL or RBS)
|
|
696
690
|
arg_types = node.args.map { |arg| infer(arg).type }
|
|
691
|
+
class_entry = @signature_registry.lookup_class_method(class_name, node.method.to_s)
|
|
692
|
+
class_source = class_entry.is_a?(Registry::SignatureRegistry::GemMethodEntry) && class_entry.skip_stdlib_rbs? ? :dsl : :rbs
|
|
693
|
+
|
|
697
694
|
return_type = @signature_registry.get_class_method_return_type(
|
|
698
695
|
class_name,
|
|
699
696
|
node.method.to_s,
|
|
700
697
|
arg_types
|
|
701
698
|
)
|
|
702
699
|
|
|
703
|
-
# Early return:
|
|
700
|
+
# Early return: class method found — substitute SelfType with actual class
|
|
704
701
|
unless return_type.is_a?(Types::Unknown)
|
|
702
|
+
return_type = return_type.substitute(self: Types::ClassInstance.for(class_name))
|
|
705
703
|
return Result.new(
|
|
706
704
|
return_type,
|
|
707
|
-
"#{class_name}.#{node.method}
|
|
708
|
-
|
|
705
|
+
"#{class_name}.#{node.method}",
|
|
706
|
+
class_source
|
|
709
707
|
)
|
|
710
708
|
end
|
|
711
709
|
|
|
@@ -806,7 +804,7 @@ module TypeGuessr
|
|
|
806
804
|
# @param receiver_type [Type] The receiver type
|
|
807
805
|
# @return [Hash{Symbol => Type}] Substitutions including :self
|
|
808
806
|
private def build_substitutions(receiver_type)
|
|
809
|
-
substitutions = receiver_type.
|
|
807
|
+
substitutions = receiver_type.type_parameter_bindings.dup
|
|
810
808
|
substitutions[:self] = receiver_type
|
|
811
809
|
substitutions
|
|
812
810
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "
|
|
3
|
+
require_relative "config"
|
|
4
4
|
|
|
5
5
|
module TypeGuessr
|
|
6
6
|
module Core
|
|
@@ -19,19 +19,17 @@ module TypeGuessr
|
|
|
19
19
|
# @param msg [String] the error message
|
|
20
20
|
# @param exception [Exception, nil] optional exception for backtrace
|
|
21
21
|
module_function def error(msg, exception = nil)
|
|
22
|
-
return unless debug_enabled?
|
|
23
|
-
|
|
24
22
|
warn "[TypeGuessr:ERROR] #{msg}"
|
|
25
23
|
return unless exception
|
|
26
24
|
|
|
27
25
|
warn " #{exception.class}: #{exception.message}"
|
|
28
|
-
warn exception.backtrace
|
|
26
|
+
warn exception.backtrace&.first(5)&.map { |l| " #{l}" }&.join("\n")
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
# Check if debug mode is enabled
|
|
32
30
|
# @return [Boolean] true if Config.debug? returns true
|
|
33
31
|
module_function def debug_enabled?
|
|
34
|
-
|
|
32
|
+
TypeGuessr::Core::Config.debug?
|
|
35
33
|
end
|
|
36
34
|
end
|
|
37
35
|
end
|
|
@@ -98,6 +98,15 @@ module TypeGuessr
|
|
|
98
98
|
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
# Get the source file path for a registered method
|
|
102
|
+
# @param class_name [String] Class name
|
|
103
|
+
# @param method_name [String] Method name
|
|
104
|
+
# @return [String, nil] File path or nil
|
|
105
|
+
def source_file_for(class_name, method_name)
|
|
106
|
+
sources = @entry_sources[[class_name, method_name]]
|
|
107
|
+
sources&.first
|
|
108
|
+
end
|
|
109
|
+
|
|
101
110
|
# Search for methods matching a pattern
|
|
102
111
|
# @param pattern [String] Search pattern (partial match on "ClassName#method_name")
|
|
103
112
|
# @return [Array<Array>] Array of [class_name, method_name, def_node]
|