Skip to content

Commit 30e66ca

Browse files
authored
Improve definition handling for guessed receiver types (#2472)
It's not uncommon for Ruby programmers to name variables after a class's superclass. But with guessed receiver types, such usages would actually reduce the number of definitions returned. For example, given the following code: ```rb class Animal end class Cat < Animal def meow "Meow" end end animal = Cat.new animal.meow # meow can't be found with experimented features enabled ``` This commit improves the definition handling for guessed receiver types by treating them as unknown when no methods are found.
1 parent d9c0158 commit 30e66ca

File tree

2 files changed

+72
-4
lines changed

2 files changed

+72
-4
lines changed

lib/ruby_lsp/listeners/definition.rb

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,10 +208,13 @@ def handle_instance_variable_definition(name)
208208
def handle_method_definition(message, receiver_type, inherited_only: false)
209209
methods = if receiver_type
210210
@index.resolve_method(message, receiver_type.name, inherited_only: inherited_only)
211-
else
212-
# If the method doesn't have a receiver, then we provide a few candidates to jump to
213-
# But we don't want to provide too many candidates, as it can be overwhelming
214-
@index[message]&.take(MAX_NUMBER_OF_DEFINITION_CANDIDATES_WITHOUT_RECEIVER)
211+
end
212+
213+
# If the method doesn't have a receiver, or the guessed receiver doesn't have any matched candidates,
214+
# then we provide a few candidates to jump to
215+
# But we don't want to provide too many candidates, as it can be overwhelming
216+
if receiver_type.nil? || (receiver_type.is_a?(TypeInferrer::GuessedType) && methods.nil?)
217+
methods = @index[message]&.take(MAX_NUMBER_OF_DEFINITION_CANDIDATES_WITHOUT_RECEIVER)
215218
end
216219

217220
return unless methods

test/requests/definition_expectations_test.rb

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -472,6 +472,71 @@ def foo; end
472472
end
473473
end
474474

475+
def test_definition_for_guessed_receiver_is_listed
476+
source = <<~RUBY
477+
# typed: false
478+
479+
class Cheetah
480+
def meow; end
481+
end
482+
483+
class Cat
484+
def meow; end
485+
end
486+
487+
cat = Cat.new
488+
cat.meow
489+
RUBY
490+
491+
with_server(source) do |server, uri|
492+
server.process_message(
493+
id: 1,
494+
method: "textDocument/definition",
495+
params: { textDocument: { uri: uri }, position: { character: 4, line: 11 } },
496+
)
497+
response = server.pop_response.response
498+
499+
# Only Cat#meow is listed
500+
assert_equal(1, response.size)
501+
assert_equal(7, response.first.target_range.start.line)
502+
assert_equal(7, response.first.target_range.end.line)
503+
assert_equal(2, response.first.target_range.start.character)
504+
assert_equal(15, response.first.target_range.end.character)
505+
end
506+
end
507+
508+
def test_guessed_receiver_is_treated_as_unknown_when_no_declaration_exists
509+
source = <<~RUBY
510+
# typed: false
511+
512+
class Animal
513+
def eat; end
514+
end
515+
516+
class Cheetah
517+
def meow; end
518+
end
519+
520+
class Cat
521+
def meow; end
522+
end
523+
524+
animal = Cat.new
525+
animal.meow
526+
RUBY
527+
528+
with_server(source) do |server, uri|
529+
server.process_message(
530+
id: 1,
531+
method: "textDocument/definition",
532+
params: { textDocument: { uri: uri }, position: { character: 7, line: 15 } },
533+
)
534+
response = server.pop_response.response
535+
536+
assert_equal(2, response.size)
537+
end
538+
end
539+
475540
def test_definitions_for_unknown_receiver_is_capped
476541
source = +"# typed: false\n"
477542

0 commit comments

Comments
 (0)