Skip to content

Commit caaf3e7

Browse files
authored
Merge pull request #4054 from sharwell/top-level-statements
Update for top-level statements
2 parents f206562 + 1eeb436 commit caaf3e7

File tree

5 files changed

+194
-11
lines changed

5 files changed

+194
-11
lines changed

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp11/LayoutRules/SA1516CSharp11UnitTests.cs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
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.CSharp11.LayoutRules
75
{
86
using Microsoft.CodeAnalysis.Testing;
97
using StyleCop.Analyzers.Test.CSharp10.LayoutRules;
10-
118
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
129
StyleCop.Analyzers.LayoutRules.SA1516ElementsMustBeSeparatedByBlankLine,
1310
StyleCop.Analyzers.LayoutRules.SA1516CodeFixProvider>;
@@ -33,5 +30,15 @@ protected override DiagnosticResult[] GetExpectedResultTestGlobalStatementAndRec
3330
Diagnostic().WithLocation(0),
3431
};
3532
}
33+
34+
protected override DiagnosticResult[] GetExpectedResultTopLevelStatementsFollowedByType()
35+
{
36+
// NOTE: Roslyn bug fix. Earlier versions made diagnostics be reported twice.
37+
return new[]
38+
{
39+
Diagnostic().WithLocation(0),
40+
Diagnostic().WithLocation(1),
41+
};
42+
}
3643
}
3744
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/DocumentationRules/SA1649CSharp9UnitTests.cs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,99 @@
33

44
namespace StyleCop.Analyzers.Test.CSharp9.DocumentationRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis;
69
using StyleCop.Analyzers.Test.CSharp8.DocumentationRules;
10+
using StyleCop.Analyzers.Test.Verifiers;
11+
using Xunit;
12+
using static StyleCop.Analyzers.Test.Verifiers.CustomDiagnosticVerifier<StyleCop.Analyzers.DocumentationRules.SA1649FileNameMustMatchTypeName>;
713

