Skip to content

Commit 61a7218

Browse files
committed
Fix SA1516 reporting location for top-level programs
Fixes #3242
1 parent 363a36c commit 61a7218

File tree

2 files changed

+58
-1
lines changed

2 files changed

+58
-1
lines changed

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

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

44
namespace StyleCop.Analyzers.Test.CSharp9.LayoutRules
55
{
6+
using System.Threading;
7+
using System.Threading.Tasks;
8+
using Microsoft.CodeAnalysis;
9+
using Microsoft.CodeAnalysis.CSharp;
10+
using Microsoft.CodeAnalysis.Testing;
611
using StyleCop.Analyzers.Test.CSharp8.LayoutRules;
12+
using Xunit;
13+
using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier<
14+
StyleCop.Analyzers.LayoutRules.SA1516ElementsMustBeSeparatedByBlankLine,
15+
StyleCop.Analyzers.LayoutRules.SA1516CodeFixProvider>;
716

817
public class SA1516CSharp9UnitTests : SA1516CSharp8UnitTests
918
{
19+
/// <summary>
20+
/// Verifies that SA1516 is reported at the correct location in top-level programs.
21+
/// </summary>
22+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
23+
[Fact]
24+
[WorkItem(3242, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3242")]
25+
public async Task TestStatementSpacingInTopLevelProgramAsync()
26+
{
27+
var testCode = @"using System;
28+
using System.Threading;
29+
{|#0:return|} 0;
30+
";
31+
var fixedCode = @"using System;
32+
using System.Threading;
33+
34+
return 0;
35+
";
36+
37+
await new CSharpTest(LanguageVersion.CSharp9)
38+
{
39+
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
40+
TestCode = testCode,
41+
ExpectedDiagnostics =
42+
{
43+
// /0/Test0.cs(3,1): warning SA1516: Elements should be separated by blank line
44+
Diagnostic().WithLocation(0),
45+
46+
// /0/Test0.cs(3,1): warning SA1516: Elements should be separated by blank line
47+
Diagnostic().WithLocation(0),
48+
},
49+
FixedCode = fixedCode,
50+
SolutionTransforms =
51+
{
52+
(solution, projectId) =>
53+
{
54+
var project = solution.GetProject(projectId);
55+
var options = project.CompilationOptions;
56+
return solution.WithProjectCompilationOptions(projectId, options.WithOutputKind(OutputKind.ConsoleApplication));
57+
},
58+
},
59+
}.RunAsync(CancellationToken.None).ConfigureAwait(false);
60+
}
1061
}
1162
}

StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1516ElementsMustBeSeparatedByBlankLine.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -366,10 +366,16 @@ private static Location GetDiagnosticLocation(SyntaxNode node)
366366
return node.GetLeadingTrivia()[0].GetLocation();
367367
}
368368

369+
// Prefer the first token which is a direct child, but fall back to the first descendant token
369370
var firstToken = node.ChildTokens().FirstOrDefault();
371+
if (firstToken.IsKind(SyntaxKind.None))
372+
{
373+
firstToken = node.GetFirstToken();
374+
}
375+
370376
if (firstToken != default)
371377
{
372-
return node.ChildTokens().First().GetLocation();
378+
return firstToken.GetLocation();
373379
}
374380

375381
return Location.None;

0 commit comments

Comments
 (0)