From 70507789f1d130dbe187cbd7c784a950821ad555 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Apr 2026 00:22:21 +0000 Subject: [PATCH 1/2] Fix: removeAt and updateAt now raise ArgumentException for out-of-range index AsyncSeq.removeAt and AsyncSeq.updateAt previously silently returned the source sequence unchanged when the index was >= the sequence length, which is inconsistent with List.removeAt, Array.removeAt, and AsyncSeq.insertAt. After the enumeration loop, check that the index was actually reached; if not (i.e. i <= index at end), raise ArgumentException matching the message used by AsyncSeq.insertAt. Adds 6 new tests covering out-of-range and boundary indices. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- RELEASE_NOTES.md | 4 +++ src/FSharp.Control.AsyncSeq/AsyncSeq.fs | 8 +++-- .../AsyncSeqTests.fs | 36 +++++++++++++++++++ version.props | 2 +- 4 files changed, 47 insertions(+), 3 deletions(-) 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 From d73aa9f5662542da7d4527cf8b9dde54eda7397c Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Sun, 19 Apr 2026 00:22:24 +0000 Subject: [PATCH 2/2] ci: trigger checks