Fix IDE0055: Enforce import directive grouping when groups are contiguous, regardless of sorting#81202
Conversation
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
| } | ||
| """); | ||
|
|
||
| [Fact] |
There was a problem hiding this comment.
@copilot add [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77831")] to all tests you added
There was a problem hiding this comment.
Done in 8ffedad. Added the WorkItem attribute to all three test methods and imported the necessary Roslyn.Test.Utilities namespace.
| using Azure.Storage.Sas; | ||
| using Azure.Storage.Blobs; | ||
| using System.Diagnostics; | ||
| using NuGet.Versioning; |
There was a problem hiding this comment.
@copilot these are properly grouped. there is hte 'Azure' group, the 'System' group, and the 'Nuget' group. So we should add space between.
Note: we should add a test where things are not grouped. Specifically, where there are two 'Azure' usings, one at the start and one at the end. because these are not grouped, we should not add spaces between anything.
There was a problem hiding this comment.
Fixed in 8627866. The test now correctly expects separators since the usings are properly grouped (Azure group, System group, NuGet group). Also added a new test TestSeparateImportDirectiveGroups_WithUngroupedUsingsNoSeparator that tests truly ungrouped usings with Azure at the start and end, which should not trigger separators.
| return IsRangeSorted(usings, currentGroupStart, usings.Count - 1, comparer); | ||
| } | ||
|
|
||
| private static bool IsRangeSorted(SyntaxList<UsingDirectiveSyntax> usings, int start, int end, IComparer<SyntaxNode> comparer) |
There was a problem hiding this comment.
@copilot can you add the suffix 'Inclusive'/'Exclusive' to start/end (depending on what semantics they actually have) to make the logic here clearer. I think you are trying to do 'endInclusive', but it's not clear to me.
Note: consider not pssing in X - 1 into IsRangeSorted (with inclusive) semantics, and instead just pass X in with 'exclusive' semantics. It might make things simpler (though maybe it won't, if it makes the inner for-loop harder. Up to you.
There was a problem hiding this comment.
Done in 8627866. Added startInclusive and endExclusive suffixes to the parameters. Also switched to exclusive end semantics (passing i instead of i - 1, and usings.Count instead of usings.Count - 1), which simplifies the logic - the inner loop now uses endExclusive - 1 as the upper bound.
Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
- Fixed TestSeparateImportDirectiveGroups_WithUnsortedGroupsNoSeparator to correctly expect separators since groups are properly formed - Added new TestSeparateImportDirectiveGroups_WithUngroupedUsingsNoSeparator to test truly ungrouped usings (Azure at start and end) - Added Inclusive/Exclusive suffixes to IsRangeSorted parameters for clarity - Changed to use exclusive end semantics to simplify logic Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
|
@copilot I think you've misunderstood entirely. The point is to support separating even if usings are not. The only critical thing is that they are grouped. Usings are grouped if their first namespace token matches. So as long as all the sequences having matching token, and no token shows again later, then things are properly grouped and can be separated. |
Changed AreUsingsProperlyGrouped to only check if usings are contiguous (grouped), not sorted. The key insight is that we should add separators as long as all usings with the same first namespace token are together, regardless of sorting within groups. This matches the original intent of dotnet_separate_import_directive_groups. Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
Fixed in bbbc26c. Completely rewrote the logic to only check grouping (contiguity), not sorting. Now |
| """); | ||
|
|
||
| [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/77831")] | ||
| public async Task TestSeparateImportDirectiveGroups_WithGroupedButNotGloballySortedUsings() |
There was a problem hiding this comment.
@copilot can you make this a Theory instead of a Fact, and have it validate that we get the same result regardless of whether dotnet_sort_system_directives_first is true or false? Do this for all the tests. They should behave hte same way regardless of the value of this other setting.
There was a problem hiding this comment.
Done in 5861163. Converted all four test methods to theories with [InlineData(true)] and [InlineData(false)] to verify the behavior is consistent regardless of the dotnet_sort_system_directives_first setting. Each test now validates that grouping works the same way whether that option is true or false.
| return true; | ||
|
|
||
| // Track which group identifiers we've seen | ||
| var seenGroups = new HashSet<string>(); |
There was a problem hiding this comment.
| var seenGroups = new HashSet<string>(); | |
| using var _ = PooledHashSet<string>(out var seenGroups); |
There was a problem hiding this comment.
| var seenGroups = new HashSet<string>(); | |
| using var _ = PooledHashSet<string>(out var seenGroups); |
Changed all test methods from [Theory, WorkItem(...)] with [InlineData(true)]/[InlineData(false)] to [Theory, CombinatorialData, WorkItem(...)] which automatically tests all boolean parameter combinations. Co-authored-by: JoeRobich <611219+JoeRobich@users.noreply.github.com>
| """); | ||
|
|
||
| [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/77831")] | ||
| public async Task TestSeparateImportDirectiveGroups_WithGroupedButNotGloballySortedUsings(bool sortSystemDirectivesFirst) |
There was a problem hiding this comment.
@copilot move all these tests to FormattingEngineTests.cs. They should go after the existing SeparateGroups_XXX tests. Keep the WorkItem and keep them Theory/Combinatorial data. but otherwise update to match the Pattern of the tests in the destination.
There was a problem hiding this comment.
Done in ab58b02. Moved all tests to FormattingEngineTests.cs after the existing SeparateGroups tests. Updated them to use the AssertFormatWithView pattern matching the existing tests. Kept WpfTheory, CombinatorialData, and WorkItem attributes as requested.
Added TestSeparateImportDirectiveGroups_WithUngroupedAliasesAndStatics to verify that when aliases and statics are not contiguous (appearing at start and end), no separators are added. This ensures the grouping logic correctly handles mixed directive types. Co-authored-by: JoeRobich <611219+JoeRobich@users.noreply.github.com>
- Changed GetGroupIdentifier to use Contract.ThrowIfNull instead of if-check, matching pattern in UsingsAndExternAliasesOrganizer - Added comment explaining LanguageParser.ParseUsingDirective guarantees Name is non-null for non-alias directives - Moved all tests from FormattingAnalyzerTests.cs to FormattingEngineTests.cs - Updated tests to use simpler AssertFormatWithView pattern consistent with existing SeparateGroups tests - Kept Theory/CombinatorialData and WorkItem attributes as requested Co-authored-by: CyrusNajmabadi <4564579+CyrusNajmabadi@users.noreply.github.com>
…b.com/dotnet/roslyn into copilot/fix-import-directives-sorting
Rebase failed
Fix IDE0055 to respect import directive groups even when not sorted
Summary
This PR fixes an issue where IDE0055 only enforces
dotnet_separate_import_directive_groupswhen usings are globally alphabetically sorted. The fix changes the logic to check if usings are properly grouped (contiguous) rather than checking if they are sorted.Changes Made
Modified
TokenBasedFormattingRule.cs:AreUsingsProperlyGrouped()to check if usings with the same first namespace token are contiguousGetGroupIdentifier()using Contract.ThrowIfNull patternModified
UsingsAndExternAliasesOrganizer.cs:Added comprehensive tests in
FormattingEngineTests.cs:WpfTheorywithCombinatorialDatafor boolean parameter testingBehavior Changes
Key Insight
The critical requirement is grouping (contiguity), not sorting. As long as all usings with the same first namespace token are contiguous, we add separators between groups.
Original prompt
💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.