Skip to content

StringTextSource.LastIndexOf(char) throws ArgumentOutOfRangeException on empty search range at offset 0 #575

@udlose

Description

@udlose

Important

Breaking change

Fixing this changes observable behavior: callers that currently catch (ArgumentOutOfRangeException) around LastIndexOf with count = 0 would stop entering their catch block. Any downstream code that relies on the exception (intentionally or via defensive try/catch) will behave differently after the fix.

Summary

StringTextSource.LastIndexOf(char c, int startIndex, int count) throws ArgumentOutOfRangeException when called with startIndex = 0 and count = 0. An empty search range should return -1 (not found), consistent with IndexOf(char, 0, 0) which correctly returns -1.

Repro

var source = new StringTextSource("Hello");
int result = source.LastIndexOf('H', 0, 0); // throws ArgumentOutOfRangeException

Expected

Returns -1 - an empty search range contains no characters, so nothing can be found.

Actual

System.ArgumentOutOfRangeException: Index was out of range.
Must be non-negative and less than the size of the collection. (Parameter 'startIndex')
   at System.String.LastIndexOf(Char value, Int32 startIndex, Int32 count)
   at AvaloniaEdit.Document.StringTextSource.LastIndexOf(Char c, Int32 startIndex, Int32 count)

Root cause

ITextSource.LastIndexOf uses forward-range semantics: startIndex is the left edge, count is the range width, and the search proceeds backward within [startIndex, startIndex + count).

string.LastIndexOf uses backward-search semantics: startIndex is the rightmost position to begin searching, and count is how many positions to walk left.

The implementation bridges these two conventions with:

return Text.LastIndexOf(c, startIndex + count - 1, count);

When count = 0, this computes startIndex + 0 - 1 = -1, which string.LastIndexOf rejects.

The same issue exists in the LastIndexOf(string, ...) overload which uses the same arithmetic.

Affected overloads

  • LastIndexOf(char c, int startIndex, int count)
  • LastIndexOf(string searchText, int startIndex, int count, StringComparison comparisonType)

Inconsistency with IndexOf

Method Call Result
IndexOf('H', 0, 0) Works -1
LastIndexOf('H', 0, 0) Throws ArgumentOutOfRangeException

Both should return -1 for an empty search range.

Inconsistency with BCL

.NET's string.LastIndexOf('H', 0, 0) correctly returns -1. The bug is not in the BCL - it is in StringTextSource's arithmetic translation from ITextSource's forward-range convention to string.LastIndexOf's backward-search convention. The -1 underflow only occurs because of the startIndex + count - 1 bridging formula.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions