From 1c501d914007c97523525f0fbbe0fdaf852974d7 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Tue, 3 Feb 2026 09:28:08 -0500 Subject: [PATCH 1/4] Add failing test --- .../RCS1077OptimizeLinqMethodCallTests.cs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs b/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs index e78825022b..7a75508ee7 100644 --- a/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs @@ -1553,4 +1553,91 @@ void M() } "); } + + [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 action) { } +} + +class BatchEvent +{ + public BatchEvent(IReadOnlyList events) { } +} + +class C +{ + void M(EventPublisher eventPublisher, List normalizedSpecialistBios) + { + eventPublisher.EnqueueLowPriorityEvent(() => + { + IReadOnlyList 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 action) { } +} + +class BatchEvent +{ + public BatchEvent(IReadOnlyList events) { } +} + +class C +{ + void M(EventPublisher eventPublisher, List normalizedSpecialistBios) + { + eventPublisher.EnqueueLowPriorityEvent(() => + { + IReadOnlyList events = normalizedSpecialistBios + .ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)); + + return new BatchEvent(events); + }); + } +} +"); + } + + } From c84264268d19b7c3003b1bc4ee2c900372e0c874 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Tue, 3 Feb 2026 09:51:42 -0500 Subject: [PATCH 2/4] Update change log --- ChangeLog.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ChangeLog.md b/ChangeLog.md index c889f8fea5..e6910b9bcd 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +- 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 ### Added From e6cd046bbe6d89f88e61c04f832e1d5117a0e5f3 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Wed, 4 Feb 2026 11:33:36 -0500 Subject: [PATCH 3/4] Added code fix. Added unit tests to expand coverage --- .../OptimizeLinqMethodCallCodeFixProvider.cs | 21 +++- .../RCS1077OptimizeLinqMethodCallTests.cs | 100 ++++++++++++++++++ 2 files changed, 118 insertions(+), 3 deletions(-) diff --git a/src/Analyzers.CodeFixes/CSharp/CodeFixes/OptimizeLinqMethodCallCodeFixProvider.cs b/src/Analyzers.CodeFixes/CSharp/CodeFixes/OptimizeLinqMethodCallCodeFixProvider.cs index 42226365b3..c6154602fd 100644 --- a/src/Analyzers.CodeFixes/CSharp/CodeFixes/OptimizeLinqMethodCallCodeFixProvider.cs +++ b/src/Analyzers.CodeFixes/CSharp/CodeFixes/OptimizeLinqMethodCallCodeFixProvider.cs @@ -587,11 +587,26 @@ private static Task 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 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 CallSumInsteadOfSelectManyAndCountAsync( diff --git a/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs b/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs index 7a75508ee7..dd965ef508 100644 --- a/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs @@ -1639,5 +1639,105 @@ void M(EventPublisher eventPublisher, List normalizedSpecialistBi "); } + [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 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 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 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 normalizedSpecialistBios) + { + var events = normalizedSpecialistBios + .ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)); // trailing comment + } +} +"); + } } From f143154ccb6e4ee7f1fe977558fff58e143a6660 Mon Sep 17 00:00:00 2001 From: Matt Chaulklin Date: Mon, 23 Feb 2026 09:21:03 -0500 Subject: [PATCH 4/4] Made tests more concise --- .../RCS1077OptimizeLinqMethodCallTests.cs | 130 ++++-------------- 1 file changed, 24 insertions(+), 106 deletions(-) diff --git a/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs b/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs index dd965ef508..1707f86ca8 100644 --- a/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs +++ b/src/Tests/Analyzers.Tests/RCS1077OptimizeLinqMethodCallTests.cs @@ -1558,81 +1558,39 @@ void M() public async Task Test_CallConvertAllInsteadOfSelectAndToList_List_SemicolonNotMoved() { await VerifyDiagnosticAndFixAsync(@" +using System; 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 action) { } -} - -class BatchEvent -{ - public BatchEvent(IReadOnlyList events) { } -} - class C { - void M(EventPublisher eventPublisher, List normalizedSpecialistBios) + void M(Action> action, List items) { - eventPublisher.EnqueueLowPriorityEvent(() => + action(() => { - IReadOnlyList events = normalizedSpecialistBios - .[|Select(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)) + List x = items + .[|Select(f => f.ToString()) .ToList()|]; - return new BatchEvent(events); + return x; }); } } ", @" +using System; 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 action) { } -} - -class BatchEvent -{ - public BatchEvent(IReadOnlyList events) { } -} - class C { - void M(EventPublisher eventPublisher, List normalizedSpecialistBios) + void M(Action> action, List items) { - eventPublisher.EnqueueLowPriorityEvent(() => + action(() => { - IReadOnlyList events = normalizedSpecialistBios - .ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)); + List x = items + .ConvertAll(f => f.ToString()); - return new BatchEvent(events); + return x; }); } } @@ -1646,22 +1604,12 @@ 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 normalizedSpecialistBios) + void M(List items) { - var events = normalizedSpecialistBios - .[|Select(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)) + var x = items + .[|Select(f => f.ToString()) /* comment in removed segment */.ToList()|]; } } @@ -1669,22 +1617,12 @@ void M(List normalizedSpecialistBios) using System.Collections.Generic; using System.Linq; -class SpecialistBio -{ - public int ProfileId { get; } -} - -class SpecialistBioUpdatedEvent -{ - public SpecialistBioUpdatedEvent(int profileId) { } -} - class C { - void M(List normalizedSpecialistBios) + void M(List items) { - var events = normalizedSpecialistBios - .ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId))/* comment in removed segment */; + var x = items + .ConvertAll(f => f.ToString())/* comment in removed segment */; } } "); @@ -1697,22 +1635,12 @@ 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 normalizedSpecialistBios) + void M(List items) { - var events = normalizedSpecialistBios - .[|Select(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)) + var x = items + .[|Select(f => f.ToString()) .ToList()|]; // trailing comment } } @@ -1720,22 +1648,12 @@ void M(List normalizedSpecialistBios) using System.Collections.Generic; using System.Linq; -class SpecialistBio -{ - public int ProfileId { get; } -} - -class SpecialistBioUpdatedEvent -{ - public SpecialistBioUpdatedEvent(int profileId) { } -} - class C { - void M(List normalizedSpecialistBios) + void M(List items) { - var events = normalizedSpecialistBios - .ConvertAll(sb => new SpecialistBioUpdatedEvent(sb.ProfileId)); // trailing comment + var x = items + .ConvertAll(f => f.ToString()); // trailing comment } } ");