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.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/lib/ruby_lsp/type_guessr/addon.rb +4 -5
  4. data/lib/ruby_lsp/type_guessr/code_index_adapter.rb +18 -1
  5. data/lib/ruby_lsp/type_guessr/{graph_builder.rb → debug_graph_builder.rb} +3 -3
  6. data/lib/ruby_lsp/type_guessr/debug_server.rb +2 -2
  7. data/lib/ruby_lsp/type_guessr/dsl/activerecord_adapter.rb +404 -0
  8. data/lib/ruby_lsp/type_guessr/dsl/ar_schema_watcher.rb +96 -0
  9. data/lib/ruby_lsp/type_guessr/dsl/ar_type_mapper.rb +51 -0
  10. data/lib/ruby_lsp/type_guessr/dsl.rb +3 -0
  11. data/lib/ruby_lsp/type_guessr/dsl_type_registrar.rb +60 -0
  12. data/lib/ruby_lsp/type_guessr/hover.rb +46 -40
  13. data/lib/ruby_lsp/type_guessr/rails_server_addon.rb +83 -0
  14. data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +90 -16
  15. data/lib/type-guessr.rb +2 -13
  16. data/lib/type_guessr/core/cache/gem_signature_cache.rb +3 -2
  17. data/lib/type_guessr/core/cache.rb +5 -0
  18. data/lib/{ruby_lsp/type_guessr → type_guessr/core}/config.rb +2 -2
  19. data/lib/type_guessr/core/converter/call_converter.rb +161 -0
  20. data/lib/type_guessr/core/converter/container_mutation_converter.rb +241 -0
  21. data/lib/type_guessr/core/converter/context.rb +144 -0
  22. data/lib/type_guessr/core/converter/control_flow_converter.rb +425 -0
  23. data/lib/type_guessr/core/converter/definition_converter.rb +312 -0
  24. data/lib/type_guessr/core/converter/literal_converter.rb +217 -0
  25. data/lib/type_guessr/core/converter/prism_converter.rb +9 -1682
  26. data/lib/type_guessr/core/converter/rbs_converter.rb +15 -1
  27. data/lib/type_guessr/core/converter/registration.rb +100 -0
  28. data/lib/type_guessr/core/converter/variable_converter.rb +225 -0
  29. data/lib/type_guessr/core/converter.rb +4 -0
  30. data/lib/type_guessr/core/index.rb +3 -0
  31. data/lib/type_guessr/core/inference/resolver.rb +206 -208
  32. data/lib/type_guessr/core/inference.rb +4 -0
  33. data/lib/type_guessr/core/ir.rb +3 -0
  34. data/lib/type_guessr/core/logger.rb +3 -5
  35. data/lib/type_guessr/core/registry/method_registry.rb +9 -0
  36. data/lib/type_guessr/core/registry/signature_registry.rb +73 -16
  37. data/lib/type_guessr/core/registry.rb +6 -0
  38. data/lib/type_guessr/core/type_serializer.rb +18 -14
  39. data/lib/type_guessr/core/type_simplifier.rb +5 -5
  40. data/lib/type_guessr/core/types.rb +64 -22
  41. data/lib/type_guessr/core.rb +29 -0
  42. data/lib/type_guessr/mcp/server.rb +55 -46
  43. data/lib/type_guessr/mcp/standalone_runtime.rb +70 -110
  44. data/lib/type_guessr/version.rb +1 -1
  45. metadata +24 -4
  46. data/.mcp.json +0 -9
@@ -1,12 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../ir/nodes"
3
+ require_relative "../ir"
4
4
  require_relative "../types"
5
5
  require_relative "../type_simplifier"
