Skip to content

Commit 1136464

Browse files
authored
Make all overloads of ArrayLiteral#[] return nil on out of bounds (#16453)
The single-index overload in the macro language has always behaved like `#[]?` in non-macro code, so I don't think there is any particular benefit in making both the range overload and the start + count overload both raise on out-of-bounds start indices. This actually applies to `TupleLiteral` as well.
1 parent d703bce commit 1136464

File tree

3 files changed

+43
-17
lines changed

3 files changed

+43
-17
lines changed

spec/compiler/macro/macro_methods_spec.cr

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1043,10 +1043,20 @@ module Crystal
10431043
assert_macro %({{ [1, 2, 3, 4][nil..nil] }}), %([1, 2, 3, 4])
10441044
end
10451045

1046+
it "executes [] with range, start is out of bounds" do
1047+
assert_macro %({{ [1, 2, 3, 4][5..] }}), %(nil)
1048+
assert_macro %({{ [1, 2, 3, 4][-5..] }}), %(nil)
1049+
end
1050+
10461051
it "executes [] with two numbers" do
10471052
assert_macro %({{ [1, 2, 3, 4, 5][1, 3] }}), %([2, 3, 4])
10481053
end
10491054

1055+
it "executes [] with two numbers, start is out of bounds" do
1056+
assert_macro %({{ [1, 2, 3, 4][5, 1] }}), %(nil)
1057+
assert_macro %({{ [1, 2, 3, 4][-5, 4] }}), %(nil)
1058+
end
1059+
10501060
it "executes []=" do
10511061
assert_macro %({% a = [0]; a[0] = 2 %}{{a[0]}}), "2"
10521062
end
@@ -1341,6 +1351,20 @@ module Crystal
13411351
assert_macro %({{ {1, 2, 3, 4}[nil..nil] }}), %({1, 2, 3, 4})
13421352
end
13431353

1354+
it "executes [] with range, start is out of bounds" do
1355+
assert_macro %({{ {1, 2, 3, 4}[5..] }}), %(nil)
1356+
assert_macro %({{ {1, 2, 3, 4}[-5..] }}), %(nil)
1357+
end
1358+
1359+
it "executes [] with two numbers" do
1360+
assert_macro %({{ {1, 2, 3, 4, 5}[1, 3] }}), %({2, 3, 4})
1361+
end
1362+
1363+
it "executes [] with two numbers, start is out of bounds" do
1364+
assert_macro %({{ {1, 2, 3, 4}[5, 1] }}), %(nil)
1365+
assert_macro %({{ {1, 2, 3, 4}[-5, 4] }}), %(nil)
1366+
end
1367+
13441368
it "executes size" do
13451369
assert_macro %({{ {1, 2, 3}.size }}), "3"
13461370
end

src/compiler/crystal/macros.cr

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -804,12 +804,16 @@ module Crystal::Macros
804804
def uniq : ArrayLiteral
805805
end
806806

807-
# Similar to `Array#[]`, but returns `NilLiteral` on out of bounds.
807+
# Similar to `Array#[]?(Int)`.
808808
def [](index : NumberLiteral) : ASTNode
809809
end
810810

811-
# Similar to `Array#[]`.
812-
def [](index : RangeLiteral) : ArrayLiteral(ASTNode)
811+
# Similar to `Array#[]?(Range)`.
812+
def [](index : RangeLiteral) : ArrayLiteral(ASTNode) | NilLiteral
813+
end
814+
815+
# Similar to `Array#[]?(Int, Int)`.
816+
def [](start : NumberLiteral, count : NumberLiteral) : ArrayLiteral(ASTNode) | NilLiteral
813817
end
814818

815819
# Similar to `Array#[]=`.
@@ -1118,12 +1122,17 @@ module Crystal::Macros
11181122
def uniq : TupleLiteral
11191123
end
11201124

1121-
# Similar to `Tuple#[]`, but returns `NilLiteral` on out of bounds.
1125+
# Similar to `Tuple#[]?(Int)`.
11221126
def [](index : NumberLiteral) : ASTNode
11231127
end
11241128

1125-
# Similar to `Tuple#[]`.
1126-
def [](index : RangeLiteral) : TupleLiteral(ASTNode)
1129+
# Similar to `Tuple#[]?(Range)`.
1130+
def [](index : RangeLiteral) : TupleLiteral | NilLiteral
1131+
end
1132+
1133+
# Similar to `Array#[]?(Int, Int)`, but returns another `TupleLiteral`
1134+
# instead of an `ArrayLiteral`.
1135+
def [](start : NumberLiteral, count : NumberLiteral) : TupleLiteral | NilLiteral
11271136
end
11281137

11291138
# Similar to `Array#[]=`.

src/compiler/crystal/macros/methods.cr

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3135,24 +3135,17 @@ private def interpret_array_or_tuple_method(object, klass, method, args, named_a
31353135

31363136
from = from.to_number.to_i
31373137
to = to.to_number.to_i
3138-
3139-
begin
3140-
klass.new(object.elements[from, to])
3141-
rescue ex
3142-
object.raise ex.message
3143-
end
3138+
values = object.elements[from, to]?
3139+
values ? klass.new(values) : Crystal::NilLiteral.new
31443140
else
31453141
case arg = from
31463142
when Crystal::NumberLiteral
31473143
index = arg.to_number.to_i
31483144
object.elements[index]? || Crystal::NilLiteral.new
31493145
when Crystal::RangeLiteral
31503146
range = arg.interpret_to_nilable_range(interpreter)
3151-
begin
3152-
klass.new(object.elements[range])
3153-
rescue ex
3154-
object.raise ex.message
3155-
end
3147+
values = object.elements[range]?
3148+
values ? klass.new(values) : Crystal::NilLiteral.new
31563149
else
31573150
arg.raise "argument to [] must be a number or range, not #{arg.class_desc}:\n\n#{arg}"
31583151
end

0 commit comments

Comments
 (0)