diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/LayoutRules/SA1516UsingGroupsCSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/LayoutRules/SA1516UsingGroupsCSharp10UnitTests.cs index 255ab862b..545783bd2 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/LayoutRules/SA1516UsingGroupsCSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/LayoutRules/SA1516UsingGroupsCSharp10UnitTests.cs @@ -3,9 +3,63 @@ namespace StyleCop.Analyzers.Test.CSharp10.LayoutRules { + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis.Testing; + using StyleCop.Analyzers.LayoutRules; + using StyleCop.Analyzers.Settings.ObjectModel; using StyleCop.Analyzers.Test.CSharp9.LayoutRules; + using Xunit; + using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< + StyleCop.Analyzers.LayoutRules.SA1516ElementsMustBeSeparatedByBlankLine, + StyleCop.Analyzers.LayoutRules.SA1516CodeFixProvider>; public partial class SA1516UsingGroupsCSharp10UnitTests : SA1516UsingGroupsCSharp9UnitTests { + [Fact] + [WorkItem(3982, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3982")] + public async Task TestBlankLineRequiredBetweenGlobalAndLocalUsingGroupsAsync() + { + var testCode = @" +global using System; +global using System.Threading; +{|#0:using System.IO;|} + +Console.WriteLine(); +"; + + var fixedCode = @" +global using System; +global using System.Threading; + +using System.IO; + +Console.WriteLine(); +"; + + await new CSharpTest + { + TestCode = testCode, + FixedCode = fixedCode, + Settings = GetBlankLinesBetweenUsingGroupsSettings(OptionSetting.Require), + ExpectedDiagnostics = + { + Diagnostic(SA1516ElementsMustBeSeparatedByBlankLine.DescriptorRequire).WithLocation(0), + }, + }.RunAsync(CancellationToken.None).ConfigureAwait(false); + } + + private static string GetBlankLinesBetweenUsingGroupsSettings(OptionSetting optionSetting) + { + return $@"{{ + ""settings"": {{ + ""orderingRules"": {{ + ""systemUsingDirectivesFirst"": true, + ""blankLinesBetweenUsingGroups"": ""{optionSetting.ToString().ToLowerInvariant()}"" + }} + }} +}} +"; + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs index 6db7eec07..1831fe0fe 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs @@ -5,6 +5,7 @@ namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules { using System.Threading; using System.Threading.Tasks; + using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.OrderingRules; using StyleCop.Analyzers.Test.CSharp9.OrderingRules; @@ -84,5 +85,25 @@ namespace TestNamespace await VerifyCSharpFixAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false); } + + [Fact] + [WorkItem(3982, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3982")] + public async Task TestGlobalUsingStatementWithTopLevelStatementsAsync() + { + var testCode = @"global using System; +using System.Threading; + +Console.WriteLine(); +"; + + await new CSharpTest + { + TestState = + { + Sources = { testCode }, + OutputKind = OutputKind.ConsoleApplication, + }, + }.RunAsync(CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1208CSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1208CSharp10UnitTests.cs index c28eb17b6..2bc93f968 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1208CSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1208CSharp10UnitTests.cs @@ -129,5 +129,45 @@ class TestClass await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } + + [Fact] + [WorkItem(3982, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3982")] + public async Task TestGlobalAndLocalUsingDirectivesMaintainIndependentSystemOrderingAsync() + { + await new CSharpTest + { + TestSources = + { + "namespace NameSpaceA { }", + "namespace OtherNamespace { }", + @" +global using NameSpaceA; +{|#0:global using System.Text;|} +global using System; + +using OtherNamespace; +{|#1:using System.IO;|} +using System; +", + }, + FixedSources = + { + @" +global using System; +global using System.Text; +global using NameSpaceA; + +using System; +using System.IO; +using OtherNamespace; +", + }, + ExpectedDiagnostics = + { + Diagnostic().WithLocation(0).WithArguments("System.Text", "NameSpaceA"), + Diagnostic().WithLocation(1).WithArguments("System.IO", "OtherNamespace"), + }, + }.RunAsync(CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1209CSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1209CSharp10UnitTests.cs index 1e8115028..27c4688b6 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1209CSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1209CSharp10UnitTests.cs @@ -108,5 +108,43 @@ class TestClass await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } + + [Fact] + [WorkItem(3982, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3982")] + public async Task TestAliasOrderingIsIndependentForGlobalAndLocalUsingDirectivesAsync() + { + await new CSharpTest + { + TestSources = + { + @" +{|#0:global using Alias = System.Console;|} +global using System; +global using System.Threading; + +{|#1:using AliasLocal = System.Text.StringBuilder;|} +using System; +using System.IO; +", + }, + FixedSources = + { + @" +global using System; +global using System.Threading; +global using Alias = System.Console; + +using System; +using System.IO; +using AliasLocal = System.Text.StringBuilder; +", + }, + ExpectedDiagnostics = + { + Diagnostic().WithLocation(0), + Diagnostic().WithLocation(1), + }, + }.RunAsync(CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1216CSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1216CSharp10UnitTests.cs index e71ec18dc..eb6dad955 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1216CSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1216CSharp10UnitTests.cs @@ -108,5 +108,43 @@ class TestClass await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } + + [Fact] + [WorkItem(3982, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3982")] + public async Task TestOrderingResetsBetweenGlobalAndLocalStaticAndAliasUsingDirectivesAsync() + { + await new CSharpTest + { + TestSources = + { + @" +{|#0:global using static System.Math;|} +global using System; +global using Alias = System.Console; + +using System; +using AliasLocal = System.Text.StringBuilder; +{|#1:using static System.Console;|} +", + }, + FixedSources = + { + @" +global using System; +global using static System.Math; +global using Alias = System.Console; + +using System; +using static System.Console; +using AliasLocal = System.Text.StringBuilder; +", + }, + ExpectedDiagnostics = + { + Diagnostic().WithLocation(0), + Diagnostic().WithLocation(1), + }, + }.RunAsync(CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1217CSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1217CSharp10UnitTests.cs index 7688126f7..8da4040ba 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1217CSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1217CSharp10UnitTests.cs @@ -110,5 +110,39 @@ class TestClass await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } + + [Fact] + [WorkItem(3982, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3982")] + public async Task TestAlphabeticalOrderingEnforcedSeparatelyForGlobalAndLocalStaticUsingDirectivesAsync() + { + await new CSharpTest + { + TestSources = + { + @" +global using static System.Math; +{|#0:global using static System.Array;|} + +using static System.Console; +{|#1:using static System.Array;|} +", + }, + FixedSources = + { + @" +global using static System.Array; +global using static System.Math; + +using static System.Array; +using static System.Console; +", + }, + ExpectedDiagnostics = + { + Diagnostic().WithLocation(0).WithArguments("System.Array", "System.Math"), + Diagnostic().WithLocation(1).WithArguments("System.Array", "System.Console"), + }, + }.RunAsync(CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/documentation/SA1516.md b/documentation/SA1516.md index f86801575..865cfe5e4 100644 --- a/documentation/SA1516.md +++ b/documentation/SA1516.md @@ -21,6 +21,8 @@ Adjacent C# elements are not separated by a blank line. > :memo: The behavior of this rule can change based on the configuration of the `blankLinesBetweenUsingGroups` property in **stylecop.json**. See [Configuration.md](Configuration.md) for more information. +> :memo: Global using directives (C# 10) are treated as their own set of using groups. When `blankLinesBetweenUsingGroups` requires separation, the rule enforces blank lines between the global using block and subsequent namespace-level usings or top-level statements in addition to the usual spacing within each set of using directives. + ## Rule description To improve the readability of the code, StyleCop requires blank lines in certain situations, and prohibits blank lines in other situations. This results in a consistent visual pattern across the code, which can improve recognition and readability of unfamiliar code.