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.
@@ -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