Skip to content

Conversation

@GrabYourPitchforks
Copy link
Member

Fixes #13382.
Fixes #13383.

string.LastIndexOf and MemoryExtensions.LastIndexOf are inconsistent with how they handle zero-length target strings with various StringComparison arguments. In some cases they return string.Length - 1. In some cases they return span.Length. And in some cases they return 0.

This PR normalizes the behavior of all of these APIs so that a zero-length target substring is always found at the end of the search space. Since LastIndexOf's parameters vary significantly from IndexOf's parameters, I also included a code comment right in front of string.IndexOf(string) that explains the concept of "search space" vis-à-vis this particular API.

There's also some small comment cleanup throughout the code paths which deal with these checks.

This PR was extracted from #1514 into its own standalone review. This PR addresses only LastIndexOf and doesn't address the zero-weight code point issue tracked by that PR.

- string.LastIndexOf(string.Empty) shouldn't perform -1 adjustment
- MemoryExtensions.LastIndexOf(ROS<char>, string.Empty) shouldn't return 0
- Tighten up some existing unit tests
@GrabYourPitchforks GrabYourPitchforks added area-System.Runtime breaking-change Issue or PR that represents a breaking API or functional change over a prerelease. labels Apr 7, 2020
@GrabYourPitchforks GrabYourPitchforks requested review from adamsitnik and bartonjs and removed request for ahsonkhan April 7, 2020 00:36
@GrabYourPitchforks
Copy link
Member Author

Note to @tarekgh: This PR doesn't have the "avoid validating CompareOptions if you can early-exit" concerns that you raised in #1514 (comment). But that's because this PR doesn't attempt to solve the zero-weight code point problem. 😉 Once I spin #1514 back up we'll have to deal with the issue at that time since it'll necessarily involve contorting control flow.

@tarekgh
Copy link
Member

tarekgh commented Apr 7, 2020

CC @safern just in case he is touching some of the changed files here.

Copy link
Member

@tarekgh tarekgh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like the change and now our behavior is more understandable. only one thing, we need to document this as a breaking change. right? if so, can we ensure this is written so we can point users to the doc when anyone complains? Thanks for clearing this area.

@GrabYourPitchforks
Copy link
Member Author

This issue is marked with a breaking change tag. In theory it should be caught in any pre-release sweep we do to account for such changes in the docs.

@safern
Copy link
Member

safern commented Apr 7, 2020

CC @safern just in case he is touching some of the changed files here.

Thanks @tarekgh for the heads up. It seems only the file renames will conflict, I'll have to deal with it 😄

Copy link
Member

@adamsitnik adamsitnik left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👍 :shipit:

Assert.Equal(10, span.LastIndexOf(value.AsSpan(), StringComparison.CurrentCulture));
Assert.Equal(10, span.LastIndexOf(value.AsSpan(), StringComparison.CurrentCultureIgnoreCase));
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not related to your changes: IMHO this test (IndexOf_AllSubstrings) is very complex and is trying to test too many test cases at the same time. It would be better to split it into few smaller tests with self-describing names

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed, it's a bit of a bear. Do you have recommendations for how it might naturally be split?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that the two if blocks could become separate tests: value.Length == 0, s.Length == 0

@GrabYourPitchforks GrabYourPitchforks merged commit c4507d7 into dotnet:master Apr 7, 2020
@GrabYourPitchforks GrabYourPitchforks deleted the lastindexof branch April 7, 2020 19:16
@ghost ghost locked as resolved and limited conversation to collaborators Dec 9, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

area-System.Runtime breaking-change Issue or PR that represents a breaking API or functional change over a prerelease.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

string.LastIndexOf(string.Empty, ...) returns wrong index ROS<T>.LastIndexOf(ROS<T> value) returns wrong index when 'value' is empty

4 participants