-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #57 from sharwell/use-unicode
Implement DOC103 (Use Unicode Characters)
- Loading branch information
Showing
8 changed files
with
508 additions
and
0 deletions.
There are no files selected for viewing
77 changes: 77 additions & 0 deletions
77
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,77 @@ | ||
// 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.Diagnostics; | ||
using System.Net; | ||
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; | ||
|
||
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC103CodeFixProvider))] | ||
[Shared] | ||
internal class DOC103CodeFixProvider : CodeFixProvider | ||
{ | ||
private const string CS1570 = nameof(CS1570); | ||
|
||
public override ImmutableArray<string> FixableDiagnosticIds { get; } | ||
= ImmutableArray.Create(DOC103UseUnicodeCharacters.DiagnosticId, CS1570); | ||
|
||
public override FixAllProvider GetFixAllProvider() | ||
=> CustomFixAllProviders.BatchFixer; | ||
|
||
public override async Task RegisterCodeFixesAsync(CodeFixContext context) | ||
{ | ||
SyntaxNode root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); | ||
|
||
foreach (var diagnostic in context.Diagnostics) | ||
{ | ||
Debug.Assert(FixableDiagnosticIds.Contains(diagnostic.Id), "Assertion failed: FixableDiagnosticIds.Contains(diagnostic.Id)"); | ||
|
||
SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true); | ||
if (!token.IsKind(SyntaxKind.XmlEntityLiteralToken)) | ||
{ | ||
// Could be an unrelated CS1570 error. | ||
return; | ||
} | ||
|
||
string newText = token.ValueText; | ||
if (newText == token.Text) | ||
{ | ||
// The entity is not recognized. Try decoding as an HTML entity. | ||
newText = WebUtility.HtmlDecode(token.Text); | ||
} | ||
|
||
if (newText == token.Text) | ||
{ | ||
// Unknown entity | ||
continue; | ||
} | ||
|
||
context.RegisterCodeFix( | ||
CodeAction.Create( | ||
StyleResources.DOC103CodeFix, | ||
cancellationToken => GetTransformedDocumentAsync(context.Document, diagnostic, newText, cancellationToken), | ||
nameof(DOC103CodeFixProvider)), | ||
diagnostic); | ||
} | ||
} | ||
|
||
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, string newText, CancellationToken cancellationToken) | ||
{ | ||
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); | ||
SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true); | ||
|
||
var newToken = SyntaxFactory.Token(token.LeadingTrivia, SyntaxKind.XmlTextLiteralToken, newText, newText, token.TrailingTrivia); | ||
|
||
return document.WithSyntaxRoot(root.ReplaceToken(token, newToken)); | ||
} | ||
} | ||
} |
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 | ||
{ | ||
} | ||
} |
243 changes: 243 additions & 0 deletions
243
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,243 @@ | ||
// 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.Threading.Tasks; | ||
using DocumentationAnalyzers.StyleRules; | ||
using Microsoft.CodeAnalysis.CSharp.Testing; | ||
using Microsoft.CodeAnalysis.Testing; | ||
using Microsoft.CodeAnalysis.Testing.Verifiers; | ||
using Xunit; | ||
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier<DocumentationAnalyzers.StyleRules.DOC103UseUnicodeCharacters, DocumentationAnalyzers.StyleRules.DOC103CodeFixProvider, Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier>; | ||
|
||
public class DOC103UnitTests | ||
{ | ||
[Fact] | ||
public async Task TestApostropheReplacementAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// Don[|'|]t use <![CDATA[']]> this <element attr=""'"" attr2='''/>. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = @" | ||
/// <summary> | ||
/// Don't use <![CDATA[']]> this <element attr=""'"" attr2='''/>. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestApostropheReplacementByNumberAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// Don[|'|]t use <![CDATA[']]> this <element attr=""'"" attr2='''/>. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = @" | ||
/// <summary> | ||
/// Don't use <![CDATA[']]> this <element attr=""'"" attr2='''/>. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestQuoteReplacementAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// Don[|"|]t use <![CDATA["]]> this <element attr=""""" attr2='"'/>. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = @" | ||
/// <summary> | ||
/// Don""t use <![CDATA["]]> this <element attr=""""" attr2='"'/>. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlEntityReplacementAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// From A→B. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = @" | ||
/// <summary> | ||
/// From A→B. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
|
||
await new CSharpCodeFixTest<DOC103UseUnicodeCharacters, DOC103CodeFixProvider, XUnitVerifier> | ||
{ | ||
TestCode = testCode, | ||
ExpectedDiagnostics = { DiagnosticResult.CompilerWarning("CS1570").WithSpan(3, 11, 3, 11).WithMessage("XML comment has badly formed XML -- 'Reference to undefined entity 'rarr'.'") }, | ||
FixedCode = fixedCode, | ||
CompilerDiagnostics = CompilerDiagnostics.Warnings, | ||
}.RunAsync(); | ||
} | ||
|
||
[Fact] | ||
public async Task TestUnknownEntityNotReplacedAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// Unknown entity &myEntity;. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = testCode; | ||
|
||
await new CSharpCodeFixTest<DOC103UseUnicodeCharacters, DOC103CodeFixProvider, XUnitVerifier> | ||
{ | ||
TestState = | ||
{ | ||
Sources = { testCode }, | ||
ExpectedDiagnostics = { DiagnosticResult.CompilerWarning("CS1570").WithSpan(3, 20, 3, 20).WithMessage("XML comment has badly formed XML -- 'Reference to undefined entity 'myEntity'.'") }, | ||
}, | ||
FixedState = | ||
{ | ||
Sources = { fixedCode }, | ||
InheritanceMode = StateInheritanceMode.AutoInheritAll, | ||
}, | ||
CompilerDiagnostics = CompilerDiagnostics.Warnings, | ||
}.RunAsync(); | ||
} | ||
|
||
[Fact] | ||
public async Task TestHtmlEntityReplacementInInvalidXmlAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// From A→B. | ||
/// <p> | ||
/// An unterminated second paragraph... | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = @" | ||
/// <summary> | ||
/// From A→B. | ||
/// <p> | ||
/// An unterminated second paragraph... | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
|
||
await new CSharpCodeFixTest<DOC103UseUnicodeCharacters, DOC103CodeFixProvider, XUnitVerifier> | ||
{ | ||
TestState = | ||
{ | ||
Sources = { testCode }, | ||
ExpectedDiagnostics = | ||
{ | ||
DiagnosticResult.CompilerWarning("CS1570").WithSpan(3, 11, 3, 11).WithMessage("XML comment has badly formed XML -- 'Reference to undefined entity 'rarr'.'"), | ||
DiagnosticResult.CompilerWarning("CS1570").WithSpan(6, 7, 6, 14).WithMessage("XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'p'.'"), | ||
DiagnosticResult.CompilerWarning("CS1570").WithSpan(7, 1, 7, 1).WithMessage("XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'"), | ||
}, | ||
}, | ||
FixedState = | ||
{ | ||
Sources = { fixedCode }, | ||
ExpectedDiagnostics = | ||
{ | ||
DiagnosticResult.CompilerWarning("CS1570").WithSpan(6, 7, 6, 14).WithMessage("XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'p'.'"), | ||
DiagnosticResult.CompilerWarning("CS1570").WithSpan(7, 1, 7, 1).WithMessage("XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'"), | ||
}, | ||
}, | ||
CompilerDiagnostics = CompilerDiagnostics.Warnings, | ||
}.RunAsync(); | ||
} | ||
|
||
[Fact] | ||
public async Task TestNoCodeFixForRequiredEntityAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// Processing for <c><code></c> elements. | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = testCode; | ||
|
||
await Verify.VerifyCodeFixAsync(testCode, fixedCode); | ||
} | ||
|
||
[Fact] | ||
public async Task TestNoCodeFixForInvalidXmlAsync() | ||
{ | ||
var testCode = @" | ||
/// <summary> | ||
/// From A to B. | ||
/// <p> | ||
/// An unterminated second paragraph... | ||
/// </summary> | ||
class TestClass | ||
{ | ||
} | ||
"; | ||
var fixedCode = testCode; | ||
|
||
await new CSharpCodeFixTest<DOC103UseUnicodeCharacters, DOC103CodeFixProvider, XUnitVerifier> | ||
{ | ||
TestState = | ||
{ | ||
Sources = { testCode }, | ||
ExpectedDiagnostics = | ||
{ | ||
DiagnosticResult.CompilerWarning("CS1570").WithSpan(6, 7, 6, 14).WithMessage("XML comment has badly formed XML -- 'End tag 'summary' does not match the start tag 'p'.'"), | ||
DiagnosticResult.CompilerWarning("CS1570").WithSpan(7, 1, 7, 1).WithMessage("XML comment has badly formed XML -- 'Expected an end tag for element 'summary'.'"), | ||
}, | ||
}, | ||
FixedState = | ||
{ | ||
Sources = { fixedCode }, | ||
InheritanceMode = StateInheritanceMode.AutoInheritAll, | ||
}, | ||
CompilerDiagnostics = CompilerDiagnostics.Warnings, | ||
}.RunAsync(); | ||
} | ||
} | ||
} |
Oops, something went wrong.