type-guessr 0.0.1 → 0.0.3
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 +41 -0
- data/exe/type-guessr +30 -0
- data/lib/ruby_lsp/type_guessr/addon.rb +20 -45
- data/lib/ruby_lsp/type_guessr/code_index_adapter.rb +352 -0
- data/lib/ruby_lsp/type_guessr/constants.rb +39 -0
- data/lib/ruby_lsp/type_guessr/{graph_builder.rb → debug_graph_builder.rb} +27 -22
- data/lib/ruby_lsp/type_guessr/debug_server.rb +20 -17
- 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 +129 -261
- data/lib/ruby_lsp/type_guessr/rails_server_addon.rb +83 -0
- data/lib/ruby_lsp/type_guessr/runtime_adapter.rb +613 -277
- data/lib/ruby_lsp/type_guessr/type_inferrer.rb +8 -105
- data/lib/type-guessr.rb +3 -11
- data/lib/type_guessr/core/cache/gem_dependency_resolver.rb +113 -0
- data/lib/type_guessr/core/cache/gem_signature_cache.rb +98 -0
- data/lib/type_guessr/core/cache/gem_signature_extractor.rb +87 -0
- data/lib/type_guessr/core/cache.rb +5 -0
- data/lib/{ruby_lsp/type_guessr → type_guessr/core}/config.rb +19 -34
- 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 +246 -0
- data/lib/type_guessr/core/converter/literal_converter.rb +217 -0
- data/lib/type_guessr/core/converter/prism_converter.rb +154 -1613
- data/lib/type_guessr/core/converter/rbs_converter.rb +35 -14
- 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/location_index.rb +32 -0
- data/lib/type_guessr/core/index.rb +3 -0
- data/lib/type_guessr/core/inference/resolver.rb +516 -349
- data/lib/type_guessr/core/inference.rb +4 -0
- data/lib/type_guessr/core/ir/nodes.rb +362 -103
- data/lib/type_guessr/core/ir.rb +3 -0
- data/lib/type_guessr/core/logger.rb +6 -13
- data/lib/type_guessr/core/node_context_helper.rb +126 -0
- data/lib/type_guessr/core/node_key_generator.rb +31 -0
- data/lib/type_guessr/core/registry/class_variable_registry.rb +63 -0
- data/lib/type_guessr/core/registry/instance_variable_registry.rb +84 -0
- data/lib/type_guessr/core/registry/method_registry.rb +65 -38
- data/lib/type_guessr/core/registry/signature_registry.rb +543 -0
- data/lib/type_guessr/core/registry.rb +6 -0
- data/lib/type_guessr/core/signature_builder.rb +39 -0
- data/lib/type_guessr/core/type_serializer.rb +96 -0
- data/lib/type_guessr/core/type_simplifier.rb +15 -12
- data/lib/type_guessr/core/types.rb +250 -32
- data/lib/type_guessr/core.rb +29 -0
- data/lib/type_guessr/mcp/file_watcher.rb +87 -0
- data/lib/type_guessr/mcp/server.rb +463 -0
- data/lib/type_guessr/mcp/standalone_runtime.rb +213 -0
- data/lib/type_guessr/version.rb +1 -1
- metadata +57 -8
- data/lib/type_guessr/core/rbs_provider.rb +0 -304
- data/lib/type_guessr/core/registry/variable_registry.rb +0 -87
- data/lib/type_guessr/core/signature_provider.rb +0 -101
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: type-guessr
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.0.
|
|
4
|
+
version: 0.0.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- riseshia
|
|
@@ -9,6 +9,20 @@ bindir: exe
|
|
|
9
9
|
cert_chain: []
|
|
10
10
|
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: mcp
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.6'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.6'
|
|
12
26
|
- !ruby/object:Gem::Dependency
|
|
13
27
|
name: ruby-lsp
|
|
14
28
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -26,33 +40,68 @@ dependencies:
|
|
|
26
40
|
description: TypeGuessr provides heuristic type inference
|
|
27
41
|
email:
|
|
28
42
|
- ''
|
|
29
|
-
executables:
|
|
43
|
+
executables:
|
|
44
|
+
- type-guessr
|
|
30
45
|
extensions: []
|
|
31
46
|
extra_rdoc_files: []
|
|
32
47
|
files:
|
|
33
48
|
- LICENSE
|
|
34
49
|
- README.md
|
|
50
|
+
- exe/type-guessr
|
|
35
51
|
- lib/ruby_lsp/type_guessr/addon.rb
|
|
36
|
-
- lib/ruby_lsp/type_guessr/
|
|
52
|
+
- lib/ruby_lsp/type_guessr/code_index_adapter.rb
|
|
53
|
+
- lib/ruby_lsp/type_guessr/constants.rb
|
|
54
|
+
- lib/ruby_lsp/type_guessr/debug_graph_builder.rb
|
|
37
55
|
- lib/ruby_lsp/type_guessr/debug_server.rb
|
|
38
|
-
- lib/ruby_lsp/type_guessr/
|
|
56
|
+
- lib/ruby_lsp/type_guessr/dsl.rb
|
|
57
|
+
- lib/ruby_lsp/type_guessr/dsl/activerecord_adapter.rb
|
|
58
|
+
- lib/ruby_lsp/type_guessr/dsl/ar_schema_watcher.rb
|
|
59
|
+
- lib/ruby_lsp/type_guessr/dsl/ar_type_mapper.rb
|
|
60
|
+
- lib/ruby_lsp/type_guessr/dsl_type_registrar.rb
|
|
39
61
|
- lib/ruby_lsp/type_guessr/hover.rb
|
|
62
|
+
- lib/ruby_lsp/type_guessr/rails_server_addon.rb
|
|
40
63
|
- lib/ruby_lsp/type_guessr/runtime_adapter.rb
|
|
41
64
|
- lib/ruby_lsp/type_guessr/type_inferrer.rb
|
|
42
65
|
- lib/type-guessr.rb
|
|
66
|
+
- lib/type_guessr/core.rb
|
|
67
|
+
- lib/type_guessr/core/cache.rb
|
|
68
|
+
- lib/type_guessr/core/cache/gem_dependency_resolver.rb
|
|
69
|
+
- lib/type_guessr/core/cache/gem_signature_cache.rb
|
|
70
|
+
- lib/type_guessr/core/cache/gem_signature_extractor.rb
|
|
71
|
+
- lib/type_guessr/core/config.rb
|
|
72
|
+
- lib/type_guessr/core/converter.rb
|
|
73
|
+
- lib/type_guessr/core/converter/call_converter.rb
|
|
74
|
+
- lib/type_guessr/core/converter/container_mutation_converter.rb
|
|
75
|
+
- lib/type_guessr/core/converter/context.rb
|
|
76
|
+
- lib/type_guessr/core/converter/control_flow_converter.rb
|
|
77
|
+
- lib/type_guessr/core/converter/definition_converter.rb
|
|
78
|
+
- lib/type_guessr/core/converter/literal_converter.rb
|
|
43
79
|
- lib/type_guessr/core/converter/prism_converter.rb
|
|
44
80
|
- lib/type_guessr/core/converter/rbs_converter.rb
|
|
81
|
+
- lib/type_guessr/core/converter/registration.rb
|
|
82
|
+
- lib/type_guessr/core/converter/variable_converter.rb
|
|
83
|
+
- lib/type_guessr/core/index.rb
|
|
45
84
|
- lib/type_guessr/core/index/location_index.rb
|
|
85
|
+
- lib/type_guessr/core/inference.rb
|
|
46
86
|
- lib/type_guessr/core/inference/resolver.rb
|
|
47
87
|
- lib/type_guessr/core/inference/result.rb
|
|
88
|
+
- lib/type_guessr/core/ir.rb
|
|
48
89
|
- lib/type_guessr/core/ir/nodes.rb
|
|
49
90
|
- lib/type_guessr/core/logger.rb
|
|
50
|
-
- lib/type_guessr/core/
|
|
91
|
+
- lib/type_guessr/core/node_context_helper.rb
|
|
92
|
+
- lib/type_guessr/core/node_key_generator.rb
|
|
93
|
+
- lib/type_guessr/core/registry.rb
|
|
94
|
+
- lib/type_guessr/core/registry/class_variable_registry.rb
|
|
95
|
+
- lib/type_guessr/core/registry/instance_variable_registry.rb
|
|
51
96
|
- lib/type_guessr/core/registry/method_registry.rb
|
|
52
|
-
- lib/type_guessr/core/registry/
|
|
53
|
-
- lib/type_guessr/core/
|
|
97
|
+
- lib/type_guessr/core/registry/signature_registry.rb
|
|
98
|
+
- lib/type_guessr/core/signature_builder.rb
|
|
99
|
+
- lib/type_guessr/core/type_serializer.rb
|
|
54
100
|
- lib/type_guessr/core/type_simplifier.rb
|
|
55
101
|
- lib/type_guessr/core/types.rb
|
|
102
|
+
- lib/type_guessr/mcp/file_watcher.rb
|
|
103
|
+
- lib/type_guessr/mcp/server.rb
|
|
104
|
+
- lib/type_guessr/mcp/standalone_runtime.rb
|
|
56
105
|
- lib/type_guessr/version.rb
|
|
57
106
|
homepage: https://github.com/riseshia/type-guessr
|
|
58
107
|
licenses: []
|
|
@@ -75,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
75
124
|
- !ruby/object:Gem::Version
|
|
76
125
|
version: '0'
|
|
77
126
|
requirements: []
|
|
78
|
-
rubygems_version:
|
|
127
|
+
rubygems_version: 4.0.3
|
|
79
128
|
specification_version: 4
|
|
80
129
|
summary: A heuristic type inference tool for Ruby
|
|
81
130
|
test_files: []
|
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "singleton"
|
|
4
|
-
require "rbs"
|
|
5
|
-
require_relative "types"
|
|
6
|
-
require_relative "logger"
|
|
7
|
-
require_relative "converter/rbs_converter"
|
|
8
|
-
|
|
9
|
-
module TypeGuessr
|
|
10
|
-
module Core
|
|
11
|
-
# RBSProvider loads and queries RBS signature information
|
|
12
|
-
# Provides lazy loading of RBS environment for method signatures
|
|
13
|
-
# Uses RBSConverter to convert RBS types to internal type system
|
|
14
|
-
class RBSProvider
|
|
15
|
-
include Singleton
|
|
16
|
-
|
|
17
|
-
# Represents a method signature from RBS
|
|
18
|
-
class Signature
|
|
19
|
-
attr_reader :method_type
|
|
20
|
-
|
|
21
|
-
def initialize(method_type)
|
|
22
|
-
@method_type = method_type
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def initialize
|
|
27
|
-
@env = nil
|
|
28
|
-
@loader = nil
|
|
29
|
-
@converter = Converter::RBSConverter.new
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Preload RBS environment (for eager loading during addon activation)
|
|
33
|
-
# @return [self]
|
|
34
|
-
def preload
|
|
35
|
-
ensure_environment_loaded
|
|
36
|
-
self
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
# Get method signatures for a class and method name
|
|
40
|
-
# @param class_name [String] the class name
|
|
41
|
-
# @param method_name [String] the method name
|
|
42
|
-
# @return [Array<Signature>] array of method signatures
|
|
43
|
-
def get_method_signatures(class_name, method_name)
|
|
44
|
-
ensure_environment_loaded
|
|
45
|
-
|
|
46
|
-
# Build the type name
|
|
47
|
-
type_name = build_type_name(class_name)
|
|
48
|
-
|
|
49
|
-
# Check if class exists in RBS before building (avoids RuntimeError for project classes)
|
|
50
|
-
return [] unless @env.class_decl?(type_name)
|
|
51
|
-
|
|
52
|
-
# Use RBS::DefinitionBuilder to get method definitions
|
|
53
|
-
builder = RBS::DefinitionBuilder.new(env: @env)
|
|
54
|
-
definition = builder.build_instance(type_name)
|
|
55
|
-
|
|
56
|
-
# Get the method definition
|
|
57
|
-
method_def = definition.methods[method_name.to_sym]
|
|
58
|
-
return [] unless method_def
|
|
59
|
-
|
|
60
|
-
# Return all method types (overloads)
|
|
61
|
-
method_def.method_types.map { |mt| Signature.new(mt) }
|
|
62
|
-
rescue RBS::NoTypeFoundError, RBS::NoSuperclassFoundError, RBS::NoMixinFoundError => _e
|
|
63
|
-
# Class not found in RBS
|
|
64
|
-
[]
|
|
65
|
-
rescue StandardError => e
|
|
66
|
-
# If anything goes wrong, return empty array
|
|
67
|
-
Logger.error("RBSProvider error", e)
|
|
68
|
-
[]
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
# Get the return type of a method call with overload resolution
|
|
72
|
-
# @param class_name [String] the receiver class name
|
|
73
|
-
# @param method_name [String] the method name
|
|
74
|
-
# @param arg_types [Array<Types::Type>] argument types for overload matching
|
|
75
|
-
# @return [Types::Type] the return type (Unknown if not found)
|
|
76
|
-
def get_method_return_type(class_name, method_name, arg_types = [])
|
|
77
|
-
signatures = get_method_signatures(class_name, method_name)
|
|
78
|
-
return Types::Unknown.instance if signatures.empty?
|
|
79
|
-
|
|
80
|
-
# Find best matching overload based on argument types
|
|
81
|
-
best_match = find_best_overload(signatures, arg_types)
|
|
82
|
-
return_type = best_match.method_type.type.return_type
|
|
83
|
-
|
|
84
|
-
@converter.convert(return_type)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
# Get block parameter types for a method
|
|
88
|
-
# @param class_name [String] the receiver class name
|
|
89
|
-
# @param method_name [String] the method name
|
|
90
|
-
# @return [Array<Types::Type>] array of block parameter types (empty if no block)
|
|
91
|
-
def get_block_param_types(class_name, method_name)
|
|
92
|
-
block_sig = find_block_signature(class_name, method_name)
|
|
93
|
-
return [] unless block_sig
|
|
94
|
-
|
|
95
|
-
extract_block_param_types(block_sig)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Get class method signatures (singleton methods like File.read, Array.new)
|
|
99
|
-
# @param class_name [String] the class name
|
|
100
|
-
# @param method_name [String] the method name
|
|
101
|
-
# @return [Array<Signature>] array of method signatures
|
|
102
|
-
def get_class_method_signatures(class_name, method_name)
|
|
103
|
-
ensure_environment_loaded
|
|
104
|
-
|
|
105
|
-
# Build the type name
|
|
106
|
-
type_name = build_type_name(class_name)
|
|
107
|
-
|
|
108
|
-
# Check if class exists in RBS before building (avoids RuntimeError for project classes)
|
|
109
|
-
return [] unless @env.class_decl?(type_name)
|
|
110
|
-
|
|
111
|
-
# Use RBS::DefinitionBuilder to get singleton method definitions
|
|
112
|
-
builder = RBS::DefinitionBuilder.new(env: @env)
|
|
113
|
-
definition = builder.build_singleton(type_name)
|
|
114
|
-
|
|
115
|
-
# Get the method definition
|
|
116
|
-
method_def = definition.methods[method_name.to_sym]
|
|
117
|
-
return [] unless method_def
|
|
118
|
-
|
|
119
|
-
# Return all method types (overloads)
|
|
120
|
-
method_def.method_types.map { |mt| Signature.new(mt) }
|
|
121
|
-
rescue RBS::NoTypeFoundError, RBS::NoSuperclassFoundError, RBS::NoMixinFoundError => _e
|
|
122
|
-
# Class not found in RBS
|
|
123
|
-
[]
|
|
124
|
-
rescue StandardError => e
|
|
125
|
-
# If anything goes wrong, return empty array
|
|
126
|
-
Logger.error("RBSProvider class method error", e)
|
|
127
|
-
[]
|
|
128
|
-
end
|
|
129
|
-
|
|
130
|
-
# Get the return type of a class method call
|
|
131
|
-
# @param class_name [String] the class name
|
|
132
|
-
# @param method_name [String] the method name
|
|
133
|
-
# @param arg_types [Array<Types::Type>] the argument types
|
|
134
|
-
# @return [Types::Type] the return type (Unknown if not found)
|
|
135
|
-
def get_class_method_return_type(class_name, method_name, arg_types = [])
|
|
136
|
-
signatures = get_class_method_signatures(class_name, method_name)
|
|
137
|
-
return Types::Unknown.instance if signatures.empty?
|
|
138
|
-
|
|
139
|
-
# Find best matching overload based on argument types
|
|
140
|
-
best_match = find_best_overload(signatures, arg_types)
|
|
141
|
-
return_type = best_match.method_type.type.return_type
|
|
142
|
-
|
|
143
|
-
@converter.convert(return_type)
|
|
144
|
-
end
|
|
145
|
-
|
|
146
|
-
private
|
|
147
|
-
|
|
148
|
-
# Find a method signature that has a block
|
|
149
|
-
# @param class_name [String] the receiver class name
|
|
150
|
-
# @param method_name [String] the method name
|
|
151
|
-
# @return [RBS::MethodType, nil] the method type with block, or nil
|
|
152
|
-
def find_block_signature(class_name, method_name)
|
|
153
|
-
signatures = get_method_signatures(class_name, method_name)
|
|
154
|
-
return nil if signatures.empty?
|
|
155
|
-
|
|
156
|
-
# Find the signature with a block
|
|
157
|
-
sig_with_block = signatures.find { |s| s.method_type.block }
|
|
158
|
-
sig_with_block&.method_type
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
# Extract block parameter types from a method type
|
|
162
|
-
# @param method_type [RBS::MethodType] the method type
|
|
163
|
-
# @param substitutions [Hash{Symbol => Types::Type}] type variable substitutions (e.g., { Elem: Integer })
|
|
164
|
-
# @return [Array<Types::Type>] array of parameter types
|
|
165
|
-
def extract_block_param_types(method_type, substitutions: {})
|
|
166
|
-
return [] unless method_type.block
|
|
167
|
-
|
|
168
|
-
block_func = method_type.block.type
|
|
169
|
-
block_func.required_positionals.flat_map do |param|
|
|
170
|
-
# Handle Tuple types (e.g., [K, V] in Hash#each) by flattening
|
|
171
|
-
raw_types = if param.type.is_a?(RBS::Types::Tuple)
|
|
172
|
-
param.type.types.map { |t| @converter.convert(t) }
|
|
173
|
-
else
|
|
174
|
-
[@converter.convert(param.type)]
|
|
175
|
-
end
|
|
176
|
-
# Apply substitutions after conversion
|
|
177
|
-
raw_types.map { |t| t.substitute(substitutions) }
|
|
178
|
-
end
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
def ensure_environment_loaded
|
|
182
|
-
return if @env
|
|
183
|
-
|
|
184
|
-
@loader = RBS::EnvironmentLoader.new
|
|
185
|
-
|
|
186
|
-
# Add optional library paths if they exist
|
|
187
|
-
# loader.add(path: Pathname("sig")) if Dir.exist?("sig")
|
|
188
|
-
|
|
189
|
-
# Load environment (this automatically loads core/stdlib)
|
|
190
|
-
@env = RBS::Environment.from_loader(@loader).resolve_type_names
|
|
191
|
-
rescue StandardError => e
|
|
192
|
-
Logger.error("Failed to load RBS environment", e)
|
|
193
|
-
# Fallback to empty environment
|
|
194
|
-
@env = RBS::Environment.new
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
def build_type_name(class_name)
|
|
198
|
-
# Parse class name to handle namespaced classes like "Foo::Bar"
|
|
199
|
-
parts = class_name.split("::")
|
|
200
|
-
namespace_parts = parts[0...-1]
|
|
201
|
-
name = parts.last
|
|
202
|
-
|
|
203
|
-
namespace = if namespace_parts.empty?
|
|
204
|
-
RBS::Namespace.root
|
|
205
|
-
else
|
|
206
|
-
RBS::Namespace.parse(namespace_parts.join("::"))
|
|
207
|
-
end
|
|
208
|
-
|
|
209
|
-
RBS::TypeName.new(name: name.to_sym, namespace: namespace)
|
|
210
|
-
end
|
|
211
|
-
|
|
212
|
-
# Find the best matching overload for given argument types
|
|
213
|
-
# @param signatures [Array<Signature>] available overloads
|
|
214
|
-
# @param arg_types [Array<Types::Type>] argument types
|
|
215
|
-
# @return [Signature] best matching signature (first if no match)
|
|
216
|
-
def find_best_overload(signatures, arg_types)
|
|
217
|
-
return signatures.first if arg_types.empty?
|
|
218
|
-
|
|
219
|
-
# Score each overload
|
|
220
|
-
scored = signatures.map do |sig|
|
|
221
|
-
score = calculate_overload_score(sig.method_type, arg_types)
|
|
222
|
-
[sig, score]
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Return best scoring overload, or first if all scores are 0
|
|
226
|
-
best = scored.max_by { |_sig, score| score }
|
|
227
|
-
best[1].positive? ? best[0] : signatures.first
|
|
228
|
-
end
|
|
229
|
-
|
|
230
|
-
# Calculate match score for an overload
|
|
231
|
-
# @param method_type [RBS::MethodType] the method type
|
|
232
|
-
# @param arg_types [Array<Types::Type>] argument types
|
|
233
|
-
# @return [Integer] score (higher = better match)
|
|
234
|
-
def calculate_overload_score(method_type, arg_types)
|
|
235
|
-
func = method_type.type
|
|
236
|
-
required = func.required_positionals
|
|
237
|
-
optional = func.optional_positionals
|
|
238
|
-
rest = func.rest_positionals
|
|
239
|
-
|
|
240
|
-
# Check argument count
|
|
241
|
-
min_args = required.size
|
|
242
|
-
max_args = rest ? Float::INFINITY : required.size + optional.size
|
|
243
|
-
return 0 unless arg_types.size.between?(min_args, max_args)
|
|
244
|
-
|
|
245
|
-
# Score each argument match
|
|
246
|
-
score = 0
|
|
247
|
-
arg_types.each_with_index do |arg_type, i|
|
|
248
|
-
param = if i < required.size
|
|
249
|
-
required[i]
|
|
250
|
-
elsif i < required.size + optional.size
|
|
251
|
-
optional[i - required.size]
|
|
252
|
-
else
|
|
253
|
-
rest
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
break unless param
|
|
257
|
-
|
|
258
|
-
score += type_match_score(arg_type, param.type)
|
|
259
|
-
end
|
|
260
|
-
|
|
261
|
-
score
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
# Calculate match score between our type and RBS parameter type
|
|
265
|
-
# @param our_type [Types::Type] our type representation
|
|
266
|
-
# @param rbs_type [RBS::Types::t] RBS type
|
|
267
|
-
# @return [Integer] score (0 = no match, 1 = weak match, 2 = exact match)
|
|
268
|
-
def type_match_score(our_type, rbs_type)
|
|
269
|
-
case rbs_type
|
|
270
|
-
when RBS::Types::ClassInstance
|
|
271
|
-
# Exact class match
|
|
272
|
-
class_name = rbs_type.name.to_s.delete_prefix("::")
|
|
273
|
-
return 2 if types_match_class?(our_type, class_name)
|
|
274
|
-
|
|
275
|
-
0
|
|
276
|
-
when RBS::Types::Union
|
|
277
|
-
# Check if our type matches any member
|
|
278
|
-
max_score = rbs_type.types.map { |t| type_match_score(our_type, t) }.max || 0
|
|
279
|
-
max_score.positive? ? 1 : 0
|
|
280
|
-
else
|
|
281
|
-
# Unknown RBS type - give weak match to avoid penalizing
|
|
282
|
-
1
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
|
|
286
|
-
# Check if our type matches a class name
|
|
287
|
-
# @param our_type [Types::Type] our type
|
|
288
|
-
# @param class_name [String] class name to match
|
|
289
|
-
# @return [Boolean] true if types match
|
|
290
|
-
def types_match_class?(our_type, class_name)
|
|
291
|
-
case our_type
|
|
292
|
-
when Types::ClassInstance
|
|
293
|
-
our_type.name == class_name
|
|
294
|
-
when Types::ArrayType
|
|
295
|
-
class_name == "Array"
|
|
296
|
-
when Types::HashShape
|
|
297
|
-
class_name == "Hash"
|
|
298
|
-
else
|
|
299
|
-
false
|
|
300
|
-
end
|
|
301
|
-
end
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
end
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module TypeGuessr
|
|
4
|
-
module Core
|
|
5
|
-
module Registry
|
|
6
|
-
# Stores and retrieves instance/class variable write nodes
|
|
7
|
-
# Supports inheritance chain traversal for instance variables when ancestry_provider is set
|
|
8
|
-
class VariableRegistry
|
|
9
|
-
# Callback for getting class ancestors
|
|
10
|
-
# @return [Proc, nil] A proc that takes class_name and returns array of ancestor names
|
|
11
|
-
attr_accessor :ancestry_provider
|
|
12
|
-
|
|
13
|
-
# @param ancestry_provider [Proc, nil] Returns ancestors for inheritance lookup
|
|
14
|
-
def initialize(ancestry_provider: nil)
|
|
15
|
-
@instance_variables = {} # { "ClassName" => { :@name => WriteNode } }
|
|
16
|
-
@class_variables = {} # { "ClassName" => { :@@name => WriteNode } }
|
|
17
|
-
@ancestry_provider = ancestry_provider
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Register an instance variable write
|
|
21
|
-
# @param class_name [String] Class name
|
|
22
|
-
# @param name [Symbol] Variable name (e.g., :@recipe)
|
|
23
|
-
# @param write_node [IR::InstanceVariableWriteNode]
|
|
24
|
-
def register_instance_variable(class_name, name, write_node)
|
|
25
|
-
return unless class_name
|
|
26
|
-
|
|
27
|
-
@instance_variables[class_name] ||= {}
|
|
28
|
-
# First write wins (preserves consistent behavior)
|
|
29
|
-
@instance_variables[class_name][name] ||= write_node
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Look up an instance variable write (with inheritance chain traversal)
|
|
33
|
-
# @param class_name [String]
|
|
34
|
-
# @param name [Symbol]
|
|
35
|
-
# @return [IR::InstanceVariableWriteNode, nil]
|
|
36
|
-
def lookup_instance_variable(class_name, name)
|
|
37
|
-
return nil unless class_name
|
|
38
|
-
|
|
39
|
-
# Try current class first
|
|
40
|
-
result = @instance_variables.dig(class_name, name)
|
|
41
|
-
return result if result
|
|
42
|
-
|
|
43
|
-
# Traverse ancestor chain if provider available
|
|
44
|
-
return nil unless @ancestry_provider
|
|
45
|
-
|
|
46
|
-
ancestors = @ancestry_provider.call(class_name)
|
|
47
|
-
ancestors.each do |ancestor_name|
|
|
48
|
-
next if ancestor_name == class_name # Skip self
|
|
49
|
-
|
|
50
|
-
result = @instance_variables.dig(ancestor_name, name)
|
|
51
|
-
return result if result
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
nil
|
|
55
|
-
end
|
|
56
|
-
|
|
57
|
-
# Register a class variable write
|
|
58
|
-
# @param class_name [String]
|
|
59
|
-
# @param name [Symbol] (e.g., :@@count)
|
|
60
|
-
# @param write_node [IR::ClassVariableWriteNode]
|
|
61
|
-
def register_class_variable(class_name, name, write_node)
|
|
62
|
-
return unless class_name
|
|
63
|
-
|
|
64
|
-
@class_variables[class_name] ||= {}
|
|
65
|
-
# First write wins
|
|
66
|
-
@class_variables[class_name][name] ||= write_node
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
# Look up a class variable write
|
|
70
|
-
# @param class_name [String]
|
|
71
|
-
# @param name [Symbol]
|
|
72
|
-
# @return [IR::ClassVariableWriteNode, nil]
|
|
73
|
-
def lookup_class_variable(class_name, name)
|
|
74
|
-
return nil unless class_name
|
|
75
|
-
|
|
76
|
-
@class_variables.dig(class_name, name)
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# Clear all registered variables
|
|
80
|
-
def clear
|
|
81
|
-
@instance_variables.clear
|
|
82
|
-
@class_variables.clear
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
end
|
|
87
|
-
end
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative "types"
|
|
4
|
-
|
|
5
|
-
module TypeGuessr
|
|
6
|
-
module Core
|
|
7
|
-
# SignatureProvider aggregates multiple type sources for method signature lookups
|
|
8
|
-
# Uses priority-based resolution: first non-Unknown result wins
|
|
9
|
-
#
|
|
10
|
-
# @example
|
|
11
|
-
# provider = SignatureProvider.new
|
|
12
|
-
# provider.add_provider(ProjectRBSProvider.new, priority: :high)
|
|
13
|
-
# provider.add_provider(RBSProvider.instance)
|
|
14
|
-
#
|
|
15
|
-
# return_type = provider.get_method_return_type("String", "upcase")
|
|
16
|
-
class SignatureProvider
|
|
17
|
-
# @param providers [Array<#get_method_return_type>] Providers in priority order (first = highest)
|
|
18
|
-
def initialize(providers = [])
|
|
19
|
-
@providers = providers.dup
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Add a provider to the chain
|
|
23
|
-
# @param provider [#get_method_return_type] Provider implementing the signature protocol
|
|
24
|
-
# @param priority [:high, :low] Priority level (:high = first, :low = last)
|
|
25
|
-
def add_provider(provider, priority: :low)
|
|
26
|
-
case priority
|
|
27
|
-
when :high
|
|
28
|
-
@providers.unshift(provider)
|
|
29
|
-
when :low
|
|
30
|
-
@providers.push(provider)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
# Get instance method return type with overload resolution
|
|
35
|
-
# @param class_name [String] Class name (e.g., "String", "Array")
|
|
36
|
-
# @param method_name [String] Method name (e.g., "upcase", "map")
|
|
37
|
-
# @param arg_types [Array<Types::Type>] Argument types for overload matching
|
|
38
|
-
# @return [Types::Type] Return type (Unknown if not found in any provider)
|
|
39
|
-
def get_method_return_type(class_name, method_name, arg_types = [])
|
|
40
|
-
@providers.each do |provider|
|
|
41
|
-
result = provider.get_method_return_type(class_name, method_name, arg_types)
|
|
42
|
-
return result unless result.is_a?(Types::Unknown)
|
|
43
|
-
end
|
|
44
|
-
Types::Unknown.instance
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
# Get class method return type (e.g., File.read, Array.new)
|
|
48
|
-
# @param class_name [String] Class name
|
|
49
|
-
# @param method_name [String] Method name
|
|
50
|
-
# @param arg_types [Array<Types::Type>] Argument types for overload matching
|
|
51
|
-
# @return [Types::Type] Return type (Unknown if not found in any provider)
|
|
52
|
-
def get_class_method_return_type(class_name, method_name, arg_types = [])
|
|
53
|
-
@providers.each do |provider|
|
|
54
|
-
result = provider.get_class_method_return_type(class_name, method_name, arg_types)
|
|
55
|
-
return result unless result.is_a?(Types::Unknown)
|
|
56
|
-
end
|
|
57
|
-
Types::Unknown.instance
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
# Get block parameter types for a method
|
|
61
|
-
# @param class_name [String] Class name
|
|
62
|
-
# @param method_name [String] Method name
|
|
63
|
-
# @return [Array<Types::Type>] Block parameter types (empty if no block or not found)
|
|
64
|
-
def get_block_param_types(class_name, method_name)
|
|
65
|
-
@providers.each do |provider|
|
|
66
|
-
result = provider.get_block_param_types(class_name, method_name)
|
|
67
|
-
return result unless result.empty?
|
|
68
|
-
end
|
|
69
|
-
[]
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Get method signatures for hover display
|
|
73
|
-
# @param class_name [String] Class name
|
|
74
|
-
# @param method_name [String] Method name
|
|
75
|
-
# @return [Array<Signature>] Method signatures (empty if not found)
|
|
76
|
-
def get_method_signatures(class_name, method_name)
|
|
77
|
-
@providers.each do |provider|
|
|
78
|
-
next unless provider.respond_to?(:get_method_signatures)
|
|
79
|
-
|
|
80
|
-
result = provider.get_method_signatures(class_name, method_name)
|
|
81
|
-
return result unless result.empty?
|
|
82
|
-
end
|
|
83
|
-
[]
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Get class method signatures for hover display (e.g., File.exist?, Dir.pwd)
|
|
87
|
-
# @param class_name [String] Class name
|
|
88
|
-
# @param method_name [String] Method name
|
|
89
|
-
# @return [Array<Signature>] Method signatures (empty if not found)
|
|
90
|
-
def get_class_method_signatures(class_name, method_name)
|
|
91
|
-
@providers.each do |provider|
|
|
92
|
-
next unless provider.respond_to?(:get_class_method_signatures)
|
|
93
|
-
|
|
94
|
-
result = provider.get_class_method_signatures(class_name, method_name)
|
|
95
|
-
return result unless result.empty?
|
|
96
|
-
end
|
|
97
|
-
[]
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
end
|