-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Description
Searching for the last index of an item in an instance of ImmutableList<T> that holds at least one matching item can return the wrong index. This happens when the index argument passed in to ImmutableList<T>.LastIndexOf(T item, int index, int count, IEqualityComparer<T>? equalityComparer) equals the collection size, at which point the returned index will be off by one (+1), e.g. if the actual index is 2 the returned index will be 3.
Reproduction Steps
A .NET 6 console application with the following top level statements:
using System.Collections.Immutable;
var lastIndexOf = new[] { "NonMatch", "Match", "Match", "NonMatch" }
.ToImmutableList()
.LastIndexOf("Match", index: 3, count: 4, equalityComparer: null); // Returns 2 (correct)
var lastIndexOf2 = new[] { "NonMatch", "Match", "Match", "NonMatch" }
.ToImmutableList()
.LastIndexOf("Match", index: 4, count: 4, equalityComparer: null); // Returns 3 (incorrect, should throw?)
Console.WriteLine($"Last index when 'index' argument equals count - 1: {lastIndexOf}");
Console.WriteLine($"Last index when 'index' argument equals count : {lastIndexOf2}");
Expected behavior
One of the two below:
ImmutableList<T>.LastIndexOf(T item, int index, int count, IEqualityComparer<T>? equalityComparer)should throw ifindexequals the size of the collection, the same waystartIndexdoes forImmutableArray<T>.LastIndexOf(T item, int startIndex, int count, IEqualityComparer<T>? equalityComparer ).ImmutableList<T>.LastIndexOf(T item, int index, int count, IEqualityComparer<T>? equalityComparer)should return the correct index of the last item found when theindexargument equals the size of the collection.
Actual behavior
The returned index is bigger (+1) than the correct index.
Regression?
No response
Known Workarounds
No response
Configuration
- .NET 6
- Windows 10, 64-bit
- x64
- Don't know if it is specific to this version
Other information
I suspect that the culprit is a missing guard clause for the upper boundary for the index parameter in the internal int LastIndexOf(T item, int index, int count, IEqualityComparer<T>? equalityComparer) method (line 786 as of this writing) in ImmutableList_1.Node.cs. This method is called from ImmutableList_1.cs.
Contrast the guard clause in the above mentiond method with the one from ImmutableArray_1.Builder.cs
ImmutableList<T> - Requires.Range(index >= 0, nameof(index));
vs
ImmutableArray<T> - Requires.Range(startIndex >= 0 && startIndex < this.Count, nameof(startIndex));
where the latter makes more sense.