6
- require_relative "../registry/method_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
- infer_local_write(node)
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 infer_local_write(node)
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
- # Special case: Class method calls (ClassName.method)
300
- if node.receiver.is_a?(IR::ConstantNode)
301
- # Resolve constant first (handles aliases like RecipeAlias = Recipe)
302
- receiver_result = infer(node.receiver)
303
- class_name = case receiver_result.type
304
- when Types::SingletonType then receiver_result.type.name
305
- else node.receiver.name
306
- end
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
- # Infer receiver type first
314
- if node.receiver
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
- # Fall back to Object if class-specific lookup returns Unknown
346
- if return_type.is_a?(Types::Unknown) && receiver_type.name != "Object"
347
- return_type = @signature_registry.get_method_return_type(
348
- "Object",
349
- node.method.to_s,
350
- arg_types
351
- )
352
- end
353
-
354
- # Substitute self, block return type, and remaining type variables
355
- substitutions = { self: receiver_type }
356
- add_method_type_var_substitutions(substitutions, node, receiver_type.name, node.method.to_s, arg_types)
357
- return_type = return_type.substitute(substitutions)
358
-
359
- # Early return: ClassInstance RBS lookup (may be Unknown if not found)
360
- return Result.new(
361
- return_type,
362
- "#{receiver_type.name}##{node.method}",
363
- :stdlib
364
- )
365
- when Types::ArrayType
366
- # Handle Array methods with element type substitution
367
- substitutions = build_substitutions(receiver_type)
368
- add_method_type_var_substitutions(substitutions, node, "Array", node.method.to_s)
369
-
370
- # Get raw return type, then substitute type variables
371
- raw_return_type = @signature_registry.get_method_return_type("Array", node.method.to_s)
372
- return_type = raw_return_type.substitute(substitutions)
373
- # Early return: ArrayType RBS lookup with type variable substitution
374
- return Result.new(
375
- return_type,
376
- "Array[#{receiver_type.element_type || "untyped"}]##{node.method}",
377
- :stdlib
378
- )
379
- when Types::TupleType
380
- # Handle indexed access with integer literal
381
- if node.method == :[] && node.args.size == 1
382
- tuple_result = infer_tuple_access(receiver_type, node.args.first)
383
- return tuple_result if tuple_result
384
- end
385
-
386
- # Fall back to Array RBS for other methods
387
- substitutions = build_substitutions(receiver_type)
388
- add_method_type_var_substitutions(substitutions, node, "Array", node.method.to_s)
389
- raw_return_type = @signature_registry.get_method_return_type("Array", node.method.to_s)
390
- return_type = raw_return_type.substitute(substitutions)
391
- return Result.new(
392
- return_type,
393
- "[#{receiver_type.element_types.join(", ")}]##{node.method}",
394
- :stdlib
395
- )
396
- when Types::HashShape
397
- # Handle HashShape field access with [] method
398
- if node.method == :[] && node.args.size == 1
399
- key_result = infer_hash_shape_access(receiver_type, node.args.first)
400
- # Early return: hash[:key] with known symbol key resolved to field type
401
- return key_result if key_result
402
- end
403
-
404
- # Fall back to Hash RBS for other methods
405
- substitutions = build_substitutions(receiver_type)
406
- add_method_type_var_substitutions(substitutions, node, "Hash", node.method.to_s)
407
- raw_return_type = @signature_registry.get_method_return_type("Hash", node.method.to_s)
408
- return_type = raw_return_type.substitute(substitutions)
409
- # Early return: HashShape RBS lookup for non-[] methods
410
- return Result.new(
411
- return_type,
412
- "HashShape##{node.method}",
413
- :stdlib
414
- )
415
- when Types::RangeType
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
- return_type,
435
- "Hash[#{receiver_type.key_type}, #{receiver_type.value_type}]##{node.method}",
436
- :stdlib
368
+ return_result.type,
369
+ "#{inferred_receiver.name}##{node.method} (inferred receiver)",
370
+ :project
437
371
  )
438
372
  end
439
373
 
440
- # Fallback: try to infer Unknown receiver type from method uniqueness
441
- if receiver_type.is_a?(Types::Unknown)
442
- # Create CalledMethod with nil positional_count to skip signature matching
443
- cm = IR::CalledMethod.new(name: node.method, positional_count: nil, keywords: [])
444
- inferred_receiver = resolve_called_methods([cm])
445
- if inferred_receiver.is_a?(Types::ClassInstance)
446
- # Try project methods with inferred receiver type
447
- def_node = @method_registry.lookup(inferred_receiver.name, node.method.to_s)
448
- # Early return: inferred receiver → project method found
449
- if def_node
450
- return_result = infer(def_node)
451
- return Result.new(
452
- return_result.type,
453
- "#{inferred_receiver.name}##{node.method} (inferred receiver)",
454
- :project
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
- # Fallback: method call without receiver or unknown receiver type
478
- # First, try to lookup top-level method
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
- # Fallback to Object to query RBS for common methods (==, to_s, etc.)
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 RBS signature provider
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: RBS class method found
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} (RBS)",
708
- :rbs
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.type_variable_substitutions.dup
807
+ substitutions = receiver_type.type_parameter_bindings.dup
810
808
  substitutions[:self] = receiver_type
811
809
  substitutions
812
810
  end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "inference/result"
4
+ require_relative "inference/resolver"
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "ir/nodes"
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../ruby_lsp/type_guessr/config"
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.first(5).map { |l| " #{l}" }.join("\n")
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
- RubyLsp::TypeGuessr::Config.debug?
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]