Skip to content

Commit 7fe5875

Browse files
committed
SA1633 now accepts leading whitespace (incl. end-of-line)
1 parent 6cc07c4 commit 7fe5875

3 files changed

Lines changed: 82 additions & 5 deletions

File tree

StyleCop.Analyzers/StyleCop.Analyzers.Test/DocumentationRules/SA1633UnitTests.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,20 @@ namespace StyleCop.Analyzers.Test.DocumentationRules
1414
/// </summary>
1515
public class SA1633UnitTests : FileHeaderTestBase
1616
{
17+
private const string NoXmlMultiLineHeaderTestSettings = @"
18+
{
19+
""settings"": {
20+
""documentationRules"": {
21+
""companyName"": ""FooCorp"",
22+
""copyrightText"": ""copyright (c) {companyName}. All rights reserved."",
23+
""xmlHeader"": false
24+
}
25+
}
26+
}
27+
";
28+
29+
private bool useNoXmlSettings;
30+
1731
/// <summary>
1832
/// Verifies that the analyzer will report <see cref="FileHeaderAnalyzers.SA1633DescriptorMissing"/> for
1933
/// projects using XML headers (the default) when the file is completely missing a header.
@@ -241,6 +255,54 @@ namespace Foo
241255
await this.VerifyCSharpFixAsync(testCode, fixedCode).ConfigureAwait(false);
242256
}
243257

258+
/// <summary>
259+
/// Verifies that blank lines before a valid XML header will not produce a diagnostic message.
260+
/// This is a regression test for #1781.
261+
/// </summary>
262+
/// <param name="prefix">The string to add before the header.</param>
263+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
264+
[Theory]
265+
[InlineData("\r\n")]
266+
[InlineData("\r\n\r\n")]
267+
public async Task TestValidXmlFileHeaderWithLeadingBlankLinesAsync(string prefix)
268+
{
269+
var testCode = $@"{prefix}// <copyright file=""Test0.cs"" company=""FooCorp"">
270+
// Copyright (c) FooCorp. All rights reserved.
271+
// </copyright>
272+
";
273+
274+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
275+
}
276+
277+
/// <summary>
278+
/// Verifies that blank lines before a valid header will not produce a diagnostic message.
279+
/// This is a regression test for #1781.
280+
/// </summary>
281+
/// <param name="prefix">The string to add before the header.</param>
282+
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
283+
[Theory]
284+
[InlineData("\r\n")]
285+
[InlineData("\r\n\r\n")]
286+
public async Task TestValidFileHeaderWithLeadingBlankLinesAsync(string prefix)
287+
{
288+
this.useNoXmlSettings = true;
289+
var testCode = $@"{prefix}// copyright (c) FooCorp. All rights reserved.
290+
";
291+
292+
await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
293+
}
294+
295+
/// <inheritdoc/>
296+
protected override string GetSettings()
297+
{
298+
if (this.useNoXmlSettings)
299+
{
300+
return NoXmlMultiLineHeaderTestSettings;
301+
}
302+
303+
return base.GetSettings();
304+
}
305+
244306
/// <inheritdoc/>
245307
protected override CodeFixProvider GetCSharpCodeFixProvider()
246308
{

StyleCop.Analyzers/StyleCop.Analyzers/Helpers/FileHeaderHelpers.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,9 @@ internal static class FileHeaderHelpers
2525
internal static FileHeader ParseFileHeader(SyntaxNode root)
2626
{
2727
var firstToken = root.GetFirstToken(includeZeroWidth: true);
28+
var firstNonWhitespaceTrivia = TriviaHelper.IndexOfFirstNonWhitespaceTrivia(firstToken.LeadingTrivia, true);
2829

29-
if (!firstToken.HasLeadingTrivia)
30+
if (firstNonWhitespaceTrivia == -1)
3031
{
3132
return FileHeader.MissingFileHeader;
3233
}
@@ -37,8 +38,7 @@ internal static FileHeader ParseFileHeader(SyntaxNode root)
3738
var fileHeaderStart = int.MaxValue;
3839
var fileHeaderEnd = int.MinValue;
3940

40-
int i;
41-
for (i = 0; !done && (i < firstToken.LeadingTrivia.Count); i++)
41+
for (var i = firstNonWhitespaceTrivia; !done && (i < firstToken.LeadingTrivia.Count); i++)
4242
{
4343
var trivia = firstToken.LeadingTrivia[i];
4444

@@ -119,7 +119,7 @@ internal static XmlFileHeader ParseXmlFileHeader(SyntaxNode root)
119119
int fileHeaderStart;
120120
int fileHeaderEnd;
121121

122-
var firstNonWhitespaceTrivia = TriviaHelper.IndexOfFirstNonWhitespaceTrivia(firstToken.LeadingTrivia, false);
122+
var firstNonWhitespaceTrivia = TriviaHelper.IndexOfFirstNonWhitespaceTrivia(firstToken.LeadingTrivia, true);
123123
if (firstNonWhitespaceTrivia == -1)
124124
{
125125
return XmlFileHeader.MissingFileHeader;

documentation/SA1633.md

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ A C# code file is missing a standard file header.
2121

2222
## Rule description
2323

24-
A violation of this rule occurs when a C# source file is missing a file header. The file header must begin on the first line of the file, and must be formatted as a block of comments containing Xml, as follows:
24+
A violation of this rule occurs when a C# source file is missing a file header.
25+
26+
The file header must begin at the start of the file, and it may only be preceded by whitespace.
27+
28+
The file header must be formatted as a block of comments containing either Xml or preconfigured text, as follows:
2529

2630
```csharp
2731
//-----------------------------------------------------------------------
@@ -31,6 +35,17 @@ A violation of this rule occurs when a C# source file is missing a file header.
3135
//-----------------------------------------------------------------------
3236
```
3337

38+
or
39+
40+
```csharp
41+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
42+
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
43+
```
44+
45+
The type of header to use depends on the setting of the `xmlHeader` property. See the [configuration](https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/Configuration.md) section for details on how to set this up.
46+
47+
### Examples
48+
3449
For example, a file called Widget.cs from a fictional company called Sprocket Enterprises should contain a file header similar to the following:
3550

3651
```csharp

0 commit comments

Comments
 (0)