Skip to content

Commit bdf9932

Browse files
authored
Merge pull request #44 from sharwell/inline-code
Implement element usage analyzers
2 parents 500d699 + 125ee0a commit bdf9932

File tree

18 files changed

+1219
-0
lines changed

18 files changed

+1219
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT license. See LICENSE in the project root for license information.
3+
4+
namespace DocumentationAnalyzers.PortabilityRules
5+
{
6+
using System.Collections.Immutable;
7+
using System.Composition;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using DocumentationAnalyzers.Helpers;
11+
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.CodeActions;
13+
using Microsoft.CodeAnalysis.CodeFixes;
14+
using Microsoft.CodeAnalysis.CSharp;
15+
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
17+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC202CodeFixProvider))]
18+
[Shared]
19+
internal class DOC202CodeFixProvider : CodeFixProvider
20+
{
21+
public override ImmutableArray<string> FixableDiagnosticIds { get; }
22+
= ImmutableArray.Create(DOC202UseSectionElementsCorrectly.DiagnosticId);
23+
24+
public override FixAllProvider GetFixAllProvider()
25+
=> CustomFixAllProviders.BatchFixer;
26+
27+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
28+
{
29+
foreach (var diagnostic in context.Diagnostics)
30+
{
31+
if (!FixableDiagnosticIds.Contains(diagnostic.Id))
32+
{
33+
continue;
34+
}
35+
36+
context.RegisterCodeFix(
37+
CodeAction.Create(
38+
PortabilityResources.DOC202CodeFix,
39+
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
40+
nameof(DOC202CodeFixProvider)),
41+
diagnostic);
42+
}
43+
44+
return SpecializedTasks.CompletedTask;
45+
}
46+
47+
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
48+
{
49+
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
50+
SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true);
51+
52+
var xmlNode = token.Parent.FirstAncestorOrSelf<XmlNodeSyntax>();
53+
var oldStartToken = xmlNode.GetName().LocalName;
54+
55+
string newIdentifier;
56+
switch (oldStartToken.ValueText)
57+
{
58+
case XmlCommentHelper.ParamXmlTag:
59+
newIdentifier = XmlCommentHelper.ParamRefXmlTag;
60+
break;
61+
62+
case XmlCommentHelper.TypeParamXmlTag:
63+
newIdentifier = XmlCommentHelper.TypeParamRefXmlTag;
64+
break;
65+
66+
default:
67+
// Not handled
68+
return document;
69+
}
70+
71+
var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, newIdentifier, oldStartToken.TrailingTrivia);
72+
var newXmlNode = xmlNode.ReplaceToken(oldStartToken, newStartToken);
73+
74+
if (newXmlNode is XmlElementSyntax newXmlElement)
75+
{
76+
var oldEndToken = newXmlElement.EndTag.Name.LocalName;
77+
var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, newIdentifier, oldEndToken.TrailingTrivia);
78+
newXmlNode = newXmlNode.ReplaceToken(oldEndToken, newEndToken);
79+
}
80+
81+
return document.WithSyntaxRoot(root.ReplaceNode(xmlNode, newXmlNode));
82+
}
83+
}
84+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT license. See LICENSE in the project root for license information.
3+
4+
namespace DocumentationAnalyzers.PortabilityRules
5+
{
6+
using System.Collections.Immutable;
7+
using System.Composition;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using DocumentationAnalyzers.Helpers;
11+
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.CodeActions;
13+
using Microsoft.CodeAnalysis.CodeFixes;
14+
using Microsoft.CodeAnalysis.CSharp;
15+
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
17+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC203CodeFixProvider))]
18+
[Shared]
19+
internal class DOC203CodeFixProvider : CodeFixProvider
20+
{
21+
public override ImmutableArray<string> FixableDiagnosticIds { get; }
22+
= ImmutableArray.Create(DOC203UseBlockElementsCorrectly.DiagnosticId);
23+
24+
public override FixAllProvider GetFixAllProvider()
25+
=> CustomFixAllProviders.BatchFixer;
26+
27+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
28+
{
29+
foreach (var diagnostic in context.Diagnostics)
30+
{
31+
if (!FixableDiagnosticIds.Contains(diagnostic.Id))
32+
{
33+
continue;
34+
}
35+
36+
context.RegisterCodeFix(
37+
CodeAction.Create(
38+
PortabilityResources.DOC203CodeFix,
39+
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
40+
nameof(DOC203CodeFixProvider)),
41+
diagnostic);
42+
}
43+
44+
return SpecializedTasks.CompletedTask;
45+
}
46+
47+
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
48+
{
49+
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
50+
SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true);
51+
52+
var xmlNode = token.Parent.FirstAncestorOrSelf<XmlNodeSyntax>();
53+
var oldStartToken = xmlNode.GetName().LocalName;
54+
55+
string newIdentifier;
56+
switch (oldStartToken.ValueText)
57+
{
58+
case XmlCommentHelper.CodeXmlTag:
59+
newIdentifier = XmlCommentHelper.CXmlTag;
60+
break;
61+
62+
default:
63+
// Not handled
64+
return document;
65+
}
66+
67+
var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, newIdentifier, oldStartToken.TrailingTrivia);
68+
var newXmlNode = xmlNode.ReplaceToken(oldStartToken, newStartToken);
69+
70+
if (newXmlNode is XmlElementSyntax newXmlElement)
71+
{
72+
var oldEndToken = newXmlElement.EndTag.Name.LocalName;
73+
var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, newIdentifier, oldEndToken.TrailingTrivia);
74+
newXmlNode = newXmlNode.ReplaceToken(oldEndToken, newEndToken);
75+
}
76+
77+
return document.WithSyntaxRoot(root.ReplaceNode(xmlNode, newXmlNode));
78+
}
79+
}
80+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT license. See LICENSE in the project root for license information.
3+
4+
namespace DocumentationAnalyzers.PortabilityRules
5+
{
6+
using System.Collections.Immutable;
7+
using System.Composition;
8+
using System.Threading;
9+
using System.Threading.Tasks;
10+
using DocumentationAnalyzers.Helpers;
11+
using Microsoft.CodeAnalysis;
12+
using Microsoft.CodeAnalysis.CodeActions;
13+
using Microsoft.CodeAnalysis.CodeFixes;
14+
using Microsoft.CodeAnalysis.CSharp;
15+
using Microsoft.CodeAnalysis.CSharp.Syntax;
16+
17+
[ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(DOC204CodeFixProvider))]
18+
[Shared]
19+
internal class DOC204CodeFixProvider : CodeFixProvider
20+
{
21+
public override ImmutableArray<string> FixableDiagnosticIds { get; }
22+
= ImmutableArray.Create(DOC204UseInlineElementsCorrectly.DiagnosticId);
23+
24+
public override FixAllProvider GetFixAllProvider()
25+
=> CustomFixAllProviders.BatchFixer;
26+
27+
public override Task RegisterCodeFixesAsync(CodeFixContext context)
28+
{
29+
foreach (var diagnostic in context.Diagnostics)
30+
{
31+
if (!FixableDiagnosticIds.Contains(diagnostic.Id))
32+
{
33+
continue;
34+
}
35+
36+
context.RegisterCodeFix(
37+
CodeAction.Create(
38+
PortabilityResources.DOC204CodeFix,
39+
token => GetTransformedDocumentAsync(context.Document, diagnostic, token),
40+
nameof(DOC204CodeFixProvider)),
41+
diagnostic);
42+
}
43+
44+
return SpecializedTasks.CompletedTask;
45+
}
46+
47+
private static async Task<Document> GetTransformedDocumentAsync(Document document, Diagnostic diagnostic, CancellationToken cancellationToken)
48+
{
49+
SyntaxNode root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
50+
SyntaxToken token = root.FindToken(diagnostic.Location.SourceSpan.Start, findInsideTrivia: true);
51+
52+
var xmlNode = token.Parent.FirstAncestorOrSelf<XmlNodeSyntax>();
53+
var oldStartToken = xmlNode.GetName().LocalName;
54+
55+
string newIdentifier;
56+
switch (oldStartToken.ValueText)
57+
{
58+
case XmlCommentHelper.ParamRefXmlTag:
59+
newIdentifier = XmlCommentHelper.ParamXmlTag;
60+
break;
61+
62+
case XmlCommentHelper.TypeParamRefXmlTag:
63+
newIdentifier = XmlCommentHelper.TypeParamXmlTag;
64+
break;
65+
66+
default:
67+
// Not handled
68+
return document;
69+
}
70+
71+
var newStartToken = SyntaxFactory.Identifier(oldStartToken.LeadingTrivia, newIdentifier, oldStartToken.TrailingTrivia);
72+
var newXmlNode = xmlNode.ReplaceToken(oldStartToken, newStartToken);
73+
74+
if (newXmlNode is XmlElementSyntax newXmlElement)
75+
{
76+
var oldEndToken = newXmlElement.EndTag.Name.LocalName;
77+
var newEndToken = SyntaxFactory.Identifier(oldEndToken.LeadingTrivia, newIdentifier, oldEndToken.TrailingTrivia);
78+
newXmlNode = newXmlNode.ReplaceToken(oldEndToken, newEndToken);
79+
}
80+
81+
return document.WithSyntaxRoot(root.ReplaceNode(xmlNode, newXmlNode));
82+
}
83+
}
84+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT license. See LICENSE in the project root for license information.
3+
4+
namespace DocumentationAnalyzers.Test.CSharp7.PortabilityRules
5+
{
6+
using DocumentationAnalyzers.Test.PortabilityRules;
7+
8+
public class DOC202CSharp7UnitTests : DOC202UnitTests
9+
{
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT license. See LICENSE in the project root for license information.
3+
4+
namespace DocumentationAnalyzers.Test.CSharp7.PortabilityRules
5+
{
6+
using DocumentationAnalyzers.Test.PortabilityRules;
7+
8+
public class DOC203CSharp7UnitTests : DOC203UnitTests
9+
{
10+
}
11+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT license. See LICENSE in the project root for license information.
3+
4+
namespace DocumentationAnalyzers.Test.CSharp7.PortabilityRules
5+
{
6+
using DocumentationAnalyzers.Test.PortabilityRules;
7+
8+
public class DOC204CSharp7UnitTests : DOC204UnitTests
9+
{
10+
}
11+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// Copyright (c) Tunnel Vision Laboratories, LLC. All Rights Reserved.
2+
// Licensed under the MIT license. See LICENSE in the project root for license information.
3+
4+
namespace DocumentationAnalyzers.Test.PortabilityRules
5+
{
6+
using System.Threading.Tasks;
7+
using Xunit;
8+
using Verify = Microsoft.CodeAnalysis.CSharp.Testing.CSharpCodeFixVerifier<DocumentationAnalyzers.PortabilityRules.DOC202UseSectionElementsCorrectly, DocumentationAnalyzers.PortabilityRules.DOC202CodeFixProvider, Microsoft.CodeAnalysis.Testing.Verifiers.XUnitVerifier>;
9+
10+
public class DOC202UnitTests
11+
{
12+
[Fact]
13+
public async Task TestElementsUsedCorrectlyAsync()
14+
{
15+
var testCode = @"
16+
class TestClass
17+
{
18+
/// <summary>
19+
/// Pass in a value.
20+
/// </summary>
21+
/// <typeparam name=""T"">The type of value</typeparam>
22+
/// <param name=""value"">The value</param>
23+
void Method<T>(int value)
24+
{
25+
}
26+
}
27+
";
28+
29+
await Verify.VerifyAnalyzerAsync(testCode);
30+
}
31+
32+
[Fact]
33+
public async Task TestParamUsedAsParamRefAsync()
34+
{
35+
var testCode = @"
36+
class TestClass
37+
{
38+
/// <summary>
39+
/// Pass in a <$$param name=""value""/>.
40+
/// </summary>
41+
void Method(int value)
42+
{
43+
}
44+
}
45+
";
46+
var fixedCode = @"
47+
class TestClass
48+
{
49+
/// <summary>
50+
/// Pass in a <paramref name=""value""/>.
51+
/// </summary>
52+
void Method(int value)
53+
{
54+
}
55+
}
56+
";
57+
58+
await Verify.VerifyCodeFixAsync(testCode, fixedCode);
59+
}
60+
61+
[Fact]
62+
public async Task TestTypeParamUsedAsTypeParamRefAsync()
63+
{
64+
var testCode = @"
65+
class TestClass
66+
{
67+
/// <summary>
68+
/// Pass in a <$$typeparam name=""T""/>.
69+
/// </summary>
70+
void Method<T>()
71+
{
72+
}
73+
}
74+
";
75+
var fixedCode = @"
76+
class TestClass
77+
{
78+
/// <summary>
79+
/// Pass in a <typeparamref name=""T""/>.
80+
/// </summary>
81+
void Method<T>()
82+
{
83+
}
84+
}
85+
";
86+
87+
await Verify.VerifyCodeFixAsync(testCode, fixedCode);
88+
}
89+
}
90+
}

0 commit comments

Comments
 (0)