Skip to content

Commit 82f80b1

Browse files
Merge pull request #5 from bjornhellander/feature/sa1200-only-global-usings
Update SA1200UsingDirectivesMustBePlacedCorrectly to not trigger in files with only global using directives
2 parents b2ea895 + 72834ff commit 82f80b1

File tree

5 files changed

+91
-7
lines changed

5 files changed

+91
-7
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200CSharp10UnitTests.cs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Contributors to the New StyleCop Analyzers project.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
#nullable disable
5-
64
namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
75
{
86
using System.Threading;
@@ -40,5 +38,30 @@ namespace TestNamespace;
4038

4139
await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
4240
}
41+
42+
[Theory]
43+
[InlineData("")]
44+
[InlineData("\n")]
45+
[InlineData("// A comment.\n")]
46+
[WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")]
47+
public async Task TestOnlyGlobalUsingStatementInFileAsync(string leadingTrivia)
48+
{
49+
var testCode = $@"{leadingTrivia}global using System;";
50+
51+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
52+
}
53+
54+
[Fact]
55+
[WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")]
56+
public async Task TestGlobalUsingStatementInFileWithNamespaceAsync()
57+
{
58+
var testCode = @"[|global using System;|]
59+
60+
namespace TestNamespace
61+
{
62+
}";
63+
64+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
65+
}
4366
}
4467
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200OutsideNamespaceCSharp10UnitTests.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Contributors to the New StyleCop Analyzers project.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
#nullable disable
5-
64
namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
75
{
86
using System.Threading;
@@ -36,5 +34,17 @@ namespace TestNamespace;
3634

3735
await VerifyCSharpFixAsync(testCode, expectedResults, fixedTestCode, CancellationToken.None).ConfigureAwait(false);
3836
}
37+
38+
[Theory]
39+
[InlineData("")]
40+
[InlineData("\n")]
41+
[InlineData("// A comment.\n")]
42+
[WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")]
43+
public async Task TestOnlyGlobalUsingStatementInFileAsync(string leadingTrivia)
44+
{
45+
var testCode = $@"{leadingTrivia}global using System;";
46+
47+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
48+
}
3949
}
4050
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp10/OrderingRules/SA1200PreserveCSharp10UnitTests.cs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Contributors to the New StyleCop Analyzers project.
22
// Licensed under the MIT License. See LICENSE in the project root for license information.
33

4-
#nullable disable
5-
64
namespace StyleCop.Analyzers.Test.CSharp10.OrderingRules
75
{
86
using System.Threading;
@@ -41,5 +39,17 @@ namespace TestNamespace;
4139

4240
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
4341
}
42+
43+
[Theory]
44+
[InlineData("")]
45+
[InlineData("\n")]
46+
[InlineData("// A comment.\n")]
47+
[WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")]
48+
public async Task TestOnlyGlobalUsingStatementInFileAsync(string leadingTrivia)
49+
{
50+
var testCode = $@"{leadingTrivia}global using System;";
51+
52+
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
53+
}
4454
}
4555
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
// Copyright (c) Contributors to the New StyleCop Analyzers project.
2+
// Licensed under the MIT License. See LICENSE in the project root for license information.
3+
4+
namespace StyleCop.Analyzers.Lightup
5+
{
6+
using System;
7+
using Microsoft.CodeAnalysis;
8+
using Microsoft.CodeAnalysis.CSharp.Syntax;
9+
10+
internal static class UsingDirectiveSyntaxExtensions
11+
{
12+
private static readonly Func<UsingDirectiveSyntax, SyntaxToken> GlobalKeywordAccessor;
13+
private static readonly Func<UsingDirectiveSyntax, SyntaxToken, UsingDirectiveSyntax> WithGlobalKeywordAccessor;
14+
15+
static UsingDirectiveSyntaxExtensions()
16+
{
17+
GlobalKeywordAccessor = LightupHelpers.CreateSyntaxPropertyAccessor<UsingDirectiveSyntax, SyntaxToken>(typeof(UsingDirectiveSyntax), nameof(GlobalKeyword));
18+
WithGlobalKeywordAccessor = LightupHelpers.CreateSyntaxWithPropertyAccessor<UsingDirectiveSyntax, SyntaxToken>(typeof(UsingDirectiveSyntax), nameof(GlobalKeyword));
19+
}
20+
21+
public static SyntaxToken GlobalKeyword(this UsingDirectiveSyntax syntax)
22+
{
23+
return GlobalKeywordAccessor(syntax);
24+
}
25+
26+
public static UsingDirectiveSyntax WithGlobalKeyword(this UsingDirectiveSyntax syntax, SyntaxToken globalKeyword)
27+
{
28+
return WithGlobalKeywordAccessor(syntax, globalKeyword);
29+
}
30+
}
31+
}

StyleCop.Analyzers/StyleCop.Analyzers/OrderingRules/SA1200UsingDirectivesMustBePlacedCorrectly.cs

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ private static void HandleCompilationUnit(SyntaxNodeAnalysisContext context, Sty
216216
CompilationUnitSyntax syntax = (CompilationUnitSyntax)context.Node;
217217

218218
List<SyntaxNode> usingDirectives = new List<SyntaxNode>();
219+
bool containsOnlyGlobalUsingDirectives = true;
219220
foreach (SyntaxNode child in syntax.ChildNodes())
220221
{
221222
switch (child.Kind())
@@ -238,16 +239,25 @@ private static void HandleCompilationUnit(SyntaxNodeAnalysisContext context, Sty
238239

239240
case SyntaxKind.UsingDirective:
240241
usingDirectives.Add(child);
242+
bool isGlobalUsing = ((UsingDirectiveSyntax)child).GlobalKeyword().IsKind(SyntaxKind.GlobalKeyword);
243+
containsOnlyGlobalUsingDirectives = containsOnlyGlobalUsingDirectives && isGlobalUsing;
241244
continue;
242245

243-
case SyntaxKind.ExternAliasDirective:
244246
case SyntaxKind.NamespaceDeclaration:
245247
case SyntaxKindEx.FileScopedNamespaceDeclaration:
248+
case SyntaxKind.ExternAliasDirective:
246249
default:
250+
containsOnlyGlobalUsingDirectives = false;
247251
continue;
248252
}
249253
}
250254

255+
if (containsOnlyGlobalUsingDirectives)
256+
{
257+
// Suppress SA1200 if file only contains global using directives
258+
return;
259+
}
260+
251261
foreach (var directive in usingDirectives)
252262
{
253263
// Using directive should appear within a namespace declaration

0 commit comments

Comments
 (0)