Skip to content

Commit

Permalink
Track rest, keyword rest and post parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
vinistock committed Nov 30, 2023
1 parent 3d44c0f commit dfa098a
Show file tree
Hide file tree
Showing 2 changed files with 112 additions and 3 deletions.
36 changes: 33 additions & 3 deletions lib/ruby_indexer/lib/ruby_indexer/entry.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,14 @@ class KeywordParameter < Parameter
class OptionalKeywordParameter < Parameter
end

# A rest method parameter, e.g. `def foo(*a)`
class RestParameter < Parameter
end

# A keyword rest method parameter, e.g. `def foo(**a)`
class KeywordRestParameter < Parameter
end

class Member < Entry
extend T::Sig
extend T::Helpers
Expand Down Expand Up @@ -203,17 +211,39 @@ def list_params(parameters_node)
end
end

rest_name = parameters_node.rest&.name
parameters << RestParameter.new(name: rest_name) if rest_name

keyword_rest_name = parameter_name(parameters_node.keyword_rest)
parameters << KeywordRestParameter.new(name: keyword_rest_name) if keyword_rest_name

parameters_node.posts.each do |post|
name = parameter_name(post)
next unless name

parameters << RequiredParameter.new(name: name)
end

parameters
end

sig { params(node: Prism::Node).returns(T.nilable(Symbol)) }
sig { params(node: T.nilable(Prism::Node)).returns(T.nilable(Symbol)) }
def parameter_name(node)
case node
when Prism::RequiredParameterNode, Prism::OptionalParameterNode,
Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode
Prism::RequiredKeywordParameterNode, Prism::OptionalKeywordParameterNode,
Prism::RestParameterNode, Prism::KeywordRestParameterNode
node.name
when Prism::MultiTargetNode
names = [*node.lefts, *node.rest, *node.rights].map { |parameter_node| parameter_name(parameter_node) }
names = node.lefts.map { |parameter_node| parameter_name(parameter_node) }

rest = node.rest
if rest.is_a?(Prism::SplatNode)
name = rest.expression&.slice
names << (rest.operator == "*" ? "*#{name}".to_sym : name&.to_sym)
end

names.concat(node.rights.map { |parameter_node| parameter_name(parameter_node) })

names_with_commas = names.join(", ")
:"(#{names_with_commas})"
Expand Down
79 changes: 79 additions & 0 deletions lib/ruby_indexer/test/method_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,85 @@ def bar(a:, b: 123)
assert_instance_of(Entry::OptionalKeywordParameter, b)
end

def test_method_with_rest_and_keyword_rest_parameters
index(<<~RUBY)
class Foo
def bar(*a, **b)
end
end
RUBY

assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
entry = T.must(@index["bar"].first)
assert_equal(2, entry.parameters.length)
a, b = entry.parameters

assert_equal(:a, a.name)
assert_instance_of(Entry::RestParameter, a)

assert_equal(:b, b.name)
assert_instance_of(Entry::KeywordRestParameter, b)
end

def test_method_with_post_parameters
index(<<~RUBY)
class Foo
def bar(*a, b)
end
def baz(**a, b)
end
def qux(*a, (b, c))
end
RUBY

assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
entry = T.must(@index["bar"].first)
assert_equal(2, entry.parameters.length)
a, b = entry.parameters

assert_equal(:a, a.name)
assert_instance_of(Entry::RestParameter, a)

assert_equal(:b, b.name)
assert_instance_of(Entry::RequiredParameter, b)

entry = T.must(@index["baz"].first)
assert_equal(2, entry.parameters.length)
a, b = entry.parameters

assert_equal(:a, a.name)
assert_instance_of(Entry::KeywordRestParameter, a)

assert_equal(:b, b.name)
assert_instance_of(Entry::RequiredParameter, b)

entry = T.must(@index["qux"].first)
assert_equal(2, entry.parameters.length)
_a, second = entry.parameters

assert_equal(:"(b, c)", second.name)
assert_instance_of(Entry::RequiredParameter, second)
end

def test_method_with_destructured_rest_parameters
index(<<~RUBY)
class Foo
def bar((a, *b))
end
end
RUBY

assert_entry("bar", Entry::InstanceMethod, "/fake/path/foo.rb:1-2:2-5")
entry = T.must(@index["bar"].first)
assert_equal(1, entry.parameters.length)
param = entry.parameters.first

assert_equal(:"(a, *b)", param.name)
assert_instance_of(Entry::RequiredParameter, param)
end

def test_keeps_track_of_method_owner
index(<<~RUBY)
class Foo
Expand Down

0 comments on commit dfa098a

Please sign in to comment.