Skip to content

Commit 8e3dbfc

Browse files
authored
Add literal types to inferrer (#2468)
* Only show guessed type information if the type is indeed guessed * Add literal types to inferrer
1 parent 0d919fb commit 8e3dbfc

File tree

4 files changed

+156
-1
lines changed

4 files changed

+156
-1
lines changed

lib/ruby_lsp/listeners/completion.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,7 +366,7 @@ def complete_methods(node, name)
366366

367367
return unless range
368368

369-
guessed_type = type.name
369+
guessed_type = type.is_a?(TypeInferrer::GuessedType) && type.name
370370

371371
@index.method_completion_candidates(method_name, type.name).each do |entry|
372372
entry_name = entry.name

lib/ruby_lsp/type_inferrer.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,47 @@ def infer_receiver_type(node_context)
3636
def infer_receiver_for_call_node(node, node_context)
3737
receiver = node.receiver
3838

39+
# For receivers inside parenthesis, such as ranges like (0...2), we need to unwrap the parenthesis to get the
40+
# actual node
41+
if receiver.is_a?(Prism::ParenthesesNode)
42+
statements = receiver.body
43+
44+
if statements.is_a?(Prism::StatementsNode)
45+
body = statements.body
46+
47+
if body.length == 1
48+
receiver = body.first
49+
end
50+
end
51+
end
52+
3953
case receiver
4054
when Prism::SelfNode, nil
4155
self_receiver_handling(node_context)
56+
when Prism::StringNode
57+
Type.new("String")
58+
when Prism::SymbolNode
59+
Type.new("Symbol")
60+
when Prism::ArrayNode
61+
Type.new("Array")
62+
when Prism::HashNode
63+
Type.new("Hash")
64+
when Prism::IntegerNode
65+
Type.new("Integer")
66+
when Prism::FloatNode
67+
Type.new("Float")
68+
when Prism::RegularExpressionNode
69+
Type.new("Regexp")
70+
when Prism::NilNode
71+
Type.new("NilClass")
72+
when Prism::TrueNode
73+
Type.new("TrueClass")
74+
when Prism::FalseNode
75+
Type.new("FalseClass")
76+
when Prism::RangeNode
77+
Type.new("Range")
78+
when Prism::LambdaNode
79+
Type.new("Proc")
4280
when Prism::ConstantPathNode, Prism::ConstantReadNode
4381
# When the receiver is a constant reference, we have to try to resolve it to figure out the right
4482
# receiver. But since the invocation is directly on the constant, that's the singleton context of that

test/requests/completion_test.rb

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1290,6 +1290,27 @@ class Foo
12901290
end
12911291
end
12921292

1293+
def test_guessed_type_name_is_only_included_for_guessed_types
1294+
source = +<<~RUBY
1295+
[].e
1296+
RUBY
1297+
1298+
with_server(source) do |server, uri|
1299+
index = server.instance_variable_get(:@global_state).index
1300+
RubyIndexer::RBSIndexer.new(index).index_ruby_core
1301+
1302+
server.process_message(id: 1, method: "textDocument/completion", params: {
1303+
textDocument: { uri: uri },
1304+
position: { line: 0, character: 4 },
1305+
})
1306+
1307+
items = server.pop_response.response
1308+
items.each do |item|
1309+
refute(item.data[:guessed_type])
1310+
end
1311+
end
1312+
end
1313+
12931314
private
12941315

12951316
def with_file_structure(server, &block)

test/type_inferrer_test.rb

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,102 @@ def initialize(a, b, c)
262262
assert_equal("Foo", @type_inferrer.infer_receiver_type(node_context).name)
263263
end
264264

265+
def test_infer_string_literal
266+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 3 })
267+
"".upcase
268+
RUBY
269+
270+
assert_equal("String", @type_inferrer.infer_receiver_type(node_context).name)
271+
end
272+
273+
def test_infer_symbol_literal
274+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 5 })
275+
:foo.to_s
276+
RUBY
277+
278+
assert_equal("Symbol", @type_inferrer.infer_receiver_type(node_context).name)
279+
end
280+
281+
def test_infer_array_literal
282+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 3 })
283+
[].first
284+
RUBY
285+
286+
assert_equal("Array", @type_inferrer.infer_receiver_type(node_context).name)
287+
end
288+
289+
def test_infer_hash_literal
290+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 3 })
291+
{}.keys
292+
RUBY
293+
294+
assert_equal("Hash", @type_inferrer.infer_receiver_type(node_context).name)
295+
end
296+
297+
def test_infer_integer_literal
298+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 3 })
299+
10.to_s
300+
RUBY
301+
302+
assert_equal("Integer", @type_inferrer.infer_receiver_type(node_context).name)
303+
end
304+
305+
def test_infer_float_literal
306+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 4 })
307+
1.5.to_s
308+
RUBY
309+
310+
assert_equal("Float", @type_inferrer.infer_receiver_type(node_context).name)
311+
end
312+
313+
def test_infer_regexp_literal
314+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 5 })
315+
/abc/.match("abc")
316+
RUBY
317+
318+
assert_equal("Regexp", @type_inferrer.infer_receiver_type(node_context).name)
319+
end
320+
321+
def test_infer_nil_literal
322+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 4 })
323+
nil.to_s
324+
RUBY
325+
326+
assert_equal("NilClass", @type_inferrer.infer_receiver_type(node_context).name)
327+
end
328+
329+
def test_infer_true_literal
330+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 5 })
331+
true.to_s
332+
RUBY
333+
334+
assert_equal("TrueClass", @type_inferrer.infer_receiver_type(node_context).name)
335+
end
336+
337+
def test_infer_false_literal
338+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 6 })
339+
false.to_s
340+
RUBY
341+
342+
assert_equal("FalseClass", @type_inferrer.infer_receiver_type(node_context).name)
343+
end
344+
345+
def test_infer_range_literal
346+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 8 })
347+
(5..10).to_a
348+
RUBY
349+
350+
assert_equal("Range", @type_inferrer.infer_receiver_type(node_context).name)
351+
end
352+
353+
def test_infer_lambda_literal
354+
node_context = index_and_locate(<<~RUBY, { line: 0, character: 5 })
355+
->{}.call
356+
RUBY
357+
358+
assert_equal("Proc", @type_inferrer.infer_receiver_type(node_context).name)
359+
end
360+
265361
private
266362

267363
def index_and_locate(source, position)

0 commit comments

Comments
 (0)