Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Use 'StartsWith' instead of 'IndexOf' analyzer #6295

Merged
merged 28 commits into from Dec 19, 2022

Conversation

Youssef1313
Copy link
Member

@Youssef1313 Youssef1313 commented Dec 1, 2022

@Youssef1313 Youssef1313 requested a review from a team as a code owner December 1, 2022 21:00
@codecov
Copy link

codecov bot commented Dec 1, 2022

Codecov Report

Merging #6295 (c358bfb) into main (7af823a) will increase coverage by 0.00%.
The diff coverage is 98.72%.

❗ Current head c358bfb differs from pull request most recent head d22ec17. Consider uploading reports for the commit d22ec17 to get more accurate results

Additional details and impacted files
@@           Coverage Diff            @@
##             main    #6295    +/-   ##
========================================
  Coverage   96.08%   96.09%            
========================================
  Files        1357     1362     +5     
  Lines      315287   316086   +799     
  Branches    10184    10209    +25     
========================================
+ Hits       302951   303740   +789     
- Misses       9904     9909     +5     
- Partials     2432     2437     +5     

@tarekgh
Copy link
Member

tarekgh commented Dec 2, 2022

@Youssef1313 thanks a lot for submitting the PR.

You may expect a slow response from me as I am currently off. May others get a chance to comment too.


namespace Microsoft.NetCore.Analyzers.Performance.UnitTests
{
public class UseStartsWithInsteadOfIndexOfComparisonWithZeroTests
Copy link
Member

Choose a reason for hiding this comment

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

UseStartsWithInsteadOfIndexOfComparisonWithZeroTests

We need to add a test cases for IndexOf overloads which takes 2 arguments (with StringComparison) too.

Copy link
Member Author

Choose a reason for hiding this comment

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

@tarekgh What about IndexOf(char, StringComparison)? There is no char overload in StartsWith that accepts StringComparison. Should we not report? Or use StartsWith(string, StringComparison)? Or something else?

Copy link
Member

@tarekgh tarekgh Dec 2, 2022

Choose a reason for hiding this comment

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

That is a good question. If it will not be complicated to implement, we can use StartsWith(ReadOnlySpan, ReadOnlySpan, StringComparison). In general, I see the IndexOf overloads that takes string is higher priority as these used in main scenario. We can support the char overloads later if we need to.

CC @stephentoub if has input here.

Copy link
Member Author

Choose a reason for hiding this comment

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

@stephentoub What do you think?

Copy link
Member

Choose a reason for hiding this comment

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

@Youssef1313 how complex would it be if implement IndexOf(char, StringComparison) as I suggested above?

Copy link
Member Author

Choose a reason for hiding this comment

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

Found dotnet/roslyn#61432, but it doesn't seem like the comment there applies in the test project in this repo (I don't see we reference Newtonsoft.Json in test project - maybe another dependency does and we get it as a transitive dependency?).

@sharwell Can you help please?

Copy link
Member Author

Choose a reason for hiding this comment

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

@tarekgh One more question is IndexOf(Char). There is StartsWith(Char), but it is newer and might not be available (e.g, in .NET Standard 2.0 IndexOf(Char) exists but StartsWith(Char) doesn't). In this case (only if StartsWith(Char) doesn't exist) should we replace IndexOf('a') with StartsWith('a'.ToString())/StartsWith("a")?

Copy link
Member

Choose a reason for hiding this comment

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

No, the whole point of this analyzer is the performance.

for s.IndexOf(c) == 0 you can convert it to

s.length > 0 && s[0] == c

Copy link
Member Author

Choose a reason for hiding this comment

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

Makes sense.

I started looking into IndexOf(char, StringComparison) and there is one thing.

we can use StartsWith(ReadOnlySpan, ReadOnlySpan, StringComparison)

Here, we need to change the char to a ReadOnlySpan<char>

The ReadOnlySpan<T>(T) ctor is only available in .NET 7, the other way is to use stackalloc char[1] { ch }. Is that okay? What about VB? I'm also not sure about ReadOnlySpan (ref structs in general) support in VB.

Copy link
Member

@tarekgh tarekgh Dec 8, 2022

Choose a reason for hiding this comment

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

to use stackalloc char[1] { ch }. Is that okay?

Yes, this should be Ok. You just convert the IndexOf call too string.AsSpan().StartsWith(stackalloc char[1] { chr }, stringComparison);

What about VB? I'm also not sure about ReadOnlySpan (ref structs in general) support in VB.

Considering it is rare to find the pattern IndexOf(char, StringComparison) == 0 in VB. I think for this case we either exclude this rule from there or we can go with the fixer that allocates the string from the character. I mean use StartsWith(char.ToString(), StringComparison).

@Youssef1313
Copy link
Member Author

@tarekgh I handled IndexOf(char, StringComparison) overload

For VB, if char is hardcoded character literal, I switch it to a hardcoded string literal. Otherwise, I call .ToString()
For C#, I use AsSpan().StartsWith(stackalloc ...), though if language version is < 8, this will produce an error. I think we won't care that much for this niche case where the user can simply upgrade their language version.

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.

@Youssef1313 thanks a lot for getting this ready.

@buyaa-n could you please have a look? The changes LGTM. You may merge it after reviewing too. Thanks!

@tarekgh
Copy link
Member

tarekgh commented Dec 15, 2022

@Youssef1313 could you please fix the merge conflicts?

@jeffhandley is there anyone who can look and merge this one? The changes look good to me.

""";

await VerifyCodeFixVBAsync(testCode, fixedCode, ReferenceAssemblies.NetStandard.NetStandard20);
await VerifyCodeFixVBAsync(testCode, fixedCode, ReferenceAssemblies.NetStandard.NetStandard21);
Copy link
Member

Choose a reason for hiding this comment

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

Could you add tests for the cases that should not be flagged?

Copy link
Member

@buyaa-n buyaa-n left a comment

Choose a reason for hiding this comment

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

Left few comments, overall LGTM, thank you!

@Youssef1313
Copy link
Member Author

@buyaa-n Thanks for the review!

I addressed your feedback. Can you take another look? Thanks!

@buyaa-n buyaa-n enabled auto-merge (squash) December 19, 2022 22:35
@buyaa-n
Copy link
Member

buyaa-n commented Dec 19, 2022

I addressed your feedback. Can you take another look? Thanks!

Looks great, thank you!

@buyaa-n buyaa-n merged commit 10632e6 into dotnet:main Dec 19, 2022
@github-actions github-actions bot added this to the vNext milestone Dec 19, 2022
@Youssef1313 Youssef1313 deleted the starts-with branch December 20, 2022 18:33
@Youssef1313
Copy link
Member Author

Thanks @buyaa-n for the quick review!

I opened the docs PR: dotnet/docs#33114

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Analyzer] Using StartsWith instead of IndexOf == 0
3 participants