diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs index 3ca1c97b9..45175cc73 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. -#nullable disable - namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules { using System.Threading; @@ -40,5 +38,30 @@ namespace TestNamespace; await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false); } + + [Theory] + [InlineData("")] + [InlineData("\n")] + [InlineData("// A comment.\n")] + [WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")] + public async Task TestOnlyGlobalUsingStatementInFileAsync(string leadingTrivia) + { + var testCode = $@"{leadingTrivia}global using System;"; + + await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } + + [Fact] + [WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")] + public async Task TestGlobalUsingStatementInFileWithNamespaceAsync() + { + var testCode = @"[|global using System;|] + +namespace TestNamespace +{ +}"; + + await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200OutsideNamespaceCSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200OutsideNamespaceCSharp10UnitTests.cs index 98b05c70b..dc2bf0e89 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200OutsideNamespaceCSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200OutsideNamespaceCSharp10UnitTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. -#nullable disable - namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules { using System.Threading; @@ -36,5 +34,17 @@ namespace TestNamespace; await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false); } + + [Theory] + [InlineData("")] + [InlineData("\n")] + [InlineData("// A comment.\n")] + [WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")] + public async Task TestOnlyGlobalUsingStatementInFileAsync(string leadingTrivia) + { + var testCode = $@"{leadingTrivia}global using System;"; + + await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200PreserveCSharp10UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200PreserveCSharp10UnitTests.cs index c6bbad99e..f67eb33e9 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200PreserveCSharp10UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200PreserveCSharp10UnitTests.cs @@ -1,8 +1,6 @@ // Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. // Licensed under the MIT License. See LICENSE in the project root for license information. -#nullable disable - namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules { using System.Threading; @@ -41,5 +39,17 @@ namespace TestNamespace; await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); } + + [Theory] + [InlineData("")] + [InlineData("\n")] + [InlineData("// A comment.\n")] + [WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")] + public async Task TestOnlyGlobalUsingStatementInFileAsync(string leadingTrivia) + { + var testCode = $@"{leadingTrivia}global using System;"; + + await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/Lightup/UsingDirectiveSyntaxExtensions.cs b/StyleCop.Analyzers/StyleCop.Analyzers/Lightup/UsingDirectiveSyntaxExtensions.cs new file mode 100644 index 000000000..02f27de3e --- /dev/null +++ b/StyleCop.Analyzers/StyleCop.Analyzers/Lightup/UsingDirectiveSyntaxExtensions.cs @@ -0,0 +1,31 @@ +// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +namespace StyleCop.Analyzers.Lightup +{ + using System; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp.Syntax; + + internal static class UsingDirectiveSyntaxExtensions + { + private static readonly Func GlobalKeywordAccessor; + private static readonly Func WithGlobalKeywordAccessor; + + static UsingDirectiveSyntaxExtensions() + { + GlobalKeywordAccessor = LightupHelpers.CreateSyntaxPropertyAccessor(typeof(UsingDirectiveSyntax), nameof(GlobalKeyword)); + WithGlobalKeywordAccessor = LightupHelpers.CreateSyntaxWithPropertyAccessor(typeof(UsingDirectiveSyntax), nameof(GlobalKeyword)); + } + + public static SyntaxToken GlobalKeyword(this UsingDirectiveSyntax syntax) + { + return GlobalKeywordAccessor(syntax); + } + + public static UsingDirectiveSyntax WithGlobalKeyword(this UsingDirectiveSyntax syntax, SyntaxToken globalKeyword) + { + return WithGlobalKeywordAccessor(syntax, globalKeyword); + } + } +} diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1200UsingDirectivesMustBePlacedCorrectly.cs b/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1200UsingDirectivesMustBePlacedCorrectly.cs index a42d217e1..6d6adecb2 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1200UsingDirectivesMustBePlacedCorrectly.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1200UsingDirectivesMustBePlacedCorrectly.cs @@ -216,6 +216,7 @@ private static void HandleCompilationUnit(SyntaxNodeAnalysisContext context, Sty CompilationUnitSyntax syntax = (CompilationUnitSyntax)context.Node; List usingDirectives = new List(); + bool containsOnlyGlobalUsingDirectives = true; foreach (SyntaxNode child in syntax.ChildNodes()) { switch (child.Kind()) @@ -238,16 +239,25 @@ private static void HandleCompilationUnit(SyntaxNodeAnalysisContext context, Sty case SyntaxKind.UsingDirective: usingDirectives.Add(child); + bool isGlobalUsing = ((UsingDirectiveSyntax)child).GlobalKeyword().IsKind(SyntaxKind.GlobalKeyword); + containsOnlyGlobalUsingDirectives = containsOnlyGlobalUsingDirectives && isGlobalUsing; continue; - case SyntaxKind.ExternAliasDirective: case SyntaxKind.NamespaceDeclaration: case SyntaxKindEx.FileScopedNamespaceDeclaration: + case SyntaxKind.ExternAliasDirective: default: + containsOnlyGlobalUsingDirectives = false; continue; } } + if (containsOnlyGlobalUsingDirectives) + { + // Suppress SA1200 if file only contains global using directives + return; + } + foreach (var directive in usingDirectives) { // Using directive should appear within a namespace declaration