Skip to content

Commit

Permalink
Merge pull request #1277 from vweijsters/issue-1271
Browse files Browse the repository at this point in the history
  • Loading branch information
sharwell committed Aug 25, 2015
2 parents 5686d67 + 4c6d39b commit a4427f0
Show file tree
Hide file tree
Showing 10 changed files with 460 additions and 90 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
namespace StyleCop.Analyzers.Test.DocumentationRules
{
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Analyzers.DocumentationRules;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using TestHelper;
using Xunit;

/// <summary>
/// Unit tests for file header that do not follow the XML syntax.
/// </summary>
public class NoXmlFileHeaderUnitTests : DiagnosticVerifier
{
private const string SettingsFileName = "stylecop.json";
private const string TestSettings = @"
{
""settings"": {
""documentationRules"": {
""companyName"": ""FooCorp"",
""copyrightText"": ""Copyright (c) {companyName}. All rights reserved.\nLicensed under the {licenseName} license. See {licenseFile} file in the project root for full license information."",
""variables"": {
""licenseName"": ""???"",
""licenseFile"": ""LICENSE"",
},
""xmlHeader"": false
}
}
}
";

/// <summary>
/// Verifies that a file header with an auto-generated comment will not produce a diagnostic message.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
public async Task TestAutoGeneratedSourceFileAsync()
{
var testCode = @"// <auto-generated/>
namespace Bar
{
}
";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

/// <summary>
/// Verifies that a valid file header built using single line comments will not produce a diagnostic message.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
public async Task TestValidFileHeaderWithSingleLineCommentsAsync()
{
var testCode = @"// Copyright (c) FooCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
namespace Bar
{
}
";

await this.VerifyCSharpDiagnosticAsync(testCode, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

/// <summary>
/// Verifies that a valid file header built using multi line comments will not produce a diagnostic message.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
public async Task TestValidFileHeaderWithMultiLineCommentsAsync()
{
var testCodeFormat1 = @"/* Copyright (c) FooCorp. All rights reserved.
* Licensed under the ??? license. See LICENSE file in the project root for full license information.
*/
namespace Bar
{
}
";

var testCodeFormat2 = @"/* Copyright (c) FooCorp. All rights reserved.
Licensed under the ??? license. See LICENSE file in the project root for full license information. */
namespace Bar
{
}
";

await this.VerifyCSharpDiagnosticAsync(testCodeFormat1, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(testCodeFormat2, EmptyDiagnosticResults, CancellationToken.None).ConfigureAwait(false);
}

/// <summary>
/// Verifies that a file header without text / only whitespace will produce the expected diagnostic message.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
public async Task TestInvalidFileHeaderWithoutTextAsync()
{
var testCodeFormat1 = @"//
namespace Bar
{
}
";

var testCodeFormat2 = "// " + @"
namespace Bar
{
}
";

var expected = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1635Descriptor).WithLocation(1, 1);
await this.VerifyCSharpDiagnosticAsync(testCodeFormat1, expected, CancellationToken.None).ConfigureAwait(false);
await this.VerifyCSharpDiagnosticAsync(testCodeFormat2, expected, CancellationToken.None).ConfigureAwait(false);
}

/// <summary>
/// Verifies that an invalid file header built using single line comments will produce the expected diagnostic message.
/// </summary>
/// <returns>A <see cref="Task"/> representing the asynchronous unit test.</returns>
[Fact]
public async Task TestInvalidFileHeaderWithWrongTextAsync()
{
var testCode = @"// Copyright (c) BarCorp. All rights reserved.
// Licensed under the ??? license. See LICENSE file in the project root for full license information.
namespace Bar
{
}
";
var expected = this.CSharpDiagnostic(FileHeaderAnalyzers.SA1636Descriptor).WithLocation(1, 1);
await this.VerifyCSharpDiagnosticAsync(testCode, expected, CancellationToken.None).ConfigureAwait(false);
}

/// <inheritdoc/>
protected override Solution CreateSolution(ProjectId projectId, string language)
{
var solution = base.CreateSolution(projectId, language);

var documentId = DocumentId.CreateNewId(projectId);
return solution
.AddAdditionalDocument(documentId, SettingsFileName, TestSettings);
}

/// <inheritdoc/>
protected sealed override IEnumerable<DiagnosticAnalyzer> GetCSharpDiagnosticAnalyzers()
{
yield return new FileHeaderAnalyzers();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="DocumentationRules\FileHeaderTestBase.cs" />
<Compile Include="DocumentationRules\NoXmlFileHeaderUnitTests.cs" />
<Compile Include="DocumentationRules\SA1600UnitTests.cs" />
<Compile Include="DocumentationRules\SA1601UnitTests.cs" />
<Compile Include="DocumentationRules\SA1602UnitTests.cs" />
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -181,13 +181,13 @@
<value>File header file name documentation must match file name</value>
</data>
<data name="SA1639Description" xml:space="preserve">
<value />
<value>The file header at the top of a C# code file does not contain a filled-in summary tag.</value>
</data>
<data name="SA1639MessageFormat" xml:space="preserve">
<value />
<value>File header must have summary</value>
</data>
<data name="SA1639Title" xml:space="preserve">
<value />
<value>File header must have summary</value>
</data>
<data name="SA1640Description" xml:space="preserve">
<value>The file header at the top of a C# code file does not contain company name text.</value>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace StyleCop.Analyzers.DocumentationRules
{
using System;
using System.Collections.Immutable;
using System.IO;
using System.Xml.Linq;
Expand Down Expand Up @@ -183,38 +184,72 @@ private static void HandleCompilationStart(CompilationStartAnalysisContext conte
private static void HandleSyntaxTreeAxtion(SyntaxTreeAnalysisContext context, Compilation compilation)
{
var root = context.Tree.GetRoot(context.CancellationToken);
var settings = context.GetStyleCopSettings();

// don't process empty files
if (root.FullSpan.IsEmpty)
{
return;
}

var fileHeader = FileHeaderHelpers.ParseFileHeader(root);
if (fileHeader.IsMissing)
{
context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMissing, fileHeader.GetLocation(context.Tree)));
return;
}

if (fileHeader.IsMalformed)
{
context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMalformed, fileHeader.GetLocation(context.Tree)));
return;
}

if (!compilation.IsAnalyzerSuppressed(SA1634Identifier))
if (settings.DocumentationRules.XmlHeader)
{
CheckCopyrightHeader(context, compilation, fileHeader);
var fileHeader = FileHeaderHelpers.ParseXmlFileHeader(root);
if (fileHeader.IsMissing)
{
context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMissing, fileHeader.GetLocation(context.Tree)));
return;
}

if (fileHeader.IsMalformed)
{
context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMalformed, fileHeader.GetLocation(context.Tree)));
return;
}

if (!compilation.IsAnalyzerSuppressed(SA1634Identifier))
{
CheckCopyrightHeader(context, compilation, settings, fileHeader);
}

if (!compilation.IsAnalyzerSuppressed(SA1639Identifier))
{
CheckSummaryHeader(context, compilation, fileHeader);
}
}

if (!compilation.IsAnalyzerSuppressed(SA1639Identifier))
else
{
CheckSummaryHeader(context, compilation, fileHeader);
var fileHeader = FileHeaderHelpers.ParseFileHeader(root);
if (fileHeader.IsMissing)
{
context.ReportDiagnostic(Diagnostic.Create(SA1633DescriptorMissing, fileHeader.GetLocation(context.Tree)));
return;
}

if (!compilation.IsAnalyzerSuppressed(SA1635Identifier))
{
if (string.IsNullOrWhiteSpace(fileHeader.CopyrightText))
{
context.ReportDiagnostic(Diagnostic.Create(SA1635Descriptor, fileHeader.GetLocation(context.Tree)));
return;
}

if (compilation.IsAnalyzerSuppressed(SA1636Identifier))
{
return;
}

// make sure that both \n and \r\n are accepted from the settings.
var reformattedCopyrightText = settings.DocumentationRules.CopyrightText.Replace("\r\n", "\n").Replace("\n", Environment.NewLine);
if (string.CompareOrdinal(fileHeader.CopyrightText, reformattedCopyrightText) != 0)
{
context.ReportDiagnostic(Diagnostic.Create(SA1636Descriptor, fileHeader.GetLocation(context.Tree)));
}
}
}
}

