Skip to content

Commit b47eb46

Browse files
committed
Add arity to Params, DefNode and BlockNode
1 parent 25e7555 commit b47eb46

File tree

3 files changed

+204
-0
lines changed

3 files changed

+204
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- Arity has been added to DefNode, BlockNode and Params. The method returns a range where the lower bound is the minimum and the upper bound is the maximum number of arguments that can be used to invoke that block/method definition.
12+
913
## [5.2.0] - 2023-01-04
1014

1115
### Added

lib/syntax_tree/node.rb

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4175,6 +4175,17 @@ def ===(other)
41754175
def endless?
41764176
!bodystmt.is_a?(BodyStmt)
41774177
end
4178+
4179+
def arity
4180+
case params
4181+
when Params
4182+
params.arity
4183+
when Paren
4184+
params.contents.arity
4185+
else
4186+
0..0
4187+
end
4188+
end
41784189
end
41794190

41804191
# Defined represents the use of the +defined?+ operator. It can be used with
@@ -4362,6 +4373,15 @@ def keywords?
43624373
opening.is_a?(Kw)
43634374
end
43644375

4376+
def arity
4377+
case block_var
4378+
when BlockVar
4379+
block_var.params.arity
4380+
else
4381+
0..0
4382+
end
4383+
end
4384+
43654385
private
43664386

43674387
# If this is nested anywhere inside certain nodes, then we can't change
@@ -8325,6 +8345,23 @@ def ===(other)
83258345
keyword_rest === other.keyword_rest && block === other.block
83268346
end
83278347

8348+
# Returns a range representing the possible number of arguments accepted
8349+
# by this params node not including the block. For example:
8350+
# def foo(a, b = 1, c:, d: 2, &block)
8351+
# ...
8352+
# end
8353+
# has arity 2..4
8354+
def arity
8355+
optional_keywords = keywords.count { |_label, value| value }
8356+
lower_bound =
8357+
requireds.length + posts.length + keywords.length - optional_keywords
8358+
8359+
upper_bound =
8360+
lower_bound + optionals.length +
8361+
optional_keywords if keyword_rest.nil? && rest.nil?
8362+
lower_bound..upper_bound
8363+
end
8364+
83288365
private
83298366

83308367
def format_contents(q, parts)

test/node_test.rb

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1058,6 +1058,169 @@ def test_root_class_raises_not_implemented_errors
10581058
end
10591059
end
10601060

