[ruby/prism] Provide Parameters#signature for mirroring Method#parameters

https://github.com/ruby/prism/commit/90b3245528
This commit is contained in:
Kevin Newton 2023-11-05 01:10:04 -04:00 коммит произвёл git
Родитель f9e34a1fd3
Коммит 201853f4e1
2 изменённых файлов: 140 добавлений и 3 удалений

Просмотреть файл

@ -61,7 +61,8 @@ module Prism
end
class ConstantReadNode < Node
# Returns the list of parts for the full name of this constant. For example: [:Foo]
# Returns the list of parts for the full name of this constant.
# For example: [:Foo]
def full_name_parts
[name]
end
@ -73,7 +74,8 @@ module Prism
end
class ConstantPathNode < Node
# Returns the list of parts for the full name of this constant path. For example: [:Foo, :Bar]
# Returns the list of parts for the full name of this constant path.
# For example: [:Foo, :Bar]
def full_name_parts
parts = [child.name]
current = parent
@ -93,7 +95,8 @@ module Prism
end
class ConstantPathTargetNode < Node
# Returns the list of parts for the full name of this constant path. For example: [:Foo, :Bar]
# Returns the list of parts for the full name of this constant path.
# For example: [:Foo, :Bar]
def full_name_parts
(parent&.full_name_parts || [:""]).push(child.name)
end
@ -103,4 +106,47 @@ module Prism
full_name_parts.join("::")
end
end
class ParametersNode < Node
# Mirrors the Method#parameters method.
def signature
names = []
requireds.each do |param|
names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
end
optionals.each { |param| names << [:opt, param.name] }
names << [:rest, rest.name || :*] if rest
posts.each do |param|
names << (param.is_a?(MultiTargetNode) ? [:req] : [:req, param.name])
end
# Regardless of the order in which the keywords were defined, the required
# keywords always come first followed by the optional keywords.
keyopt = []
keywords.each do |param|
if param.is_a?(OptionalKeywordParameterNode)
keyopt << param
else
names << [:keyreq, param.name]
end
end
keyopt.each { |param| names << [:key, param.name] }
case keyword_rest
when ForwardingParameterNode
names.concat([[:rest, :*], [:keyrest, :**], [:block, :&]])
when KeywordRestParameterNode
names << [:keyrest, keyword_rest.name || :**]
when NoKeywordsParameterNode
names << [:nokey]
end
names << [:block, block.name || :&] if block
names
end
end
end

Просмотреть файл

@ -0,0 +1,91 @@
# frozen_string_literal: true
require_relative "test_helper"
return if RUBY_VERSION < "3.2" || RUBY_ENGINE == "truffleruby"
module Prism
class ParametersSignatureTest < TestCase
def test_req
assert_parameters([[:req, :a]], "a")
end
def test_req_destructure
assert_parameters([[:req]], "(a, b)")
end
def test_opt
assert_parameters([[:opt, :a]], "a = 1")
end
def test_rest
assert_parameters([[:rest, :a]], "*a")
end
def test_rest_anonymous
assert_parameters([[:rest, :*]], "*")
end
def test_post
assert_parameters([[:rest, :a], [:req, :b]], "*a, b")
end
def test_post_destructure
assert_parameters([[:rest, :a], [:req]], "*a, (b, c)")
end
def test_keyreq
assert_parameters([[:keyreq, :a]], "a:")
end
def test_key
assert_parameters([[:key, :a]], "a: 1")
end
def test_keyrest
assert_parameters([[:keyrest, :a]], "**a")
end
def test_nokey
assert_parameters([[:nokey]], "**nil")
end
def test_keyrest_anonymous
assert_parameters([[:keyrest, :**]], "**")
end
def test_key_ordering
assert_parameters([[:keyreq, :a], [:keyreq, :b], [:key, :c], [:key, :d]], "a:, c: 1, b:, d: 2")
end
def test_block
assert_parameters([[:block, :a]], "&a")
end
def test_block_anonymous
assert_parameters([[:block, :&]], "&")
end
def test_forwarding
assert_parameters([[:rest, :*], [:keyrest, :**], [:block, :&]], "...")
end
private
def assert_parameters(expected, source)
eval("def self.m(#{source}); end")
begin
assert_equal(expected, method(:m).parameters)
assert_equal(expected, signature(source))
ensure
singleton_class.undef_method(:m)
end
end
def signature(source)
program = Prism.parse("def m(#{source}); end").value
program.statements.body.first.parameters.signature
end
end
end