提交 c49aaf36 编写于 作者: A Alireza Habibi

Add a code fix to add argument names

上级 b68450f5
......@@ -187,6 +187,7 @@
<Compile Include="Classification\TotalClassifierTests.cs" />
<Compile Include="Classification\TotalClassifierTests_Dynamic.cs" />
<Compile Include="CodeActions\AbstractCSharpCodeActionTest.cs" />
<Compile Include="CodeActions\UseNamedArguments\UseNamedArgumentsTests.cs" />
<Compile Include="ConvertToInterpolatedString\ConvertConcatenationToInterpolatedStringTests.cs" />
<Compile Include="ConvertToInterpolatedString\ConvertPlaceholderToInterpolatedStringTests.cs" />
<Compile Include="CodeActions\EncapsulateField\EncapsulateFieldTests.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.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseNamedArguments;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.CodeRefactorings.UseNamedArguments
{
public class UseNamedArgumentsTests : AbstractCSharpCodeActionTest
{
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace)
{
return new CSharpUseNamedArgumentsCodeRefactoringProvider();
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestFirstArgument()
{
await TestAsync(
@"class C { void M(int arg1, int arg2) => M([||]1, 2); }",
@"class C { void M(int arg1, int arg2) => M(arg1: 1, arg2: 2); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestNonFirstArgument()
{
await TestAsync(
@"class C { void M(int arg1, int arg2) => M(1, [||]2); }",
@"class C { void M(int arg1, int arg2) => M(1, arg2: 2); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestDelegate()
{
await TestAsync(
@"class C { void M(System.Action<int> f) => f([||]1); }",
@"class C { void M(System.Action<int> f) => f(obj: 1); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestConditionalMethod()
{
await TestAsync(
@"class C { void M(int arg1, int arg2) => this?.M([||]1, 2); }",
@"class C { void M(int arg1, int arg2) => this?.M(arg1: 1, arg2: 2); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestConditionalIndexer()
{
await TestAsync(
@"class C { int? this[int arg1, int arg2] => this?[[||]1, 2]; }",
@"class C { int? this[int arg1, int arg2] => this?[arg1: 1, arg2: 2]; }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestThisConstructorInitializer()
{
await TestAsync(
@"class C { C(int arg1, int arg2) {} C() : this([||]1, 2) {} }",
@"class C { C(int arg1, int arg2) {} C() : this(arg1: 1, arg2: 2) {} }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestBaseConstructorInitializer()
{
await TestAsync(
@"class C { public C(int arg1, int arg2) {} } class D : C { D() : base([||]1, 2) {} }",
@"class C { public C(int arg1, int arg2) {} } class D : C { D() : base(arg1: 1, arg2: 2) {} }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestConstructor()
{
await TestAsync(
@"class C { C(int arg1, int arg2) { new C([||]1, 2); } }",
@"class C { C(int arg1, int arg2) { new C(arg1: 1, arg2: 2); } }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestIndexer()
{
await TestAsync(
@"class C { char M(string arg1) => arg1[[||]0]; }",
@"class C { char M(string arg1) => arg1[index: 0]; }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestMissingOnArrayIndexer()
{
await TestMissingAsync(
@"class C { int M(int[] arg1) => arg1[[||]0]; }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestMissingOnConditionalArrayIndexer()
{
await TestMissingAsync(
@"class C { int? M(int[] arg1) => arg1?[[||]0]; }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestMissingOnEmptyArgumentList()
{
await TestMissingAsync(
@"class C { void M() => M([||]); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestMissingOnExistingArgumentName()
{
await TestMissingAsync(
@"class C { void M(int arg) => M([||]arg: 1); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestEmptyParams()
{
await TestAsync(
@"class C { void M(int arg1, params int[] arg2) => M([||]1); }",
@"class C { void M(int arg1, params int[] arg2) => M(arg1: 1); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestSingleParams()
{
await TestAsync(
@"class C { void M(int arg1, params int[] arg2) => M([||]1, 2); }",
@"class C { void M(int arg1, params int[] arg2) => M(arg1: 1, arg2: 2); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestNamedParams()
{
await TestAsync(
@"class C { void M(int arg1, params int[] arg2) => M([||]1, arg2: new int[0]); }",
@"class C { void M(int arg1, params int[] arg2) => M(arg1: 1, arg2: new int[0]); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestExistingArgumentNames()
{
await TestAsync(
@"class C { void M(int arg1, int arg2) => M([||]1, arg2: 2); }",
@"class C { void M(int arg1, int arg2) => M(arg1: 1, arg2: 2); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestExistingUnorderedArgumentNames()
{
await TestAsync(
@"class C { void M(int arg1, int arg2, int arg3) => M([||]1, arg3: 3, arg2: 2); }",
@"class C { void M(int arg1, int arg2, int arg3) => M(arg1: 1, arg3: 3, arg2: 2); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestPreserveTrivia()
{
await TestAsync(
@"class C { void M(int arg1, ref int arg2) => M(
[||]1,
ref arg1
); }",
@"class C { void M(int arg1, ref int arg2) => M(
arg1: 1,
arg2: ref arg1
); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestMissingOnNameOf()
{
await TestMissingAsync(
@"class C { string M() => nameof([||]M); }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestAttirbute()
{
await TestAsync(
@"[C([||]1, 2)]
class C : System.Attribute { public C(int arg1, int arg2) {} }",
@"[C(arg1: 1, arg2: 2)]
class C : System.Attribute { public C(int arg1, int arg2) {} }");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)]
public async Task TestAttirbuteWithNamedProperties()
{
await TestAsync(
@"[C([||]1, P = 2)]
class C : System.Attribute { public C(int arg1) {} public int P { get; set; } }",
@"[C(arg1: 1, P = 2)]
class C : System.Attribute { public C(int arg1) {} public int P { get; set; } }");
}
}
}
\ No newline at end of file
......@@ -91,6 +91,7 @@ public static class Features
public const string CodeActionsUseExplicitTupleName = "CodeActions.UseExplicitTupleName";
public const string CodeActionsUseFrameworkType = "CodeActions.UseFrameworkType";
public const string CodeActionsUseNullPropagation = "CodeActions.UseNullPropagation";
public const string CodeActionsUseNamedArguments = "CodeActions.UseNamedArguments";
public const string CodeActionsUseObjectInitializer = "CodeActions.UseObjectInitializer";
public const string CodeActionsUseThrowExpression = "CodeActions.UseThrowExpression";
public const string CodeGeneration = nameof(CodeGeneration);
......
......@@ -184,6 +184,7 @@
<Compile Include="Classification\SemanticClassifierTests.vb" />
<Compile Include="Classification\SyntacticClassifierTests.vb" />
<Compile Include="CodeActions\AbstractVisualBasicCodeActionTest.vb" />
<Compile Include="CodeActions\UseNamedArguments\UseNamedArgumentsTests.vb" />
<Compile Include="Structure\CollectionInitializerStructureProviderTests.vb" />
<Compile Include="ConvertToInterpolatedString\ConvertPlaceholderToInterpolatedStringTests.vb" />
<Compile Include="CodeActions\EncapsulateField\EncapsulateFieldTests.vb" />
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeRefactorings
Imports Microsoft.CodeAnalysis.ReplaceMethodWithProperty
Imports Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.UseNamedArguments
Imports Roslyn.Test.Utilities
Imports Xunit
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.UseNamedArguments
Public Class UseNamedArgumentsTests
Inherits AbstractVisualBasicCodeActionTest
Protected Overrides Function CreateCodeRefactoringProvider(workspace As Workspace) As CodeRefactoringProvider
Return New VisualBasicUseNamedArgumentsCodeRefactoringProvider()
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestFirstArgument() As Task
Await TestAsync(
NewLines("Class C \n Sub M(arg1 As Integer, arg2 As Integer) \n M([||]1, 2) \n End Sub \n End Class"),
NewLines("Class C \n Sub M(arg1 As Integer, arg2 As Integer) \n M(arg1:=1, arg2:=2) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestNonFirstArgument() As Task
Await TestAsync(
NewLines("Class C \n Sub M(arg1 As Integer, arg2 As Integer) \n M(1, [||]2) \n End Sub \n End Class"),
NewLines("Class C \n Sub M(arg1 As Integer, arg2 As Integer) \n M(1, arg2:=2) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestDelegate() As Task
Await TestAsync(
"Class C
Sub M()
Dim f = Sub (arg)
End Sub
f([||]1)
End Sub
End Class",
"Class C
Sub M()
Dim f = Sub (arg)
End Sub
f(arg:=1)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestConditionalDelegate() As Task
Await TestAsync(
"Class C
Sub M()
Dim f = Sub (arg)
End Sub
f?([||]1)
End Sub
End Class",
"Class C
Sub M()
Dim f = Sub (arg)
End Sub
f?(arg:=1)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestConditionalMethod() As Task
Await TestAsync(
NewLines("Class C \n Sub M(arg1 as Integer, arg2 as Integer) \n Me?.M([||]1, 2) \n End Sub \n End Class"),
NewLines("Class C \n Sub M(arg1 as Integer, arg2 as Integer) \n Me?.M(arg1:=1, arg2:=2) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestConditionalIndexer() As Task
Await TestAsync(
"Class C
Sub M(arg1 as String)
Dim r = arg1?([||]0)
End Sub
End Class",
"Class C
Sub M(arg1 as String)
Dim r = arg1?(index:=0)
End Sub
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestConstructor() As Task
Await TestAsync(
NewLines("Class C \n Sub New(arg1 As Integer, arg2 As Integer) \n Dim c = New C([||]1, 2) \n End Sub \n End Class"),
NewLines("Class C \n Sub New(arg1 As Integer, arg2 As Integer) \n Dim c = New C(arg1:=1, arg2:=2) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestIndexer() As Task
Await TestAsync(
"Class C
Function M(arg1 as String) As Char
Return arg1([||]0)
End Function
End Class",
"Class C
Function M(arg1 as String) As Char
Return arg1(index:=0)
End Function
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnArrayIndexer() As Task
Await TestMissingAsync(
"Class C
Function M(arg1 as Integer()) As Integer
Return arg1([||]0)
End Function
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnConditionalArrayIndexer() As Task
Await TestMissingAsync(
"Class C
Function M(arg1 as Integer()) As Integer
Return arg1?([||]0)
End Function
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnEmptyArgumentList() As Task
Await TestMissingAsync(
NewLines("Class C \n Sub M() \n M([||]) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnNamedArgument() As Task
Await TestMissingAsync(
NewLines("Class C \n Sub M(arg as Integer) \n M([||]arg:=1) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnParamArray() As Task
Await TestMissingAsync(
NewLines("Class C \n Sub M(ParamArray arg1 As Integer()) \n M([||]1) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestEmptyParamArray() As Task
Await TestAsync(
NewLines("Class C \n Sub M(arg1 As Integer, ParamArray arg2 As Integer()) \n M([||]1) \n End Sub \n End Class"),
NewLines("Class C \n Sub M(arg1 As Integer, ParamArray arg2 As Integer()) \n M(arg1:=1) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestOmittedArguments() As Task
Await TestAsync(
NewLines("Class C \n Sub M(arg1 As Integer, optional arg2 As Integer=1, optional arg3 as Integer=1) \n M([||]1,,3) \n End Sub \n End Class"),
NewLines("Class C \n Sub M(arg1 As Integer, optional arg2 As Integer=1, optional arg3 as Integer=1) \n M(arg1:=1, arg3:=3) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnOmittedArgument() As Task
Await TestMissingAsync(
NewLines("Class C \n Sub M(optional arg1 As Integer=1, optional arg2 As Integer=1) \n M([||], arg2:=2) \n End Sub \n End Class"))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnNameOf() As Task
Await TestMissingAsync(
"Class C
Function M() As String
Return NameOf([||]M)
End Function
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseNamedArguments)>
Public Async Function TestMissingOnAttribute() As Task
Await TestMissingAsync(
"<C([||]1)>
Class C
Inherits System.Attribute
Public Sub New(arg As Integer)
End Sub
End Class")
End Function
End Class
End Namespace
......@@ -58,6 +58,7 @@
<Link>InternalUtilities\LambdaUtilities.cs</Link>
</Compile>
<Compile Include="AddPackage\CSharpAddSpecificPackageCodeFixProvider.cs" />
<Compile Include="CodeRefactorings\UseNamedArguments\CSharpUseNamedArgumentsCodeRefactoringProvider.cs" />
<Compile Include="ImplementAbstractClass\CSharpImplementAbstractClassCodeFixProvider.cs" />
<Compile Include="ImplementInterface\CSharpImplementInterfaceCodeFixProvider.cs" />
<Compile Include="Structure\Providers\ArrowExpressionClauseStructureProvider.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.Composition;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeRefactorings.UseNamedArguments;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseNamedArguments
{
[ExtensionOrder(After = PredefinedCodeRefactoringProviderNames.IntroduceVariable)]
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = nameof(CSharpUseNamedArgumentsCodeRefactoringProvider)), Shared]
internal class CSharpUseNamedArgumentsCodeRefactoringProvider : AbstractUseNamedArgumentsCodeRefactoringProvider
{
protected override SyntaxNode GetOrSynthesizeNamedArguments(ImmutableArray<IParameterSymbol> parameters, SyntaxNode argumentList, int index)
{
switch (argumentList.Kind())
{
case SyntaxKind.ArgumentList:
{
var node = (ArgumentListSyntax)argumentList;
var namedArguments = node.Arguments
.Select((argument, i) => i < index || argument.NameColon != null
? argument : argument.WithNameColon(SyntaxFactory.NameColon(parameters[i].Name)
.WithTriviaFrom(argument)));
return node.WithArguments(SyntaxFactory.SeparatedList(namedArguments));
}
case SyntaxKind.BracketedArgumentList:
{
var node = (BracketedArgumentListSyntax)argumentList;
var namedArguments = node.Arguments
.Select((argument, i) => i < index || argument.NameColon != null
? argument : argument.WithNameColon(SyntaxFactory.NameColon(parameters[i].Name)
.WithTriviaFrom(argument)));
return node.WithArguments(SyntaxFactory.SeparatedList(namedArguments));
}
case SyntaxKind.AttributeArgumentList:
{
var node = (AttributeArgumentListSyntax)argumentList;
var namedArguments = node.Arguments
.Select((argument, i) => i < index || argument.NameColon != null || argument.NameEquals != null
? argument : argument.WithNameColon(SyntaxFactory.NameColon(parameters[i].Name)
.WithTriviaFrom(argument)));
return node.WithArguments(SyntaxFactory.SeparatedList(namedArguments));
}
default:
return null;
}
}
protected override SyntaxNode GetReceiver(SyntaxNode argument)
{
switch (argument.Parent.Kind())
{
case SyntaxKind.ArgumentList:
case SyntaxKind.BracketedArgumentList:
case SyntaxKind.AttributeArgumentList:
return argument.Parent.Parent;
default:
return null;
}
}
protected override ValueTuple<int, int> GetArgumentListIndexAndCount(SyntaxNode node)
{
switch (node.Parent.Kind())
{
case SyntaxKind.ArgumentList:
case SyntaxKind.BracketedArgumentList:
var argumentListSyntax = (BaseArgumentListSyntax)node.Parent;
return ValueTuple.Create(argumentListSyntax.Arguments.IndexOf((ArgumentSyntax)node), argumentListSyntax.Arguments.Count);
case SyntaxKind.AttributeArgumentList:
var attributeArgumentSyntax = (AttributeArgumentListSyntax)node.Parent;
return ValueTuple.Create(attributeArgumentSyntax.Arguments.IndexOf((AttributeArgumentSyntax)node), attributeArgumentSyntax.Arguments.Count);
default:
return default(ValueTuple<int, int>);
}
}
protected override bool IsCandidate(SyntaxNode node)
{
return node.IsKind(SyntaxKind.Argument)
|| node.IsKind(SyntaxKind.AttributeArgument);
}
protected override bool IsPositionalArgument(SyntaxNode node)
{
switch (node.Kind())
{
case SyntaxKind.Argument:
var argument = (ArgumentSyntax)node;
return argument.NameColon == null;
case SyntaxKind.AttributeArgument:
var attributeArgument = (AttributeArgumentSyntax)node;
return attributeArgument.NameColon == null
&& attributeArgument.NameEquals == null;
default:
return false;
}
}
protected override bool IsLegalToAddNamedArguments(ImmutableArray<IParameterSymbol> parameters, int argumentCount)
{
return !parameters.LastOrDefault().IsParams || parameters.Length >= argumentCount;
}
}
}
// 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.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CodeRefactorings.UseNamedArguments
{
internal abstract class AbstractUseNamedArgumentsCodeRefactoringProvider : CodeRefactoringProvider
{
public sealed override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
if (document.Project.Solution.Workspace.Kind == WorkspaceKind.MiscellaneousFiles)
{
return;
}
var cancellationToken = context.CancellationToken;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var argument = root.FindNode(context.Span).FirstAncestorOrSelf<SyntaxNode>(IsCandidate);
if (argument == null)
{
return;
}
if (!IsPositionalArgument(argument))
{
return;
}
var receiver = GetReceiver(argument);
if (receiver == null)
{
return;
}
if (receiver.ContainsDiagnostics)
{
return;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var symbol = semanticModel.GetSymbolInfo(receiver, cancellationToken).Symbol;
if (symbol == null)
{
return;
}
var parameters = symbol.GetParameters();
if (parameters.IsDefaultOrEmpty)
{
return;
}
var t = GetArgumentListIndexAndCount(argument);
if (t.Item1 < 0)
{
return;
}
if (!IsLegalToAddNamedArguments(parameters, t.Item2))
{
return;
}
var argumentName = parameters[t.Item1].Name;
context.RegisterRefactoring(new MyCodeAction(string.Format(FeaturesResources.Add_argument_name_0, argumentName),
c => AddNamedArgumentsAsync(root, document, argument, parameters, t.Item1)));
}
private Task<Document> AddNamedArgumentsAsync(
SyntaxNode root,
Document document,
SyntaxNode firstArgument,
ImmutableArray<IParameterSymbol> parameters,
int index)
{
var argumentList = firstArgument.Parent;
var newArgumentList = GetOrSynthesizeNamedArguments(parameters, argumentList, index);
var newRoot = root.ReplaceNode(argumentList, newArgumentList);
return Task.FromResult(document.WithSyntaxRoot(newRoot));
}
protected abstract bool IsCandidate(SyntaxNode node);
protected abstract bool IsPositionalArgument(SyntaxNode argument);
protected abstract bool IsLegalToAddNamedArguments(ImmutableArray<IParameterSymbol> parameters, int argumentCount);
protected abstract ValueTuple<int, int> GetArgumentListIndexAndCount(SyntaxNode argument);
protected abstract SyntaxNode GetReceiver(SyntaxNode argument);
protected abstract SyntaxNode GetOrSynthesizeNamedArguments(ImmutableArray<IParameterSymbol> parameters, SyntaxNode argumentList, int index);
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument)
: base(title, createChangedDocument)
{
}
}
}
}
......@@ -99,6 +99,7 @@
<Compile Include="AddPackage\InstallPackageDirectlyCodeActionOperation.cs" />
<Compile Include="AddPackage\InstallWithPackageManagerCodeAction.cs" />
<Compile Include="AddPackage\InstallPackageParentCodeAction.cs" />
<Compile Include="CodeRefactorings\UseNamedArguments\AbstractUseNamedArgumentsCodeRefactoringProvider.cs" />
<Compile Include="CodeStyle\AbstractCodeStyleDiagnosticAnalyzer.cs" />
<Compile Include="AddImport\CodeActions\PackageReference.InstallPackageAndAddImportCodeAction.cs" />
<Compile Include="AddImport\CodeActions\PackageReference.InstallWithPackageManagerCodeAction.cs" />
......@@ -713,4 +714,4 @@
<ItemGroup />
<Import Project="..\..\..\Compilers\Core\AnalyzerDriver\AnalyzerDriver.projitems" Label="Shared" />
<Import Project="..\..\..\..\build\Targets\Imports.targets" />
</Project>
</Project>
\ No newline at end of file
......@@ -98,6 +98,15 @@ internal class FeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Add argument name &apos;{0}&apos;.
/// </summary>
internal static string Add_argument_name_0 {
get {
return ResourceManager.GetString("Add_argument_name_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Add both.
/// </summary>
......
......@@ -1178,4 +1178,7 @@ This version used in: {2}</value>
<data name="Add_document_0" xml:space="preserve">
<value>Add document '{0}'</value>
</data>
<data name="Add_argument_name_0" xml:space="preserve">
<value>Add argument name '{0}'</value>
</data>
</root>
\ No newline at end of file
......@@ -103,6 +103,7 @@
<Compile Include="CodeFixes\GenerateParameterizedMember\GenerateParameterizedMemberCodeFixProvider.vb" />
<Compile Include="CodeFixes\GenerateType\GenerateTypeCodeFixProvider.vb" />
<Compile Include="CodeFixes\GenerateVariable\GenerateVariableCodeFixProvider.vb" />
<Compile Include="CodeRefactorings\UseNamedArguments\VisualBasicUseNamedArgumentsCodeRefactoringProvider.vb" />
<Compile Include="ImplementAbstractClass\VisualBasicImplementAbstractClassCodeFixProvider.vb" />
<Compile Include="CodeFixes\IncorrectExitContinue\IncorrectExitContinueCodeFixProvider.AddKeywordCodeAction.vb" />
<Compile Include="CodeFixes\IncorrectExitContinue\IncorrectExitContinueCodeFixProvider.ReplaceKeywordCodeAction.vb" />
......@@ -472,4 +473,4 @@
</ItemGroup>
<Import Project="..\..\..\Compilers\VisualBasic\BasicAnalyzerDriver\BasicAnalyzerDriver.projitems" Label="Shared" />
<Import Project="..\..\..\..\build\Targets\Imports.targets" />
</Project>
</Project>
\ No newline at end of file
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System
Imports System.Composition
Imports System.Collections.Immutable
Imports System.Linq
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.CodeRefactorings.UseNamedArguments
Imports Microsoft.CodeAnalysis.VisualBasic
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.UseNamedArguments
<ExtensionOrder(After:=PredefinedCodeRefactoringProviderNames.IntroduceVariable)>
<ExportCodeRefactoringProvider(LanguageNames.VisualBasic, Name:=NameOf(VisualBasicUseNamedArgumentsCodeRefactoringProvider)), [Shared]>
Friend Class VisualBasicUseNamedArgumentsCodeRefactoringProvider
Inherits AbstractUseNamedArgumentsCodeRefactoringProvider
Protected Overrides Function IsCandidate(node As SyntaxNode) As Boolean
Return node.IsKind(SyntaxKind.SimpleArgument)
End Function
Protected Overrides Function IsPositionalArgument(node As SyntaxNode) As Boolean
Dim argument = DirectCast(node, SimpleArgumentSyntax)
Return argument.NameColonEquals Is Nothing
End Function
Protected Overrides Function IsLegalToAddNamedArguments(parameters As ImmutableArray(Of IParameterSymbol), argumentCount As Integer) As Boolean
Return Not parameters.LastOrDefault().IsParams OrElse parameters.Length > argumentCount
End Function
Protected Overrides Function GetArgumentListIndexAndCount(node As SyntaxNode) As ValueTuple(Of Integer, Integer)
Dim argumentList = DirectCast(node.Parent, ArgumentListSyntax)
Return ValueTuple.Create(argumentList.Arguments.IndexOf(DirectCast(node, SimpleArgumentSyntax)), argumentList.Arguments.Count)
End Function
Protected Overrides Function GetReceiver(argument As SyntaxNode) As SyntaxNode
If argument.Parent?.Parent?.IsKind(SyntaxKind.Attribute) = True Then
Return Nothing
End If
Return argument.Parent.Parent
End Function
Private Shared Iterator Function GetNamedAruments(parameters As ImmutableArray(Of IParameterSymbol),
argumentList As ArgumentListSyntax, index As Integer) As IEnumerable(Of SyntaxNode)
Dim arguments = argumentList.Arguments
For i As Integer = 0 To arguments.Count - 1
Dim argument = DirectCast(arguments(i), ArgumentSyntax)
If i < index Then
Yield argument
ElseIf argument.IsNamed Then
Yield argument
ElseIf argument.IsOmitted Then
Continue For
Else
Dim parameter = parameters(i)
Dim simpleArgument = DirectCast(argument, SimpleArgumentSyntax)
Yield simpleArgument.WithNameColonEquals(SyntaxFactory.NameColonEquals(SyntaxFactory.IdentifierName(parameter.Name))).WithTriviaFrom(argument)
End If
Next
End Function
Protected Overrides Function GetOrSynthesizeNamedArguments(parameters As ImmutableArray(Of IParameterSymbol),
argumentList As SyntaxNode, index As Integer) As SyntaxNode
Dim argumentListSyntax = DirectCast(argumentList, ArgumentListSyntax)
Dim namedArguments = GetNamedAruments(parameters, argumentListSyntax, index)
Return argumentListSyntax.WithArguments(SyntaxFactory.SeparatedList(namedArguments))
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册