Skip to content

Commit 50e541d

Browse files
committed
Update SA1200 to simply ignore global using directive placement
1 parent 1d40ff8 commit 50e541d

File tree

3 files changed

+43
-15
lines changed

3 files changed

+43
-15
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.CodeFixes/OrderingRules/UsingCodeFixProvider.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,15 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
6969
continue;
7070
}
7171

72+
// Force preserving the placement of using directives when we are fixing a diagnostic not directly
73+
// related to placement of using directives inside/outside namespaces, and also when there are global
74+
// usings present.
75+
bool forcePreservePlacement = !isSA1200 || compilationUnit.Usings.Any(static syntax => syntax.GlobalKeyword().IsKind(SyntaxKind.GlobalKeyword));
76+
7277
context.RegisterCodeFix(
7378
CodeAction.Create(
7479
OrderingResources.UsingCodeFix,
75-
cancellationToken => GetTransformedDocumentAsync(context.Document, syntaxRoot, !isSA1200, cancellationToken),
80+
cancellationToken => GetTransformedDocumentAsync(context.Document, syntaxRoot, forcePreservePlacement, cancellationToken),
7681
nameof(UsingCodeFixProvider)),
7782
diagnostic);
7883
}
@@ -513,9 +518,14 @@ protected override async Task<SyntaxNode> FixAllInDocumentAsync(FixAllContext fi
513518
return null;
514519
}
515520

516-
var forcePreserve = diagnostics.All(d => d.Id != SA1200UsingDirectivesMustBePlacedCorrectly.DiagnosticId);
517-
518521
var syntaxRoot = await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
522+
523+
// Force preserving the placement of using directives when we are fixing any diagnostic not directly
524+
// related to placement of using directives inside/outside namespaces, and also when there are global
525+
// usings present.
526+
var forcePreserve = diagnostics.All(d => d.Id != SA1200UsingDirectivesMustBePlacedCorrectly.DiagnosticId)
527+
|| ((CompilationUnitSyntax)syntaxRoot).Usings.Any(static syntax => syntax.GlobalKeyword().IsKind(SyntaxKind.GlobalKeyword));
528+
519529
Document newDocument = await GetTransformedDocumentAsync(document, syntaxRoot, forcePreserve, fixAllContext.CancellationToken).ConfigureAwait(false);
520530
return await newDocument.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false);
521531
}

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

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,13 +55,36 @@ public async Task TestOnlyGlobalUsingStatementInFileAsync(string leadingTrivia)
5555
[WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")]
5656
public async Task TestGlobalUsingStatementInFileWithNamespaceAsync()
5757
{
58-
var testCode = @"[|global using System;|]
58+
var testCode = @"global using System;
5959
6060
namespace TestNamespace
6161
{
6262
}";
6363

6464
await VerifyCSharpDiagnosticAsync(testCode, DiagnosticResult.EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
6565
}
66+
67+
[Fact]
68+
[WorkItem(3875, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3875")]
69+
public async Task TestGlobalUsingStatementInFileWithOtherUsingDirectivesAsync()
70+
{
71+
var testCode = @"global using System;
72+
[|using System.Linq;|]
73+
74+
namespace TestNamespace
75+
{
76+
}";
77+
78+
await new CSharpTest
79+
{
80+
TestCode = testCode,
81+
82+
// UsingCodeFixProvider currently leaves all using directives in the same location (either inside or
83+
// outside the namespace) when the file contains any global using directives.
84+
FixedCode = testCode,
85+
NumberOfIncrementalIterations = 1,
86+
NumberOfFixAllIterations = 1,
87+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
88+
}
6689
}
6790
}

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

Lines changed: 6 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -216,7 +216,6 @@ 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;
220219
foreach (SyntaxNode child in syntax.ChildNodes())
221220
{
222221
switch (child.Kind())
@@ -238,26 +237,22 @@ private static void HandleCompilationUnit(SyntaxNodeAnalysisContext context, Sty
238237
return;
239238

240239
case SyntaxKind.UsingDirective:
241-
usingDirectives.Add(child);
242-
bool isGlobalUsing = ((UsingDirectiveSyntax)child).GlobalKeyword().IsKind(SyntaxKind.GlobalKeyword);
243-
containsOnlyGlobalUsingDirectives = containsOnlyGlobalUsingDirectives && isGlobalUsing;
240+
// Global using directives are only allowed at the top level, so ignore those
241+
if (!((UsingDirectiveSyntax)child).GlobalKeyword().IsKind(SyntaxKind.GlobalKeyword))
242+
{
243+
usingDirectives.Add(child);
244+
}
245+
244246
continue;
245247

246248
case SyntaxKind.NamespaceDeclaration:
247249
case SyntaxKindEx.FileScopedNamespaceDeclaration:
248250
case SyntaxKind.ExternAliasDirective:
249251
default:
250-
containsOnlyGlobalUsingDirectives = false;
251252
continue;
252253
}
253254
}
254255

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

0 commit comments

Comments
 (0)