Skip to content

Commit cd9f1e7

Browse files
committed
Migrate type inferrer to use Rubydex
1 parent abf5c41 commit cd9f1e7

4 files changed

Lines changed: 28 additions & 22 deletions

File tree

lib/ruby_lsp/global_state.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ def initialize
6363
@index = RubyIndexer::Index.new #: RubyIndexer::Index
6464
@graph = Rubydex::Graph.new #: Rubydex::Graph
6565
@supported_formatters = {} #: Hash[String, Requests::Support::Formatter]
66-
@type_inferrer = TypeInferrer.new(@index) #: TypeInferrer
66+
@type_inferrer = TypeInferrer.new(@graph) #: TypeInferrer
6767
@addon_settings = {} #: Hash[String, untyped]
6868
@top_level_bundle = begin
6969
Bundler.with_original_env { Bundler.default_gemfile }

lib/ruby_lsp/type_inferrer.rb

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ module RubyLsp
55
# A minimalistic type checker to try to resolve types that can be inferred without requiring a type system or
66
# annotations
77
class TypeInferrer
8-
#: (RubyIndexer::Index index) -> void
9-
def initialize(index)
10-
@index = index
8+
#: (Rubydex::Graph) -> void
9+
def initialize(graph)
10+
@graph = graph
1111
end
1212

1313
#: (NodeContext node_context) -> Type?
@@ -81,11 +81,10 @@ def infer_receiver_for_call_node(node, node_context)
8181
receiver_name = RubyIndexer::Index.constant_name(receiver)
8282
return unless receiver_name
8383

84-
resolved_receiver = @index.resolve(receiver_name, node_context.nesting)
85-
name = resolved_receiver&.first&.name
86-
return unless name
84+
resolved_receiver = @graph.resolve_constant(receiver_name, node_context.nesting)
85+
return unless resolved_receiver
8786

88-
*parts, last = name.split("::")
87+
*parts, last = resolved_receiver.name.split("::")
8988
return Type.new("#{last}::<#{last}>") if parts.empty?
9089

9190
Type.new("#{parts.join("::")}::#{last}::<#{last}>")
@@ -96,12 +95,14 @@ def infer_receiver_for_call_node(node, node_context)
9695
# When invoking `new`, we recursively infer the type of the receiver to get the class type its being invoked
9796
# on and then return the attached version of that type, since it's being instantiated.
9897
type = infer_receiver_for_call_node(receiver, node_context)
99-
10098
return unless type
10199

102100
# If the method `new` was overridden, then we cannot assume that it will return a new instance of the class
103-
new_method = @index.resolve_method("new", type.name)&.first
104-
return if new_method && new_method.owner&.name != "Class"
101+
declaration = @graph[type.name] #: as Rubydex::Namespace?
102+
return unless declaration
103+
104+
new_method = declaration.find_member("new()")
105+
return if new_method && new_method.owner.name != "Class"
105106

106107
type.attached
107108
elsif raw_receiver
@@ -121,11 +122,11 @@ def guess_type(raw_receiver, nesting)
121122
.map(&:capitalize)
122123
.join
123124

124-
entries = @index.resolve(guessed_name, nesting) || @index.first_unqualified_const(guessed_name)
125-
name = entries&.first&.name
126-
return unless name
125+
declaration = @graph.resolve_constant(guessed_name, nesting)
126+
declaration ||= @graph.search(guessed_name).first
127+
return unless declaration
127128

128-
GuessedType.new(name)
129+
GuessedType.new(declaration.name)
129130
end
130131

131132
#: (NodeContext node_context) -> Type
@@ -148,7 +149,6 @@ def self_receiver_handling(node_context)
148149
#: (NodeContext node_context) -> Type?
149150
def infer_receiver_for_class_variables(node_context)
150151
nesting_parts = node_context.nesting.dup
151-
152152
return Type.new("Object") if nesting_parts.empty?
153153

154154
nesting_parts.reverse_each do |part|
@@ -157,9 +157,11 @@ def infer_receiver_for_class_variables(node_context)
157157
nesting_parts.pop
158158
end
159159

160-
receiver_name = nesting_parts.join("::")
161-
resolved_receiver = @index.resolve(receiver_name, node_context.nesting)&.first
162-
return unless resolved_receiver&.name
160+
resolved_receiver = @graph.resolve_constant(
161+
nesting_parts.last, #: as !nil
162+
nesting_parts[0...-1], #: as !nil
163+
)
164+
return unless resolved_receiver
163165

164166
Type.new(resolved_receiver.name)
165167
end

test/requests/hover_expectations_test.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,8 @@ def baz
670670
end
671671

672672
def test_hover_for_methods_shows_overload_count
673+
skip("[RUBYDEX] Temporarily skipped because we don't yet index RBS methods")
674+
673675
source = <<~RUBY
674676
String.try_convert
675677
RUBY

test/type_inferrer_test.rb

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
module RubyLsp
77
class TypeInferrerTest < Minitest::Test
88
def setup
9-
@index = RubyIndexer::Index.new
10-
@type_inferrer = TypeInferrer.new(@index)
9+
@graph = Rubydex::Graph.new
10+
@type_inferrer = TypeInferrer.new(@graph)
1111
end
1212

1313
def test_infer_receiver_type_self_inside_method
@@ -499,7 +499,9 @@ class Foo
499499
private
500500

501501
def index_and_locate(source, position)
502-
@index.index_single(URI::Generic.from_path(path: "/fake/path/foo.rb"), source)
502+
@graph.index_source(URI::Generic.from_path(path: "/fake/path/foo.rb").to_s, source, "ruby")
503+
@graph.resolve
504+
503505
document = RubyLsp::RubyDocument.new(
504506
source: source,
505507
version: 1,

0 commit comments

Comments
 (0)