diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index e128d87e..bb34c946 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,3 +1,7 @@ +### 4.15.0 + +* Bug fix: `AsyncSeq.removeAt` and `AsyncSeq.updateAt` now raise `ArgumentException` when the index is greater than or equal to the sequence length, consistent with `List.removeAt`, `Array.removeAt`, and `AsyncSeq.insertAt`. Previously they silently returned the sequence unchanged. + ### 4.14.0 * Added `AsyncSeq.mapAsyncParallelThrottled` — ordered, bounded-concurrency parallel map. Like `mapAsyncParallel` but limits the number of in-flight operations to `parallelism`, preventing unbounded resource use on large or infinite sequences. diff --git a/src/FSharp.Control.AsyncSeq/AsyncSeq.fs b/src/FSharp.Control.AsyncSeq/AsyncSeq.fs index 946e9bf9..26536a7f 100644 --- a/src/FSharp.Control.AsyncSeq/AsyncSeq.fs +++ b/src/FSharp.Control.AsyncSeq/AsyncSeq.fs @@ -1706,7 +1706,9 @@ module AsyncSeq = let mutable i = 0 for x in source do if i <> index then yield x - i <- i + 1 } + i <- i + 1 + if i <= index then + invalidArg "index" "The index is outside the range of elements in the collection." } let updateAt (index : int) (value : 'T) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq { if index < 0 then invalidArg "index" "must be non-negative" @@ -1714,7 +1716,9 @@ module AsyncSeq = for x in source do if i = index then yield value else yield x - i <- i + 1 } + i <- i + 1 + if i <= index then + invalidArg "index" "The index is outside the range of elements in the collection." } let insertAt (index : int) (value : 'T) (source : AsyncSeq<'T>) : AsyncSeq<'T> = asyncSeq { if index < 0 then invalidArg "index" "must be non-negative" diff --git a/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs b/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs index bbd08426..ae14eb37 100644 --- a/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs +++ b/tests/FSharp.Control.AsyncSeq.Tests/AsyncSeqTests.fs @@ -3784,6 +3784,24 @@ let ``AsyncSeq.removeAt raises ArgumentException for negative index`` () = |> Async.RunSynchronously |> ignore) |> ignore +[] +let ``AsyncSeq.removeAt raises ArgumentException when index is out of range`` () = + Assert.Throws(fun () -> + AsyncSeq.ofSeq [ 1; 2; 3 ] + |> AsyncSeq.removeAt 10 + |> AsyncSeq.toArrayAsync + |> Async.RunSynchronously |> ignore) + |> ignore + +[] +let ``AsyncSeq.removeAt raises ArgumentException when index equals sequence length`` () = + Assert.Throws(fun () -> + AsyncSeq.ofSeq [ 1; 2; 3 ] + |> AsyncSeq.removeAt 3 + |> AsyncSeq.toArrayAsync + |> Async.RunSynchronously |> ignore) + |> ignore + // ===== updateAt ===== [] @@ -3822,6 +3840,24 @@ let ``AsyncSeq.updateAt raises ArgumentException for negative index`` () = |> Async.RunSynchronously |> ignore) |> ignore +[] +let ``AsyncSeq.updateAt raises ArgumentException when index is out of range`` () = + Assert.Throws(fun () -> + AsyncSeq.ofSeq [ 1; 2; 3 ] + |> AsyncSeq.updateAt 10 99 + |> AsyncSeq.toArrayAsync + |> Async.RunSynchronously |> ignore) + |> ignore + +[] +let ``AsyncSeq.updateAt raises ArgumentException when index equals sequence length`` () = + Assert.Throws(fun () -> + AsyncSeq.ofSeq [ 1; 2; 3 ] + |> AsyncSeq.updateAt 3 99 + |> AsyncSeq.toArrayAsync + |> Async.RunSynchronously |> ignore) + |> ignore + // ===== insertAt ===== [] diff --git a/version.props b/version.props index 30ed08f9..eda5e801 100644 --- a/version.props +++ b/version.props @@ -1,5 +1,5 @@ - 4.14.0 + 4.15.0