aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKevin Newton <kddnewton@gmail.com>2023-11-05 01:10:04 -0400
committergit <svn-admin@ruby-lang.org>2023-11-08 22:17:43 +0000
commit201853f4e1ae87eedb5cd255a80b088a7a40c59e (patch)
tree0b1cc270cada175975a6dbcf7332e56f295c8655
parentf9e34a1fd3387822903ff7a63405bf116e0c9803 (diff)
downloadruby-201853f4e1ae87eedb5cd255a80b088a7a40c59e.tar.gz
[ruby/prism] Provide Parameters#signature for mirroring Method#parameters
https://github.com/ruby/prism/commit/90b3245528
-rw-r--r--lib/prism/node_ext.rb52
-rw-r--r--test/prism/parameters_signature_test.rb91
2 files changed, 140 insertions, 3 deletions
diff --git a/lib/prism/node_ext.rb b/lib/prism/node_ext.rb
index 8d8bee074d..4febc615c1 100644
--- a/lib/prism/node_ext.rb
+++ b/lib/prism/node_ext.rb
@@ -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
diff --git a/test/prism/parameters_signature_test.rb b/test/prism/parameters_signature_test.rb
new file mode 100644
index 0000000000..788ce7b907
--- /dev/null
+++ b/test/prism/parameters_signature_test.rb
@@ -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