type-guessr 0.0.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +89 -0
- data/lib/ruby_lsp/type_guessr/addon.rb +138 -0
- data/lib/ruby_lsp/type_guessr/config.rb +90 -0
- data/lib/ruby_lsp/type_guessr/debug_server.rb +861 -0
- data/lib/ruby_lsp/type_guessr/graph_builder.rb +349 -0
- data/lib/ruby_lsp/type_guessr/hover.rb +565 -0
- data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +506 -0
- data/lib/ruby_lsp/type_guessr/type_inferrer.rb +200 -0
- data/lib/type-guessr.rb +28 -0
- data/lib/type_guessr/core/converter/prism_converter.rb +1649 -0
- data/lib/type_guessr/core/converter/rbs_converter.rb +88 -0
- data/lib/type_guessr/core/index/location_index.rb +72 -0
- data/lib/type_guessr/core/inference/resolver.rb +664 -0
- data/lib/type_guessr/core/inference/result.rb +41 -0
- data/lib/type_guessr/core/ir/nodes.rb +599 -0
- data/lib/type_guessr/core/logger.rb +43 -0
- data/lib/type_guessr/core/rbs_provider.rb +304 -0
- data/lib/type_guessr/core/registry/method_registry.rb +106 -0
- data/lib/type_guessr/core/registry/variable_registry.rb +87 -0
- data/lib/type_guessr/core/signature_provider.rb +101 -0
- data/lib/type_guessr/core/type_simplifier.rb +64 -0
- data/lib/type_guessr/core/types.rb +425 -0
- data/lib/type_guessr/version.rb +5 -0
- metadata +81 -0
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "rbs"
|
|
4
|
+
require_relative "../types"
|
|
5
|
+
|
|
6
|
+
module TypeGuessr
|
|
7
|
+
module Core
|
|
8
|
+
module Converter
|
|
9
|
+
# Converts RBS types to internal type system
|
|
10
|
+
# Isolates RBS type dependencies from the core type inference logic
|
|
11
|
+
#
|
|
12
|
+
# This class only handles conversion (RBS → internal types).
|
|
13
|
+
# Type variable substitution is handled separately by Type#substitute.
|
|
14
|
+
class RBSConverter
|
|
15
|
+
# Convert RBS type to internal type system
|
|
16
|
+
# @param rbs_type [RBS::Types::t] the RBS type
|
|
17
|
+
# @return [Types::Type] internal type representation (TypeVariables preserved)
|
|
18
|
+
def convert(rbs_type)
|
|
19
|
+
case rbs_type
|
|
20
|
+
when RBS::Types::Variable
|
|
21
|
+
Types::TypeVariable.new(rbs_type.name)
|
|
22
|
+
when RBS::Types::ClassInstance
|
|
23
|
+
convert_class_instance(rbs_type)
|
|
24
|
+
when RBS::Types::ClassSingleton
|
|
25
|
+
# Class singleton type (e.g., singleton(String))
|
|
26
|
+
class_name = rbs_type.name.to_s.delete_prefix("::")
|
|
27
|
+
Types::SingletonType.new(class_name)
|
|
28
|
+
when RBS::Types::Union
|
|
29
|
+
convert_union(rbs_type)
|
|
30
|
+
when RBS::Types::Tuple
|
|
31
|
+
convert_tuple(rbs_type)
|
|
32
|
+
when RBS::Types::Bases::Bool
|
|
33
|
+
# bool is a type alias for TrueClass | FalseClass
|
|
34
|
+
Types::ClassInstance.new("bool")
|
|
35
|
+
when RBS::Types::Bases::Void
|
|
36
|
+
Types::ClassInstance.new("void")
|
|
37
|
+
when RBS::Types::Bases::Nil
|
|
38
|
+
Types::ClassInstance.new("NilClass")
|
|
39
|
+
when RBS::Types::Bases::Self
|
|
40
|
+
Types::SelfType.instance
|
|
41
|
+
when RBS::Types::Bases::Instance
|
|
42
|
+
# Cannot resolve without context - return Unknown
|
|
43
|
+
Types::Unknown.instance
|
|
44
|
+
else
|
|
45
|
+
# Unknown RBS type - return Unknown
|
|
46
|
+
Types::Unknown.instance
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
# Convert RBS ClassInstance to internal type
|
|
53
|
+
# @param rbs_type [RBS::Types::ClassInstance] RBS class instance
|
|
54
|
+
# @return [Types::Type] internal type
|
|
55
|
+
def convert_class_instance(rbs_type)
|
|
56
|
+
class_name = rbs_type.name.to_s.delete_prefix("::")
|
|
57
|
+
|
|
58
|
+
# Handle Array with type parameter
|
|
59
|
+
if class_name == "Array" && rbs_type.args.size == 1
|
|
60
|
+
element_type = convert(rbs_type.args.first)
|
|
61
|
+
return Types::ArrayType.new(element_type)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# For other generic types, return ClassInstance (ignore type args for now)
|
|
65
|
+
# TODO: Add HashType with key/value types in the future
|
|
66
|
+
Types::ClassInstance.new(class_name)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Convert RBS Union to internal Union type
|
|
70
|
+
# @param rbs_type [RBS::Types::Union] RBS union type
|
|
71
|
+
# @return [Types::Union] internal union type
|
|
72
|
+
def convert_union(rbs_type)
|
|
73
|
+
types = rbs_type.types.map { |t| convert(t) }
|
|
74
|
+
Types::Union.new(types)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Convert RBS Tuple to internal ArrayType
|
|
78
|
+
# Tuples like [K, V] are converted to Array[K | V]
|
|
79
|
+
# @param rbs_type [RBS::Types::Tuple] RBS tuple type
|
|
80
|
+
# @return [Types::ArrayType] internal array type with union element type
|
|
81
|
+
def convert_tuple(rbs_type)
|
|
82
|
+
element_types = rbs_type.types.map { |t| convert(t) }
|
|
83
|
+
Types::ArrayType.new(Types::Union.new(element_types))
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module TypeGuessr
|
|
4
|
+
module Core
|
|
5
|
+
module Index
|
|
6
|
+
# Key-based index for finding IR nodes by scope and content
|
|
7
|
+
# Provides O(1) lookup from node_key to IR node
|
|
8
|
+
class LocationIndex
|
|
9
|
+
def initialize
|
|
10
|
+
# Hash of node_key => node for O(1) lookup
|
|
11
|
+
@key_index = {}
|
|
12
|
+
# Hash of file_path => [node_keys] for file removal
|
|
13
|
+
@file_keys = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Index an IR node with its scope_id
|
|
17
|
+
# @param file_path [String] Absolute file path
|
|
18
|
+
# @param node [TypeGuessr::Core::IR::Node] IR node to index
|
|
19
|
+
# @param scope_id [String] Scope identifier (e.g., "User#save")
|
|
20
|
+
def add(file_path, node, scope_id = "")
|
|
21
|
+
return unless node.loc
|
|
22
|
+
|
|
23
|
+
key = node.node_key(scope_id)
|
|
24
|
+
@key_index[key] = node
|
|
25
|
+
(@file_keys[file_path] ||= []) << key
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Finalize indexing (no-op, kept for API compatibility)
|
|
29
|
+
def finalize!
|
|
30
|
+
# No longer needed with key-based index
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Find IR node by its unique key
|
|
34
|
+
# @param node_key [String] The node key (scope_id + node_hash)
|
|
35
|
+
# @return [TypeGuessr::Core::IR::Node, nil] IR node or nil if not found
|
|
36
|
+
def find_by_key(node_key)
|
|
37
|
+
@key_index[node_key]
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Get all indexed nodes for a file
|
|
41
|
+
# @param file_path [String] Absolute file path
|
|
42
|
+
# @return [Array<TypeGuessr::Core::IR::Node>] All nodes in the file
|
|
43
|
+
def nodes_for_file(file_path)
|
|
44
|
+
keys = @file_keys[file_path] || []
|
|
45
|
+
keys.filter_map { |k| @key_index[k] }
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Remove all entries for a file
|
|
49
|
+
# @param file_path [String] Absolute file path
|
|
50
|
+
def remove_file(file_path)
|
|
51
|
+
@file_keys[file_path]&.each { |k| @key_index.delete(k) }
|
|
52
|
+
@file_keys.delete(file_path)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Clear all indexed data
|
|
56
|
+
def clear
|
|
57
|
+
@key_index.clear
|
|
58
|
+
@file_keys.clear
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Get statistics about the index
|
|
62
|
+
# @return [Hash] Statistics hash
|
|
63
|
+
def stats
|
|
64
|
+
{
|
|
65
|
+
files_count: @file_keys.size,
|
|
66
|
+
total_nodes: @key_index.size
|
|
67
|
+
}
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|