diff --git a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/LayoutRules/SA1516CSharp9UnitTests.cs b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/LayoutRules/SA1516CSharp9UnitTests.cs index d7caa71f5..9b8b94dd9 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/LayoutRules/SA1516CSharp9UnitTests.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers.Test.CSharp9/LayoutRules/SA1516CSharp9UnitTests.cs @@ -3,9 +3,60 @@ namespace StyleCop.Analyzers.Test.CSharp9.LayoutRules { + using System.Threading; + using System.Threading.Tasks; + using Microsoft.CodeAnalysis; + using Microsoft.CodeAnalysis.CSharp; + using Microsoft.CodeAnalysis.Testing; using StyleCop.Analyzers.Test.CSharp8.LayoutRules; + using Xunit; + using static StyleCop.Analyzers.Test.Verifiers.StyleCopCodeFixVerifier< + StyleCop.Analyzers.LayoutRules.SA1516ElementsMustBeSeparatedByBlankLine, + StyleCop.Analyzers.LayoutRules.SA1516CodeFixProvider>; public class SA1516CSharp9UnitTests : SA1516CSharp8UnitTests { + /// + /// Verifies that SA1516 is reported at the correct location in top-level programs. + /// + /// A representing the asynchronous unit test. + [Fact] + [WorkItem(3242, "https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/3242")] + public async Task TestStatementSpacingInTopLevelProgramAsync() + { + var testCode = @"using System; +using System.Threading; +{|#0:return|} 0; +"; + var fixedCode = @"using System; +using System.Threading; + +return 0; +"; + + await new CSharpTest(LanguageVersion.CSharp9) + { + ReferenceAssemblies = ReferenceAssemblies.Net.Net50, + TestCode = testCode, + ExpectedDiagnostics = + { + // /0/Test0.cs(3,1): warning SA1516: Elements should be separated by blank line + Diagnostic().WithLocation(0), + + // /0/Test0.cs(3,1): warning SA1516: Elements should be separated by blank line + Diagnostic().WithLocation(0), + }, + FixedCode = fixedCode, + SolutionTransforms = + { + (solution, projectId) => + { + var project = solution.GetProject(projectId); + var options = project.CompilationOptions; + return solution.WithProjectCompilationOptions(projectId, options.WithOutputKind(OutputKind.ConsoleApplication)); + }, + }, + }.RunAsync(CancellationToken.None).ConfigureAwait(false); + } } } diff --git a/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1516ElementsMustBeSeparatedByBlankLine.cs b/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1516ElementsMustBeSeparatedByBlankLine.cs index a0dea7a2c..7ee38b5f3 100644 --- a/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1516ElementsMustBeSeparatedByBlankLine.cs +++ b/StyleCop.Analyzers/StyleCop.Analyzers/LayoutRules/SA1516ElementsMustBeSeparatedByBlankLine.cs @@ -366,10 +366,16 @@ private static Location GetDiagnosticLocation(SyntaxNode node) return node.GetLeadingTrivia()[0].GetLocation(); } + // Prefer the first token which is a direct child, but fall back to the first descendant token var firstToken = node.ChildTokens().FirstOrDefault(); + if (firstToken.IsKind(SyntaxKind.None)) + { + firstToken = node.GetFirstToken(); + } + if (firstToken != default) { - return node.ChildTokens().First().GetLocation(); + return firstToken.GetLocation(); } return Location.None;