Skip to content

Commit

Permalink
Improve definition handling for guessed receiver types
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
st0012 committed Aug 20, 2024
1 parent 931f6a5 commit 2bced8c
Show file tree
Hide file tree
Showing 2 changed files with 72 additions and 4 deletions.
11 changes: 7 additions & 4 deletions lib/ruby_lsp/listeners/definition.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,10 +208,13 @@ def handle_instance_variable_definition(name)
def handle_method_definition(message, receiver_type, inherited_only: false)
methods = if receiver_type
@index.resolve_method(message, receiver_type.name, inherited_only: inherited_only)
else
# If the method doesn't have a receiver, then we provide a few candidates to jump to
# But we don't want to provide too many candidates, as it can be overwhelming
@index[message]&.take(MAX_NUMBER_OF_DEFINITION_CANDIDATES_WITHOUT_RECEIVER)
end

# If the method doesn't have a receiver, or the guessed receiver doesn't have any matched candidates,
# then we provide a few candidates to jump to
# But we don't want to provide too many candidates, as it can be overwhelming
if receiver_type.nil? || (receiver_type.is_a?(TypeInferrer::GuessedType) && methods.nil?)
methods = @index[message]&.take(MAX_NUMBER_OF_DEFINITION_CANDIDATES_WITHOUT_RECEIVER)
end

return unless methods
Expand Down
65 changes: 65 additions & 0 deletions test/requests/definition_expectations_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -472,6 +472,71 @@ def foo; end
end
end

def test_definition_for_guessed_receiver_is_listed
source = <<~RUBY
# typed: false
class Cheetah
def meow; end
end
class Cat
def meow; end
end
cat = Cat.new
cat.meow
RUBY

with_server(source) do |server, uri|
server.process_message(
id: 1,
method: "textDocument/definition",
params: { textDocument: { uri: uri }, position: { character: 4, line: 11 } },
)
response = server.pop_response.response

# Only Cat#meow is listed
assert_equal(1, response.size)
assert_equal(7, response.first.target_range.start.line)
assert_equal(7, response.first.target_range.end.line)
assert_equal(2, response.first.target_range.start.character)
assert_equal(15, response.first.target_range.end.character)
end
end

def test_guessed_receiver_is_treated_as_unknown_when_no_declaration_exists
source = <<~RUBY
# typed: false
class Animal
def eat; end
end
class Cheetah
def meow; end
end
class Cat
def meow; end
end
animal = Cat.new
animal.meow
RUBY

with_server(source) do |server, uri|
server.process_message(
id: 1,
method: "textDocument/definition",
params: { textDocument: { uri: uri }, position: { character: 7, line: 15 } },
)
response = server.pop_response.response

assert_equal(2, response.size)
end
end

def test_definitions_for_unknown_receiver_is_capped
source = +"# typed: false\n"

Expand Down

0 comments on commit 2bced8c

Please sign in to comment.