Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- Fix enum contained flags check for partial matches in [RCS1258](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1258) ([PR](https://github.com/dotnet/roslynator/pull/1740) by @ovska)
- Fix analyzer [RCS1194](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1194) ([PR](https://github.com/dotnet/roslynator/pull/1733))
- Fix analyzer [RCS1077](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1077) ([PR](https://github.com/dotnet/roslynator/pull/1742) by @MattFromRVA)

## [4.15.0] - 2025-12-14

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -587,11 +587,26 @@ private static Task<Document> CallConvertAllInsteadOfSelectAsync(
in SimpleMemberInvocationExpressionInfo invocationInfo,
CancellationToken cancellationToken)
{
InvocationExpressionSyntax invocationExpression2 = SimpleMemberInvocationExpressionInfo(invocationInfo.Expression).InvocationExpression;
InvocationExpressionSyntax toListInvocation = invocationInfo.InvocationExpression;

InvocationExpressionSyntax newInvocationExpression = ChangeInvokedMethodName(invocationExpression2, "ConvertAll");
InvocationExpressionSyntax selectInvocation = SimpleMemberInvocationExpressionInfo(invocationInfo.Expression).InvocationExpression;

return document.ReplaceNodeAsync(invocationInfo.InvocationExpression, newInvocationExpression, cancellationToken);
InvocationExpressionSyntax newInvocationExpression = ChangeInvokedMethodName(selectInvocation, "ConvertAll");

IEnumerable<SyntaxTrivia> removedTrivia = toListInvocation.DescendantTrivia(
TextSpan.FromBounds(selectInvocation.Span.End, toListInvocation.Span.End));

if (removedTrivia.Any(f => !f.IsWhitespaceOrEndOfLineTrivia()))
{
newInvocationExpression = newInvocationExpression.WithTrailingTrivia(
removedTrivia.Where(f => !f.IsWhitespaceOrEndOfLineTrivia()).Concat(toListInvocation.GetTrailingTrivia()));
}
else
{
newInvocationExpression = newInvocationExpression.WithTrailingTrivia(toListInvocation.GetTrailingTrivia());
}

return document.ReplaceNodeAsync(toListInvocation, newInvocationExpression, cancellationToken);
}

private static Task<Document> CallSumInsteadOfSelectManyAndCountAsync(
Expand Down
187 changes: 187 additions & 0 deletions src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1551,6 +1551,193 @@ void M()
var x = q.OrderBy(f => f);
}
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.OptimizeLinqMethodCall)]
public async Task Test_CallConvertAllInsteadOfSelectAndToList_List_SemicolonNotMoved()
{
await VerifyDiagnosticAndFixAsync(@"
using System.Collections.Generic;
using System.Linq;

interface IEvent { }

class SpecialistBioUpdatedEvent : IEvent
{
public SpecialistBioUpdatedEvent(int profileId) { }
}

class SpecialistBio
{
public int ProfileId { get; }
}

class EventPublisher
{
public void EnqueueLowPriorityEvent(System.Func<object> action) { }
}

class BatchEvent
{
public BatchEvent(IReadOnlyList<IEvent> events) { }
}
Comment on lines +1564 to +1584
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please make test a little bit more concise. You don't need these classes for the test. Applies to other new tests as well.


class C
{
void M(EventPublisher eventPublisher, List<SpecialistBio> normalizedSpecialistBios)
{
eventPublisher.EnqueueLowPriorityEvent(() =>
{
IReadOnlyList<IEvent> events = normalizedSpecialistBios
.[|Select(sb => new SpecialistBioUpdatedEvent(sb.ProfileId))
.ToList()|];

return new BatchEvent(events);
});
}
}
", @"
using System.Collections.Generic;
using System.Linq;

interface IEvent { }

class SpecialistBioUpdatedEvent : IEvent
{
public SpecialistBioUpdatedEvent(int profileId) { }
}

class SpecialistBio
{
public int ProfileId { get; }
}

class EventPublisher
{
public void EnqueueLowPriorityEvent(System.Func<object> action) { }
}

class BatchEvent
{
public BatchEvent(IReadOnlyList<IEvent> events) { }
}

class C
{
void M(EventPublisher eventPublisher, List<SpecialistBio> normalizedSpecialistBios)
{
eventPublisher.EnqueueLowPriorityEvent(() =>
{
IReadOnlyList<IEvent> events = normalizedSpecialistBios
.ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId));

return new BatchEvent(events);
});
}
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.OptimizeLinqMethodCall)]
public async Task Test_CallConvertAllInsteadOfSelectAndToList_PreservesCommentInRemovedSegment()
{
await VerifyDiagnosticAndFixAsync(@"
using System.Collections.Generic;
using System.Linq;

class SpecialistBio
{
public int ProfileId { get; }
}

class SpecialistBioUpdatedEvent
{
public SpecialistBioUpdatedEvent(int profileId) { }
}

class C
{
void M(List<SpecialistBio> normalizedSpecialistBios)
{
var events = normalizedSpecialistBios
.[|Select(sb => new SpecialistBioUpdatedEvent(sb.ProfileId))
/* comment in removed segment */.ToList()|];
}
}
", @"
using System.Collections.Generic;
using System.Linq;

class SpecialistBio
{
public int ProfileId { get; }
}

class SpecialistBioUpdatedEvent
{
public SpecialistBioUpdatedEvent(int profileId) { }
}

class C
{
void M(List<SpecialistBio> normalizedSpecialistBios)
{
var events = normalizedSpecialistBios
.ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId))/* comment in removed segment */;
}
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.OptimizeLinqMethodCall)]
public async Task Test_CallConvertAllInsteadOfSelectAndToList_PreservesTrailingCommentAfterStatement()
{
await VerifyDiagnosticAndFixAsync(@"
using System.Collections.Generic;
using System.Linq;

class SpecialistBio
{
public int ProfileId { get; }
}

class SpecialistBioUpdatedEvent
{
public SpecialistBioUpdatedEvent(int profileId) { }
}

class C
{
void M(List<SpecialistBio> normalizedSpecialistBios)
{
var events = normalizedSpecialistBios
.[|Select(sb => new SpecialistBioUpdatedEvent(sb.ProfileId))
.ToList()|]; // trailing comment
}
}
", @"
using System.Collections.Generic;
using System.Linq;

class SpecialistBio
{
public int ProfileId { get; }
}

class SpecialistBioUpdatedEvent
{
public SpecialistBioUpdatedEvent(int profileId) { }
}

class C
{
void M(List<SpecialistBio> normalizedSpecialistBios)
{
var events = normalizedSpecialistBios
.ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)); // trailing comment
}
}
");
}
}
Loading