forked from DotNetAnalyzers/DocumentationAnalyzers
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement DOC103 (Use XML Documentation Syntax)
Closes DotNetAnalyzers#2 Closes DotNetAnalyzers#3 Closes DotNetAnalyzers#4
- Loading branch information
Showing
9 changed files
with
489 additions
and
0 deletions.
There are no files selected for viewing
118 changes: 118 additions & 0 deletions
118
DocumentationAnalyzers/DocumentationAnalyzers.CodeFixes/StyleRules/DOC103CodeFixProvider.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,118 @@ | ||
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. | ||
// Licensed under the MIT license. See LICENSE in the project root for license information. | ||
|
||
namespace DocumentationAnalyzers.StyleRules | ||
{ | ||
using System.Collections.Immutable; | ||
using System.Composition; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using DocumentationAnalyzers.Helpers; | ||
using Microsoft.CodeAnalysis; | ||
using Microsoft.CodeAnalysis.CodeActions; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.CSharp; | ||
using Microsoft.CodeAnalysis.CSharp.Syntax; | ||
|
||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC103CodeFixProvider))] | ||
[Shared] | ||
internal class DOC103CodeFixProvider : CodeFixProvider | ||
{ | ||
public override ImmutableArray<string> FixableDiagnosticIds { get; } | ||
= ImmutableArray.Create(DOC103UseXmlDocumentationSyntax.DiagnosticId); | ||
|
||
public override FixAllProvider GetFixAllProvider() | ||
=> CustomFixAllProviders.BatchFixer; | ||
|
||
public override Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
foreach (var diagnostic in context.Diagnostics) | ||
{ | ||
if (!FixableDiagnosticIds.Contains(diagnostic.Id)) | ||
{ | ||
continue; | ||
} | ||
|
||
context.RegisterCodeFix( | ||
CodeAction.Create( | ||
StyleResources.BlockLevelDocumentationCodeFix, | ||
token => GetTransformedDocumentAsync(context.Document, diagnostic, token), | ||
nameof(DOC103CodeFixProvider)), | ||
diagnostic); | ||
} | ||
|
||
return SpecializedTasks.CompletedTask; | ||
} | ||
|
||
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken) | ||
{ | ||
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true); | ||
|
||
var xmlElement = token.Parent.FirstAncestorOrSelf<XmlElementSyntax>(); | ||
var oldStartToken = xmlElement.StartTag.Name.LocalName; | ||
|
||
string newIdentifier; | ||
switch (oldStartToken.ValueText) | ||
{ | ||
case "p": | ||
newIdentifier = XmlCommentHelper.ParaXmlTag; | ||
break; | ||
|
||
case "tt": | ||
newIdentifier = XmlCommentHelper.CXmlTag; | ||
break; | ||
|
||
case "pre": | ||
newIdentifier = XmlCommentHelper.CodeXmlTag; | ||
break; | ||
|
||
case "ul": | ||
case "ol": | ||
newIdentifier = XmlCommentHelper.ListXmlTag; | ||
break; | ||
|
||
default: | ||
// Not handled | ||
return document; | ||
} | ||
|
||
var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, newIdentifier, oldStartToken.TrailingTrivia); | ||
var newXmlElement = xmlElement.ReplaceToken(oldStartToken, newStartToken); | ||
|
||
var oldEndToken = newXmlElement.EndTag.Name.LocalName; | ||
var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, newIdentifier, oldEndToken.TrailingTrivia); | ||
newXmlElement = newXmlElement.ReplaceToken(oldEndToken, newEndToken); | ||
|
||
if (newIdentifier == XmlCommentHelper.ListXmlTag) | ||
{ | ||
// Add an attribute for the list kind | ||
string listType = oldStartToken.ValueText == "ol" ? "number" : "bullet"; | ||
newXmlElement = newXmlElement.WithStartTag(newXmlElement.StartTag.AddAttributes(XmlSyntaxFactory.TextAttribute(XmlCommentHelper.TypeAttributeName, listType))); | ||
|
||
// Replace each <li>...</li> element with <item><description>...</description></item> | ||
for (int i = 0; i < newXmlElement.Content.Count; i++) | ||
{ | ||
if (newXmlElement.Content[i] is XmlElementSyntax childXmlElement | ||
&& childXmlElement.StartTag?.Name?.LocalName.ValueText == "li" | ||
&& childXmlElement.StartTag.Name.Prefix == null) | ||
{ | ||
oldStartToken = childXmlElement.StartTag.Name.LocalName; | ||
newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, XmlCommentHelper.ItemXmlTag, oldStartToken.TrailingTrivia); | ||
var newChildXmlElement = childXmlElement.ReplaceToken(oldStartToken, newStartToken); | ||
|
||
oldEndToken = newChildXmlElement.EndTag.Name.LocalName; | ||
newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, XmlCommentHelper.ItemXmlTag, oldEndToken.TrailingTrivia); | ||
newChildXmlElement = newChildXmlElement.ReplaceToken(oldEndToken, newEndToken); | ||
|
||
newChildXmlElement = newChildXmlElement.WithContent(XmlSyntaxFactory.List(XmlSyntaxFactory.Element(XmlCommentHelper.DescriptionXmlTag, newChildXmlElement.Content))); | ||
|
||
newXmlElement = newXmlElement.ReplaceNode(childXmlElement, newChildXmlElement); | ||
} | ||
} | ||
} | ||
|
||
return document.WithSyntaxRoot(root.ReplaceNode(xmlElement, newXmlElement)); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...ntationAnalyzers/DocumentationAnalyzers.Test.CSharp7/StyleRules/DOC103CSharp7UnitTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. | ||
// Licensed under the MIT license. See LICENSE in the project root for license information. | ||
|
||
namespace DocumentationAnalyzers.Test.CSharp7.StyleRules | ||
{ | ||
using DocumentationAnalyzers.Test.StyleRules; | ||
|
||
public class DOC103CSharp7UnitTests : DOC103UnitTests | ||
{ | ||
} | ||
} |
201 changes: 201 additions & 0 deletions
201
DocumentationAnalyzers/DocumentationAnalyzers.Test/StyleRules/DOC103UnitTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved. | ||
// Licensed under the MIT license. See LICENSE in the project root for license information. | ||
|
||
namespace DocumentationAnalyzers.Test.StyleRules | ||
{ | ||
using System.Collections.Generic; | ||
using System.Threading; | ||
using System.Threading.Tasks; | ||
using DocumentationAnalyzers.StyleRules; | ||
using Microsoft.CodeAnalysis.CodeFixes; | ||
using Microsoft.CodeAnalysis.Diagnostics; | ||
using Microsoft.CodeAnalysis.Testing; | ||
using Xunit; | ||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier<DocumentationAnalyzers.StyleRules.DOC103UseXmlDocumentationSyntax, DocumentationAnalyzers.StyleRules.DOC103CodeFixProvider, Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier>; | ||
|
||
/// <summary> | ||
/// This class contains unit tests for <see cref="DOC103UseXmlDocumentationSyntax"/>. | ||
/// </summary> | ||
public class DOC103UnitTests | ||
{ | ||
[Fact] | ||
public async Task TestHtmlParagraphAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <[|p|]>This is a paragraph.</p> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
var fixedCode = @" | ||
/// <remarks> | ||
/// <para>This is a paragraph.</para> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlParagraphWithAttributeAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <[|p|] attr=""value"">This is a paragraph.</p> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
var fixedCode = @" | ||
/// <remarks> | ||
/// <para attr=""value"">This is a paragraph.</para> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestPrefixAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <not:p>This is a paragraph.</not:p> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyAnalyzerAsync(testCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlCodeAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <para>This is <[|tt|]>code</tt>.</para> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
var fixedCode = @" | ||
/// <remarks> | ||
/// <para>This is <c>code</c>.</para> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlCodeBlockAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <para>This is a code block:</para> | ||
/// <[|pre|]> | ||
/// code goes here | ||
/// more code here | ||
/// </pre> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
var fixedCode = @" | ||
/// <remarks> | ||
/// <para>This is a code block:</para> | ||
/// <code> | ||
/// code goes here | ||
/// more code here | ||
/// </code> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlOrderedListAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <para>This is an ordered list:</para> | ||
/// <[|ol|]> | ||
/// <li>Item 1</li> | ||
/// <li>Item 2</li> | ||
/// </ol> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
var fixedCode = @" | ||
/// <remarks> | ||
/// <para>This is an ordered list:</para> | ||
/// <list type=""number""> | ||
/// <item><description>Item 1</description></item> | ||
/// <item><description>Item 2</description></item> | ||
/// </list> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlUnorderedListAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <para>This is an ordered list:</para> | ||
/// <[|ul|]> | ||
/// <li>Item 1</li> | ||
/// <li>Item 2</li> | ||
/// </ul> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
var fixedCode = @" | ||
/// <remarks> | ||
/// <para>This is an ordered list:</para> | ||
/// <list type=""bullet""> | ||
/// <item><description>Item 1</description></item> | ||
/// <item><description>Item 2</description></item> | ||
/// </list> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlUnorderedListMultilineItemAsync() | ||
{ | ||
var testCode = @" | ||
/// <remarks> | ||
/// <para>This is an ordered list:</para> | ||
/// <[|ul|]> | ||
/// <li> | ||
/// Item 1 | ||
/// </li> | ||
/// </ul> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
var fixedCode = @" | ||
/// <remarks> | ||
/// <para>This is an ordered list:</para> | ||
/// <list type=""bullet""> | ||
/// <item><description> | ||
/// Item 1 | ||
/// </description></item> | ||
/// </list> | ||
/// </remarks> | ||
class TestClass { } | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.