814
public partial class SA1649CSharp9UnitTests : SA1649CSharp8UnitTests
915
{
16+
[Fact]
17+
[WorkItem(3967, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3967")]
18+
public async Task VerifyTopLevelStatementsOnlyDoNotReportAsync()
19+
{
20+
var testCode = @"System.Console.WriteLine();
21+
";
22+
23+
var test = new StyleCopCodeFixVerifier<StyleCop.Analyzers.DocumentationRules.SA1649FileNameMustMatchTypeName, StyleCop.Analyzers.DocumentationRules.SA1649CodeFixProvider>.CSharpTest()
24+
{
25+
TestState =
26+
{
27+
OutputKind = OutputKind.ConsoleApplication,
28+
Sources = { ("NameNotBasedOnTheCode.cs", testCode) },
29+
},
30+
};
31+
32+
await test.RunAsync(CancellationToken.None).ConfigureAwait(false);
33+
}
34+
35+
[Fact]
36+
[WorkItem(3967, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3967")]
37+
public async Task VerifyTopLevelStatementsFollowedByClassReportAsync()
38+
{
39+
var testCode = @"System.Console.WriteLine();
40+
41+
class {|#0:Helper|}
42+
{
43+
}
44+
";
45+
46+
var fixedCode = @"System.Console.WriteLine();
47+
48+
class Helper
49+
{
50+
}
51+
";
52+
53+
var test = new StyleCopCodeFixVerifier<StyleCop.Analyzers.DocumentationRules.SA1649FileNameMustMatchTypeName, StyleCop.Analyzers.DocumentationRules.SA1649CodeFixProvider>.CSharpTest()
54+
{
55+
TestState =
56+
{
57+
OutputKind = OutputKind.ConsoleApplication,
58+
Sources = { ("Program.cs", testCode) },
59+
},
60+
FixedSources = { ("Helper.cs", fixedCode) },
61+
};
62+
63+
test.TestState.ExpectedDiagnostics.Add(Diagnostic().WithLocation(0));
64+
await test.RunAsync(CancellationToken.None).ConfigureAwait(false);
65+
}
66+
67+
[Fact]
68+
[WorkItem(3967, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3967")]
69+
public async Task VerifyTopLevelStatementsFollowedByEnumReportAsync()
70+
{
71+
var testCode = @"System.Console.WriteLine();
72+
73+
enum {|#0:Helper|}
74+
{
75+
Value,
76+
}
77+
";
78+
79+
var fixedCode = @"System.Console.WriteLine();
80+
81+
enum Helper
82+
{
83+
Value,
84+
}
85+
";
86+
87+
var test = new StyleCopCodeFixVerifier<StyleCop.Analyzers.DocumentationRules.SA1649FileNameMustMatchTypeName, StyleCop.Analyzers.DocumentationRules.SA1649CodeFixProvider>.CSharpTest()
88+
{
89+
TestState =
90+
{
91+
OutputKind = OutputKind.ConsoleApplication,
92+
Sources = { ("Program.cs", testCode) },
93+
},
94+
FixedSources = { ("Helper.cs", fixedCode) },
95+
};
96+
97+
test.TestState.ExpectedDiagnostics.Add(Diagnostic().WithLocation(0));
98+
await test.RunAsync(CancellationToken.None).ConfigureAwait(false);
99+
}
10100
}
11101
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/LayoutRules/SA1516CSharp9UnitTests.cs

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
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.CSharp9.LayoutRules
75
{
86
using System.Threading;
@@ -151,6 +149,44 @@ public int X
151149
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
152150
}
153151

152+
/// <summary>
153+
/// Verifies spacing expectations when assembly attributes, top-level statements, and types appear in the same file.
154+
/// </summary>
155+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
156+
[Fact]
157+
[WorkItem(3967, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3967")]
158+
public async Task TestAssemblyAttributesTopLevelStatementsAndTypeAsync()
159+
{
160+
var testCode = @"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")]
161+
{|#0:System|}.Console.WriteLine();
162+
{|#1:class|} C
163+
{
164+
}
165+
";
166+
167+
var fixedCode = @"[assembly: System.Reflection.AssemblyVersion(""1.0.0.0"")]
168+
169+
System.Console.WriteLine();
170+
171+
class C
172+
{
173+
}
174+
";
175+
176+
var test = new CSharpTest()
177+
{
178+
TestState =
179+
{
180+
OutputKind = OutputKind.ConsoleApplication,
181+
Sources = { testCode },
182+
},
183+
FixedCode = fixedCode,
184+
};
185+
186+
test.TestState.ExpectedDiagnostics.AddRange(this.GetExpectedResultTopLevelStatementsFollowedByType());
187+
await test.RunAsync(CancellationToken.None).ConfigureAwait(false);
188+
}
189+
154190
protected virtual DiagnosticResult[] GetExpectedResultTestUsingAndGlobalStatementSpacingInTopLevelProgram()
155191
{
156192
// NOTE: Seems like a Roslyn bug made diagnostics be reported twice. Fixed in a later version.
@@ -176,5 +212,17 @@ protected virtual DiagnosticResult[] GetExpectedResultTestGlobalStatementAndReco
176212
Diagnostic().WithLocation(0),
177213
};
178214
}
215+
216+
protected virtual DiagnosticResult[] GetExpectedResultTopLevelStatementsFollowedByType()
217+
{
218+
// NOTE: Seems like a Roslyn bug made diagnostics be reported twice. Fixed in a later version.
219+
return new[]
220+
{
221+
Diagnostic().WithLocation(0),
222+
Diagnostic().WithLocation(0),
223+
Diagnostic().WithLocation(1),
224+
Diagnostic().WithLocation(1),
225+
};
226+
}
179227
}
180228
}

StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/OrderingRules/SA1200CSharp9UnitTests.cs

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
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.CSharp9.OrderingRules
75
{
6+
using System;
87
using System.Threading;
98
using System.Threading.Tasks;
109
using Microsoft.CodeAnalysis;
11-
using Microsoft.CodeAnalysis.Testing;
10+
using StyleCop.Analyzers.Settings.ObjectModel;
1211
using StyleCop.Analyzers.Test.CSharp8.OrderingRules;
1312
using Xunit;
1413
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
@@ -17,28 +16,65 @@ namespace StyleCop.Analyzers.Test.CSharp9.OrderingRules
1716

1817
public partial class SA1200CSharp9UnitTests : SA1200CSharp8UnitTests
1918
{
19+
private const string UsingDirectivesPlacementOutsideNamespace = @"{
20+
""settings"": {
21+
""orderingRules"": {
22+
""usingDirectivesPlacement"": ""outsideNamespace""
23+
}
24+
}
25+
}";
26+
27+
private const string UsingDirectivesPlacementInsideNamespace = @"{
28+
""settings"": {
29+
""orderingRules"": {
30+
""usingDirectivesPlacement"": ""insideNamespace""
31+
}
32+
}
33+
}";
34+
35+
private const string UsingDirectivesPlacementPreserve = @"{
36+
""settings"": {
37+
""orderingRules"": {
38+
""usingDirectivesPlacement"": ""preserve""
39+
}
40+
}
41+
}";
42+
2043
/// <summary>
2144
/// Verifies that having using statements in the compilation unit will not produce diagnostics for top-level
2245
/// programs.
2346
/// </summary>
47+
/// <param name="placement">The using directives placement configuration to test.</param>
2448
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
25-
[Fact]
49+
[Theory]
2650
[WorkItem(3243, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3243")]
27-
public async Task TestValidUsingStatementsInTopLevelProgramAsync()
51+
[WorkItem(3967, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3967")]
52+
[InlineData(UsingDirectivesPlacement.OutsideNamespace)]
53+
[InlineData(UsingDirectivesPlacement.InsideNamespace)]
54+
[InlineData(UsingDirectivesPlacement.Preserve)]
55+
public async Task TestValidUsingStatementsInTopLevelProgramAsync(object placement)
2856
{
2957
var testCode = @"using System;
3058
using System.Threading;
3159
3260
return 0;
3361
";
3462

63+
var file = (UsingDirectivesPlacement)placement switch
64+
{
65+
UsingDirectivesPlacement.InsideNamespace => UsingDirectivesPlacementInsideNamespace,
66+
UsingDirectivesPlacement.OutsideNamespace => UsingDirectivesPlacementOutsideNamespace,
67+
UsingDirectivesPlacement.Preserve => UsingDirectivesPlacementPreserve,
68+
_ => throw new NotImplementedException(),
69+
};
70+
3571
await new CSharpTest()
3672
{
37-
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
3873
TestState =
3974
{
4075
OutputKind = OutputKind.ConsoleApplication,
4176
Sources = { testCode },
77+
AdditionalFiles = { ("stylecop.json", file) },
4278
},
4379
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
4480
}

documentation/SA1200.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ A C# using directive is placed outside of a namespace element.
2525

2626
A violation of this rule occurs when a using directive or a using-alias directive is placed outside of a namespace element, unless the file does not contain any namespace elements.
2727

28+
> :memo: For C# 9 top-level statements, all `using` directives must appear before any executable code. When a file contains top-level statements (and therefore no namespace declaration), using directives at the top of the file are considered correct regardless of the `usingDirectivesPlacement` configuration value.
29+
2830
> :memo: Global using directives (C# 10) are always outside namespaces, and the compiler produces an error if they are placed inside. SA1200 does not report diagnostics for the placement of global using directives.
2931
3032
For example, the following code would result in two violations of this rule.

0 commit comments

Comments
 (0)