1061+
def test_arity_no_args
1062+
source = <<~SOURCE
1063+
def foo
1064+
end
1065+
SOURCE
1066+
1067+
at = location(chars: 0..11, columns: 0..3, lines: 1..2)
1068+
assert_node(DefNode, source, at: at) do |node|
1069+
assert_equal(0..0, node.arity)
1070+
node
1071+
end
1072+
end
1073+
1074+
def test_arity_positionals
1075+
source = <<~SOURCE
1076+
def foo(a, b = 1)
1077+
end
1078+
SOURCE
1079+
1080+
at = location(chars: 0..21, columns: 0..3, lines: 1..2)
1081+
assert_node(DefNode, source, at: at) do |node|
1082+
assert_equal(1..2, node.arity)
1083+
node
1084+
end
1085+
end
1086+
1087+
def test_arity_rest
1088+
source = <<~SOURCE
1089+
def foo(a, *b)
1090+
end
1091+
SOURCE
1092+
1093+
at = location(chars: 0..18, columns: 0..3, lines: 1..2)
1094+
assert_node(DefNode, source, at: at) do |node|
1095+
assert_equal(1.., node.arity)
1096+
node
1097+
end
1098+
end
1099+
1100+
def test_arity_keyword_rest
1101+
source = <<~SOURCE
1102+
def foo(a, **b)
1103+
end
1104+
SOURCE
1105+
1106+
at = location(chars: 0..19, columns: 0..3, lines: 1..2)
1107+
assert_node(DefNode, source, at: at) do |node|
1108+
assert_equal(1.., node.arity)
1109+
node
1110+
end
1111+
end
1112+
1113+
def test_arity_keywords
1114+
source = <<~SOURCE
1115+
def foo(a:, b: 1)
1116+
end
1117+
SOURCE
1118+
1119+
at = location(chars: 0..21, columns: 0..3, lines: 1..2)
1120+
assert_node(DefNode, source, at: at) do |node|
1121+
assert_equal(1..2, node.arity)
1122+
node
1123+
end
1124+
end
1125+
1126+
def test_arity_mixed
1127+
source = <<~SOURCE
1128+
def foo(a, b = 1, c:, d: 2)
1129+
end
1130+
SOURCE
1131+
1132+
at = location(chars: 0..31, columns: 0..3, lines: 1..2)
1133+
assert_node(DefNode, source, at: at) do |node|
1134+
assert_equal(2..4, node.arity)
1135+
node
1136+
end
1137+
end
1138+
1139+
guard_version("2.7.3") do
1140+
def test_arity_arg_forward
1141+
source = <<~SOURCE
1142+
def foo(...)
1143+
end
1144+
SOURCE
1145+
1146+
at = location(chars: 0..16, columns: 0..3, lines: 1..2)
1147+
assert_node(DefNode, source, at: at) do |node|
1148+
assert_equal(0.., node.arity)
1149+
node
1150+
end
1151+
end
1152+
end
1153+
1154+
guard_version("3.0.0") do
1155+
def test_arity_positional_and_arg_forward
1156+
source = <<~SOURCE
1157+
def foo(a, ...)
1158+
end
1159+
SOURCE
1160+
1161+
at = location(chars: 0..19, columns: 0..3, lines: 1..2)
1162+
assert_node(DefNode, source, at: at) do |node|
1163+
assert_equal(1.., node.arity)
1164+
node
1165+
end
1166+
end
1167+
end
1168+
1169+
def test_arity_no_parenthesis
1170+
source = <<~SOURCE
1171+
def foo a, b = 1
1172+
end
1173+
SOURCE
1174+
1175+
at = location(chars: 0..20, columns: 0..3, lines: 1..2)
1176+
assert_node(DefNode, source, at: at) do |node|
1177+
assert_equal(1..2, node.arity)
1178+
node
1179+
end
1180+
end
1181+
1182+
def test_block_arity_positionals
1183+
source = <<~SOURCE
1184+
[].each do |a, b, c|
1185+
end
1186+
SOURCE
1187+
1188+
at = location(chars: 8..24, columns: 8..3, lines: 1..2)
1189+
assert_node(BlockNode, source, at: at) do |node|
1190+
block = node.block
1191+
assert_equal(3..3, block.arity)
1192+
block
1193+
end
1194+
end
1195+
1196+
def test_block_arity_with_optional
1197+
source = <<~SOURCE
1198+
[].each do |a, b = 1|
1199+
end
1200+
SOURCE
1201+
1202+
at = location(chars: 8..25, columns: 8..3, lines: 1..2)
1203+
assert_node(BlockNode, source, at: at) do |node|
1204+
block = node.block
1205+
assert_equal(1..2, block.arity)
1206+
block
1207+
end
1208+
end
1209+
1210+
def test_block_arity_with_optional_keyword
1211+
source = <<~SOURCE
1212+
[].each do |a, b: 2|
1213+
end
1214+
SOURCE
1215+
1216+
at = location(chars: 8..24, columns: 8..3, lines: 1..2)
1217+
assert_node(BlockNode, source, at: at) do |node|
1218+
block = node.block
1219+
assert_equal(1..2, block.arity)
1220+
block
1221+
end
1222+
end
1223+
10611224
private
10621225

10631226
def location(lines: 1..1, chars: 0..0, columns: 0..0)

0 commit comments

Comments
 (0)