private static void CheckCopyrightHeader(SyntaxTreeAnalysisContext context, Compilation compilation, FileHeader fileHeader)
private static void CheckCopyrightHeader(SyntaxTreeAnalysisContext context, Compilation compilation, StyleCopSettings settings, XmlFileHeader fileHeader)
{
var copyrightElement = fileHeader.GetElement("copyright");
if (copyrightElement == null)
Expand All @@ -223,8 +258,6 @@ private static void CheckCopyrightHeader(SyntaxTreeAnalysisContext context, Comp
return;
}

var settings = context.GetStyleCopSettings();

if (!compilation.IsAnalyzerSuppressed(SA1637Identifier))
{
CheckFile(context, compilation, fileHeader, copyrightElement, settings);
Expand All @@ -241,7 +274,7 @@ private static void CheckCopyrightHeader(SyntaxTreeAnalysisContext context, Comp
}
}

private static void CheckFile(SyntaxTreeAnalysisContext context, Compilation compilation, FileHeader fileHeader, XElement copyrightElement, StyleCopSettings settings)
private static void CheckFile(SyntaxTreeAnalysisContext context, Compilation compilation, XmlFileHeader fileHeader, XElement copyrightElement, StyleCopSettings settings)
{
var fileAttribute = copyrightElement.Attribute("file");
if (fileAttribute == null)
Expand All @@ -264,7 +297,7 @@ private static void CheckFile(SyntaxTreeAnalysisContext context, Compilation com
}
}

private static void CheckCopyrightText(SyntaxTreeAnalysisContext context, Compilation compilation, FileHeader fileHeader, XElement copyrightElement, StyleCopSettings settings)
private static void CheckCopyrightText(SyntaxTreeAnalysisContext context, Compilation compilation, XmlFileHeader fileHeader, XElement copyrightElement, StyleCopSettings settings)
{
var copyrightText = copyrightElement.Value;
if (string.IsNullOrWhiteSpace(copyrightText))
Expand All @@ -286,7 +319,7 @@ private static void CheckCopyrightText(SyntaxTreeAnalysisContext context, Compil
}
}

private static void CheckCompanyName(SyntaxTreeAnalysisContext context, Compilation compilation, FileHeader fileHeader, XElement copyrightElement, StyleCopSettings settings)
private static void CheckCompanyName(SyntaxTreeAnalysisContext context, Compilation compilation, XmlFileHeader fileHeader, XElement copyrightElement, StyleCopSettings settings)
{
var companyName = copyrightElement.Attribute("company")?.Value;
if (string.IsNullOrWhiteSpace(companyName))
Expand All @@ -308,7 +341,7 @@ private static void CheckCompanyName(SyntaxTreeAnalysisContext context, Compilat
}
}

private static void CheckSummaryHeader(SyntaxTreeAnalysisContext context, Compilation compilation, FileHeader fileHeader)
private static void CheckSummaryHeader(SyntaxTreeAnalysisContext context, Compilation compilation, XmlFileHeader fileHeader)
{
var summaryElement = fileHeader.GetElement("summary");
if (summaryElement == null)
Expand Down
Loading

0 comments on commit a4427f0

Please sign in to comment.