提交 e09eaada 编写于 作者: A Abraham Hosch

Implement fix for CS1573

上级 ad754eac
......@@ -204,6 +204,7 @@
<Compile Include="Diagnostics\UseImplicitOrExplicitType\UseExplicitTypeTests_FixAllTests.cs" />
<Compile Include="Diagnostics\UseImplicitOrExplicitType\UseImplicitTypeTests_FixAllTests.cs" />
<Compile Include="Diagnostics\UseImplicitOrExplicitType\UseImplicitTypeTests.cs" />
<Compile Include="DocumentationComments\CodeFixes\AddDocCommentParamNodeCodeFixProviderTests.cs" />
<Compile Include="DocumentationComments\CodeFixes\RemoveDocCommentNodeCodeFixProviderTests.cs" />
<Compile Include="GoToAdjacentMember\CSharpGoToAdjacentMemberTests.cs" />
<Compile Include="Diagnostics\AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.DiagnosticComments.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.DocumentationComments.CodeFixes
{
public class AddDocCommentParamNodeCodeFixProviderTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override Tuple<DiagnosticAnalyzer, CodeFixProvider> CreateDiagnosticProviderAndFixer(Workspace workspace)
{
return new Tuple<DiagnosticAnalyzer, CodeFixProvider>(null, new CSharpAddDocCommentParamNodeCodeFixProvider());
}
private async Task TestAsync(string initial, string expected)
{
var parseOptions = Options.Regular.WithDocumentationMode(DocumentationMode.Diagnose);
await TestAsync(initial, expected, parseOptions: parseOptions, compareTokens: false);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_NoNodesBefore()
{
var initial =
@"class Program
{
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
public void Fizz(int [|i|], int j, int k) {}
}
";
var expected =
@"class Program
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_NoNodesAfter()
{
var initial =
@"class Program
{
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
public void Fizz(int i, int j, int [|k|]) {}
}
";
var expected =
@"class Program
{
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_NodesBeforeAndAfter()
{
var initial =
@"class Program
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""k""></param>
public void Fizz(int i, int [|j|], int k) {}
}
";
var expected =
@"class Program
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_NestedInSummaryTag()
{
var initial =
@"class Program
{
/// <summary>
/// <param name=""j""></param>
/// </summary>
public void Fizz(int i, int j, int [|k|]) {}
}
";
var expected =
@"class Program
{
/// <summary>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// </summary>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_BeforeNode_EverythingOnOneLine()
{
var initial =
@"class Program
{
/// <summary></summary> <param name=""j""></param>
public void Fizz(int [|i|], int j, int k) {}
}
";
var expected =
@"class Program
{
/// <summary></summary>
/// <param name=""i""></param> <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_AfterNode_EverythingOnOneLine()
{
var initial =
@"class Program
{
/// <summary></summary> <param name=""j""></param>
public void Fizz(int i, int j, int [|k|]) {}
}
";
var expected =
@"class Program
{
/// <summary></summary> <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_BeforeNode_JustParamNode()
{
var initial =
@"class Program
{
/// <param name=""j""></param>
public void Fizz(int [|i|], int j, int k) {}
}
";
var expected =
@"class Program
{
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
public async Task AddsParamTag_AfterNode_JustParamNode()
{
var initial =
@"class Program
{
/// <param name=""j""></param>
public void Fizz(int i, int j, int [|k|]) {}
}
";
var expected =
@"class Program
{
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}
";
await TestAsync(initial, expected);
}
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInDocument_MultipleParamNodesInVariousPlaces()
{
var initial =
@"class Program1
{
/// <summary>
/// <param name=""i""></param>
/// </summary>
/// <param name=""k""></param>
public void Fizz(int i, int {|FixAllInDocument:j|}, int k, int l) {}
}";
var expected =
@"class Program1
{
/// <summary>
/// <param name=""i""></param>
/// </summary>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// <param name=""l""></param>
public void Fizz(int i, int j, int k, int l) {}
}";
await TestAsync(initial, expected);
}
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInDocument()
{
var initial = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program1
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, {|FixAllInDocument:int k|}) {}
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// <returns></returns>
public int Buzz(int i, int j, int k) { returns 0; }
}]]>
</Document>
<Document>
<![CDATA[
class Program2
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program3
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program1
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// <returns></returns>
public int Buzz(int i, int j, int k) { returns 0; }
}]]>
</Document>
<Document>
<![CDATA[
class Program2
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program3
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
</Workspace>";
await TestAsync(initial, expected);
}
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInProject()
{
var initial = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program1
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, {|FixAllInProject:int k|}) {}
}]]>
</Document>
<Document>
<![CDATA[
class Program2
{
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// <returns></returns>
public int Buzz(int i, int j, int k) { returns 0; }
}]]>
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program3
{
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// <returns></returns>
public int Buzz(int i, int j, int k) { returns 0; }
}]]>
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
<![CDATA[
class Program1
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
<Document>
<![CDATA[
class Program2
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// <returns></returns>
public int Buzz(int i, int j, int k) { returns 0; }
}]]>
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"">
<Document>
<![CDATA[
class Program3
{
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
/// <param name=""k""></param>
/// <returns></returns>
public int Buzz(int i, int j, int k) { returns 0; }
}]]>
</Document>
</Project>
</Workspace>";
await TestAsync(initial, expected);
}
[Fact]
[Trait(Traits.Feature, Traits.Features.CodeActionsAddDocCommentParamNode)]
[Trait(Traits.Feature, Traits.Features.CodeActionsFixAllOccurrences)]
public async Task TestFixAllInSolution()
{
var initial = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program1
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, {|FixAllInSolution:int k|}) {}
}]]>
</Document>
<Document>
<![CDATA[
class Program2
{
/// <summary>
///
/// </summary>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program3
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
</Workspace>";
var expected = @"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program1
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
<Document>
<![CDATA[
class Program2
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
<Project Language=""C#"" AssemblyName=""Assembly2"" CommonReferences=""true"" DocumentationMode=""Diagnose"">
<Document>
<![CDATA[
class Program3
{
/// <summary>
///
/// </summary>
/// <param name=""i""></param>
/// <param name=""j""></param>
/// <param name=""k""></param>
public void Fizz(int i, int j, int k) {}
}]]>
</Document>
</Project>
</Workspace>";
await TestAsync(initial, expected);
}
}
}
......@@ -28,6 +28,7 @@ public static class Features
public const string Classification = nameof(Classification);
public const string ClassView = nameof(ClassView);
public const string CodeActionsAddConstructorParameters = "CodeActions.AddConstructorParameters";
public const string CodeActionsAddDocCommentParamNode = "CodeActions.AddDocCommentParamNode";
public const string CodeActionsAddAsync = "CodeActions.AddAsync";
public const string CodeActionsAddAwait = "CodeActions.AddAwait";
public const string CodeActionsAddBraces = "CodeActions.AddBraces";
......
......@@ -271,6 +271,7 @@
<Compile Include="Diagnostics\Analyzers\CSharpUseExplicitTypeDiagnosticAnalyzer.cs" />
<Compile Include="Diagnostics\Analyzers\CSharpUseImplicitTypeDiagnosticAnalyzer.cs" />
<Compile Include="Diagnostics\CSharpAnalyzerDriverService.cs" />
<Compile Include="DocumentationComments\CodeFixes\CSharpAddDocCommentParamNodeCodeFixProvider.cs" />
<Compile Include="DocumentationComments\CodeFixes\CSharpRemoveDocCommentNodeCodeFixProvider.cs" />
<Compile Include="DocumentationComments\CSharpDocumentationCommentFormattingService.cs" />
<Compile Include="DocumentationComments\DocumentationCommentUtilities.cs" />
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
namespace Microsoft.CodeAnalysis.DiagnosticComments.CodeFixes
{
[ExportCodeFixProvider(LanguageNames.CSharp, Name = PredefinedCodeFixProviderNames.AddAttribute), Shared]
[ExtensionOrder(After = PredefinedCodeFixProviderNames.ImplementInterface)]
internal class CSharpAddDocCommentParamNodeCodeFixProvider
: AbstractAddDocCommentParamNodeCodeFixProvider<XmlElementSyntax, XmlNameAttributeSyntax, ParameterSyntax, MethodDeclarationSyntax, XmlTextSyntax>
{
/// <summary>
/// Parameter has no matching param tag in XML comment
/// </summary>
private const string CS1573 = nameof(CS1573);
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(CS1573);
protected override List<XmlNameAttributeSyntax> GetNameAttributes(XmlElementSyntax node)
=> node.StartTag.Attributes.OfType<XmlNameAttributeSyntax>().ToList();
protected override string GetValueFromNameAttribute(XmlNameAttributeSyntax attribute)
=> attribute.Identifier.Identifier.ValueText;
protected override SyntaxNode GetDocCommentNode(SyntaxTriviaList leadingTrivia)
=> leadingTrivia.FirstOrDefault(f => f.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia)).GetStructure();
protected override string GetXmlElementLocalName(XmlElementSyntax element)
=> element.StartTag.Name.LocalName.ValueText;
protected override List<string> GetParameterNames(MethodDeclarationSyntax method)
=> method.ParameterList.Parameters.Select(s => s.Identifier.ValueText).ToList();
protected override string GetParameterName(ParameterSyntax parameter)
=> parameter.Identifier.ValueText;
protected override XmlElementSyntax GetNewNode(string parameterName, bool isFirstNodeInComment)
{
var newDocCommentNode = SyntaxFactory.DocumentationComment(SyntaxFactory.XmlParamElement(parameterName));
XmlElementSyntax elementNode = (XmlElementSyntax)newDocCommentNode.ChildNodes().ElementAt(0);
// return node on new line
if (!isFirstNodeInComment)
{
return elementNode.WithLeadingTrivia(SyntaxFactory.ParseLeadingTrivia(Environment.NewLine).AddRange(elementNode.GetLeadingTrivia().Select(s => s)));
}
else
{
return elementNode.WithTrailingTrivia(SyntaxFactory.ParseTrailingTrivia(Environment.NewLine));
}
}
}
}
\ No newline at end of file
......@@ -4,6 +4,7 @@ namespace Microsoft.CodeAnalysis.CodeFixes
{
internal static class PredefinedCodeFixProviderNames
{
public const string AddAttribute = "Add Attribute";
public const string AddAwait = "Add Await For Expression";
public const string AddAsync = "Add Async To Member";
public const string ApplyNamingStyle = "Apply Naming Style";
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.DiagnosticComments.CodeFixes
{
internal abstract class AbstractAddDocCommentParamNodeCodeFixProvider
<TXmlElementSyntax, TXmlNameAttributeSyntax, TParameterSyntax, TMethodDeclarationSyntax, TXmlTextSyntax> : CodeFixProvider
where TXmlElementSyntax : SyntaxNode
where TXmlNameAttributeSyntax : SyntaxNode
where TParameterSyntax : SyntaxNode
where TMethodDeclarationSyntax : SyntaxNode
where TXmlTextSyntax : SyntaxNode
{
public override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
context.RegisterCodeFix(
new MyCodeAction(
c => AddParamTagAsync(context.Document, context.Span, c)),
context.Diagnostics);
return SpecializedTasks.EmptyTask;
}
protected abstract List<TXmlNameAttributeSyntax> GetNameAttributes(TXmlElementSyntax node);
protected abstract string GetValueFromNameAttribute(TXmlNameAttributeSyntax attribute);
protected abstract SyntaxNode GetDocCommentNode(SyntaxTriviaList leadingTrivia);
protected abstract string GetXmlElementLocalName(TXmlElementSyntax element);
protected abstract List<string> GetParameterNames(TMethodDeclarationSyntax method);
protected abstract string GetParameterName(TParameterSyntax parameter);
protected abstract TXmlElementSyntax GetNewNode(string parameterName, bool isFirstNodeInComment);
protected async Task<Document> AddParamTagAsync(
Document document, TextSpan span, CancellationToken cancellationToken)
{
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var parameter = root.FindNode(span) as TParameterSyntax;
var parentMethod = parameter.FirstAncestorOrSelf<TMethodDeclarationSyntax>();
if (parentMethod == null)
{
// todo bug
throw new Exception();
}
var docCommentNode = GetDocCommentNode(parentMethod.GetLeadingTrivia());
var docCommentChildNodes = docCommentNode.ChildNodes().ToList();
var paramNodes = docCommentChildNodes.OfType<TXmlElementSyntax>()
.Where(w => GetXmlElementLocalName(w) == "param");
// Prefer to place the doc comment in the outer level, as in auto-created comments
if (!paramNodes.Any())
{
paramNodes = docCommentNode.DescendantNodes(descendIntoChildren: _ => true)
.OfType<TXmlElementSyntax>()
.Where(w => GetXmlElementLocalName(w) == "param");
}
var methodParamNames = GetParameterNames(parentMethod);
var paramsBeforeDiagnosticParam = methodParamNames.TakeWhile(t => t != GetParameterName(parameter)).ToList();
var paramsAfterDiagnosticParam = methodParamNames.Except(paramsBeforeDiagnosticParam).ToList();
paramsAfterDiagnosticParam.Remove(GetParameterName(parameter));
SyntaxNode nodeBeforeNewParamNode = null;
SyntaxNode nodeAfterNewParamNode = null;
if (paramsBeforeDiagnosticParam.Any())
{
nodeBeforeNewParamNode = GetLastParamNodeCorrespondingToParamInList(paramNodes, paramsBeforeDiagnosticParam);
}
if (paramsAfterDiagnosticParam.Any())
{
nodeAfterNewParamNode = GetLastParamNodeCorrespondingToParamInList(paramNodes.Reverse(), paramsAfterDiagnosticParam);
var indexOfNode = docCommentChildNodes.IndexOf(nodeAfterNewParamNode);
// set insert node to be the doc comment signifier of the closest param before the new node
for (var i = indexOfNode - 1; i >= 0; i--)
{
if (docCommentChildNodes[i] is TXmlTextSyntax)
{
nodeAfterNewParamNode = docCommentChildNodes[i];
break;
}
}
}
// No `param` nodes either before or after the current node
// This will currently never be hit as CS1573 only fires if there is a `param` node in the documentation
// and there is no VB equivalent yet
if (paramsBeforeDiagnosticParam == null &&
paramsAfterDiagnosticParam == null)
{
// First try to insert the node after the `summary` node,
// then just insert it at the top of the comment
var summaryNode = docCommentChildNodes.OfType<TXmlElementSyntax>().FirstOrDefault(f => GetXmlElementLocalName(f) == "summary");
if (summaryNode != null)
{
nodeBeforeNewParamNode = summaryNode;
}
else
{
nodeAfterNewParamNode = docCommentChildNodes.First();
}
}
// this must always have a value because doc comments always have the doc comment token
var newNodeList = new SyntaxNode[]
{
GetNewNode(GetParameterName(parameter),
isFirstNodeInComment: docCommentChildNodes.First() == nodeAfterNewParamNode)
};
SyntaxNode newDocComment = null;
if (nodeBeforeNewParamNode != null)
{
newDocComment = docCommentNode.InsertNodesAfter(nodeBeforeNewParamNode, newNodeList);
}
else
{
// there were no `param` nodes above the new one
newDocComment = docCommentNode.InsertNodesBefore(nodeAfterNewParamNode, newNodeList);
}
var newRoot = root.ReplaceNode(docCommentNode, newDocComment.WithAdditionalAnnotations(Formatter.Annotation));
return document.WithSyntaxRoot(newRoot);
}
protected TXmlElementSyntax GetLastParamNodeCorrespondingToParamInList(IEnumerable<TXmlElementSyntax> paramNodeList, List<string> methodParamSubset)
{
TXmlElementSyntax nodeAfterNewParamNode = null;
foreach (var paramNode in paramNodeList)
{
var paramNameForNode = GetNameAttributes(paramNode);
// param node is missing `name` attribute or there are multiple `name` attributes
// there is currently no test for this scenario because CS1573 does not fire if there are malformed nodes
if (paramNameForNode.Count != 1)
{
continue;
}
if (methodParamSubset.Contains(GetValueFromNameAttribute(paramNameForNode.Single())))
{
nodeAfterNewParamNode = paramNode;
}
}
return nodeAfterNewParamNode;
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(FeaturesResources.Add_attribute, createChangedDocument)
{
}
}
}
}
\ No newline at end of file
......@@ -224,6 +224,7 @@
<Compile Include="Diagnostics\AbstractHostDiagnosticUpdateSource.cs" />
<Compile Include="Diagnostics\Analyzers\QualifyMemberAccessDiagnosticAnalyzerBase.cs" />
<Compile Include="Diagnostics\Analyzers\NamingStyles\Capitalization.cs" />
<Compile Include="DocumentationComments\CodeFixes\AbstractAddDocCommentParamNodeCodeFixProvider.cs" />
<Compile Include="DocumentationComments\CodeFixes\AbstractRemoveDocCommentNodeCodeFixProvider.cs" />
<Compile Include="FindReferences\DefinitionItem.cs" />
<Compile Include="FindReferences\DefinitionItem.DocumentLocationDefinitionItem.cs" />
......
......@@ -107,6 +107,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Add attribute.
/// </summary>
internal static string Add_attribute {
get {
return ResourceManager.GetString("Add_attribute", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Add both.
/// </summary>
......
......@@ -1046,4 +1046,7 @@ This version used in: {2}</value>
<data name="Remove_tag" xml:space="preserve">
<value>Remove tag</value>
</data>
<data name="Add_attribute" xml:space="preserve">
<value>Add attribute</value>
</data>
</root>
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册