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
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions