Skip to content

Commit

Permalink
Merge pull request #44 from sharwell/inline-code
Browse files Browse the repository at this point in the history
Implement element usage analyzers
  • Loading branch information
sharwell authored Sep 20, 2018
2 parents 500d699 + 125ee0a commit bdf9932
Show file tree
Hide file tree
Showing 18 changed files with 1,219 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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.PortabilityRules
{
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(DOC202CodeFixProvider))]
[Shared]
internal class DOC202CodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds { get; }
= ImmutableArray.Create(DOC202UseSectionElementsCorrectly.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(
PortabilityResources.DOC202CodeFix,
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
nameof(DOC202CodeFixProvider)),
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 xmlNode = token.Parent.FirstAncestorOrSelf<XmlNodeSyntax>();
var oldStartToken = xmlNode.GetName().LocalName;

string newIdentifier;
switch (oldStartToken.ValueText)
{
case XmlCommentHelper.ParamXmlTag:
newIdentifier = XmlCommentHelper.ParamRefXmlTag;
break;

case XmlCommentHelper.TypeParamXmlTag:
newIdentifier = XmlCommentHelper.TypeParamRefXmlTag;
break;

default:
// Not handled
return document;
}

var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, newIdentifier, oldStartToken.TrailingTrivia);
var newXmlNode = xmlNode.ReplaceToken(oldStartToken, newStartToken);

if (newXmlNode is XmlElementSyntax newXmlElement)
{
var oldEndToken = newXmlElement.EndTag.Name.LocalName;
var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, newIdentifier, oldEndToken.TrailingTrivia);
newXmlNode = newXmlNode.ReplaceToken(oldEndToken, newEndToken);
}

return document.WithSyntaxRoot(root.ReplaceNode(xmlNode, newXmlNode));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// 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.PortabilityRules
{
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(DOC203CodeFixProvider))]
[Shared]
internal class DOC203CodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds { get; }
= ImmutableArray.Create(DOC203UseBlockElementsCorrectly.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(
PortabilityResources.DOC203CodeFix,
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
nameof(DOC203CodeFixProvider)),
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 xmlNode = token.Parent.FirstAncestorOrSelf<XmlNodeSyntax>();
var oldStartToken = xmlNode.GetName().LocalName;

string newIdentifier;
switch (oldStartToken.ValueText)
{
case XmlCommentHelper.CodeXmlTag:
newIdentifier = XmlCommentHelper.CXmlTag;
break;

default:
// Not handled
return document;
}

var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, newIdentifier, oldStartToken.TrailingTrivia);
var newXmlNode = xmlNode.ReplaceToken(oldStartToken, newStartToken);

if (newXmlNode is XmlElementSyntax newXmlElement)
{
var oldEndToken = newXmlElement.EndTag.Name.LocalName;
var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, newIdentifier, oldEndToken.TrailingTrivia);
newXmlNode = newXmlNode.ReplaceToken(oldEndToken, newEndToken);
}

return document.WithSyntaxRoot(root.ReplaceNode(xmlNode, newXmlNode));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
// 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.PortabilityRules
{
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(DOC204CodeFixProvider))]
[Shared]
internal class DOC204CodeFixProvider : CodeFixProvider
{
public override ImmutableArray<string> FixableDiagnosticIds { get; }
= ImmutableArray.Create(DOC204UseInlineElementsCorrectly.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(
PortabilityResources.DOC204CodeFix,
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
nameof(DOC204CodeFixProvider)),
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 xmlNode = token.Parent.FirstAncestorOrSelf<XmlNodeSyntax>();
var oldStartToken = xmlNode.GetName().LocalName;

string newIdentifier;
switch (oldStartToken.ValueText)
{
case XmlCommentHelper.ParamRefXmlTag:
newIdentifier = XmlCommentHelper.ParamXmlTag;
break;

case XmlCommentHelper.TypeParamRefXmlTag:
newIdentifier = XmlCommentHelper.TypeParamXmlTag;
break;

default:
// Not handled
return document;
}

var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, newIdentifier, oldStartToken.TrailingTrivia);
var newXmlNode = xmlNode.ReplaceToken(oldStartToken, newStartToken);

if (newXmlNode is XmlElementSyntax newXmlElement)
{
var oldEndToken = newXmlElement.EndTag.Name.LocalName;
var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, newIdentifier, oldEndToken.TrailingTrivia);
newXmlNode = newXmlNode.ReplaceToken(oldEndToken, newEndToken);
}

return document.WithSyntaxRoot(root.ReplaceNode(xmlNode, newXmlNode));
}
}
}
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.PortabilityRules
{
using DocumentationAnalyzers.Test.PortabilityRules;

public class DOC202CSharp7UnitTests : DOC202UnitTests
{
}
}
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.PortabilityRules
{
using DocumentationAnalyzers.Test.PortabilityRules;

public class DOC203CSharp7UnitTests : DOC203UnitTests
{
}
}
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.PortabilityRules
{
using DocumentationAnalyzers.Test.PortabilityRules;

public class DOC204CSharp7UnitTests : DOC204UnitTests
{
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 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.PortabilityRules
{
using System.Threading.Tasks;
using Xunit;
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier<DocumentationAnalyzers.PortabilityRules.DOC202UseSectionElementsCorrectly, DocumentationAnalyzers.PortabilityRules.DOC202CodeFixProvider, Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier>;

public class DOC202UnitTests
{
[Fact]
public async Task TestElementsUsedCorrectlyAsync()
{
var testCode = @"
class TestClass
{
/// <summary>
/// Pass in a value.
/// </summary>
/// <typeparam name=""T"">The type of value</typeparam>
/// <param name=""value"">The value</param>
void Method<T>(int value)
{
}
}
";

await Verify.VerifyAnalyzerAsync(testCode);
}

[Fact]
public async Task TestParamUsedAsParamRefAsync()
{
var testCode = @"
class TestClass
{
/// <summary>
/// Pass in a <$$param name=""value""/>.
/// </summary>
void Method(int value)
{
}
}
";
var fixedCode = @"
class TestClass
{
/// <summary>
/// Pass in a <paramref name=""value""/>.
/// </summary>
void Method(int value)
{
}
}
";

await Verify.VerifyCodeFixAsync(testCode, fixedCode);
}

[Fact]
public async Task TestTypeParamUsedAsTypeParamRefAsync()
{
var testCode = @"
class TestClass
{
/// <summary>
/// Pass in a <$$typeparam name=""T""/>.
/// </summary>
void Method<T>()
{
}
}
";
var fixedCode = @"
class TestClass
{
/// <summary>
/// Pass in a <typeparamref name=""T""/>.
/// </summary>
void Method<T>()
{
}
}
";

await Verify.VerifyCodeFixAsync(testCode, fixedCode);
}
}
}
Loading

0 comments on commit bdf9932

Please sign in to comment.