提交 6dfd11ef 编写于 作者: M Martin Strecker

Added support for type parameter and added support for more compiler diagnostics cases.

上级 3d943fe2
......@@ -758,8 +758,9 @@ void M2()
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestInvocationExtensionMethod()
{
await TestAsync(
var code =
@"
namespace N {
static class Extensions
{
public static void ExtensionM1(this object o)
......@@ -772,8 +773,10 @@ void M1()
{
new object().[|ExtensionM1|](1);
}
}",
}}";
var fix =
@"
namespace N {
static class Extensions
{
public static void ExtensionM1(this object o, int v)
......@@ -786,7 +789,8 @@ void M1()
{
new object().ExtensionM1(1);
}
}", null);
}}";
await TestInRegularAndScriptAsync(code, fix);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
......@@ -1007,12 +1011,20 @@ void M2()
M1(1, 2);
}
}";
using (var workspace = CreateWorkspaceFromOptions(code, default))
{
var actions = await GetCodeActionsAsync(workspace, default);
Assert.True(actions.Length == 1);
}
var fix1 =
@"
class C1
{
void M1(string s1, string s2) { }
void M1(int v, string s) { }
void M1(int i) { }
void M2()
{
M1(1, 2);
}
}";
await TestInRegularAndScriptAsync(code, fix0, 0);
await TestInRegularAndScriptAsync(code, fix1, 1);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
......@@ -1114,7 +1126,6 @@ void M2()
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
[Trait("TODO", "Fix broken")]
public async Task TestInvocationCS0305()
{
var code =
......@@ -1131,19 +1142,17 @@ void M2()
@"
class C1
{
void M1<T>(T i, bool v) { }
void M1<T, T1>(T i, T1 v) { }
void M2()
{
M1<int, bool>(1, true);
}
}";
// Should be void M1<T, T1>(T i, T1 v) { }
await TestInRegularAndScriptAsync(code, fix0, 0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
[Trait("TODO", "Fix broken")]
public async Task TestInvocationCS0308()
{
var code =
......@@ -1160,13 +1169,12 @@ void M2()
@"
class C1
{
void M1(int i, bool v) { }
void M1<T>(int i, T v) { }
void M2()
{
M1<bool>(1, true);
}
}";
// Should be void M1<T>(int i, T v) { }
await TestInRegularAndScriptAsync(code, fix0, 0);
}
......@@ -1192,7 +1200,6 @@ class C1 : I1
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
[Trait("TODO", "Fix missing")]
public async Task TestInvocationCS1503()
{
var code =
......@@ -1207,13 +1214,23 @@ void M2()
}
}
";
//Should fix second overload to void M1(double d, int v) { }
await TestMissingAsync(code);
var fix0 =
@"
class C1
{
void M1(int i1, int i2) { }
void M1(double d, int v) { }
void M2()
{
M1(1.0, 1);
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
[Trait("TODO", "Fix missing")]
public async Task TestInvocationCS1660()
{
var code =
......@@ -1228,13 +1245,23 @@ void M2()
}
}
";
//Should fix second overload to void M1(System.Action a, int v) { }
await TestMissingAsync(code);
var fix =
@"
class C1
{
void M1(int i1, int i2) { }
void M1(System.Action a, int v) { }
void M2()
{
M1(()=> { }, 1);
}
}
";
await TestInRegularAndScriptAsync(code, fix);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
[Trait("TODO", "Fix missing")]
public async Task TestInvocationCS1739()
{
var code =
......@@ -1248,9 +1275,337 @@ void M2()
}
}
";
// Should fix to: void M1(int i1, int i2) { }
await TestMissingAsync(code);
var fix =
@"
class C1
{
void M1(int i1, int i2) { }
void M2()
{
M1(i2: 1);
}
}
";
await TestInRegularAndScriptAsync(code, fix);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestInvocationAddTypeParameter_AddTypeParameterIfUserSpecifiesOne_OnlyTypeArgument()
{
var code =
@"
class C1
{
void M1() { }
void M2()
{
[|M1|]<bool>();
}
}
";
var fix0 =
@"
class C1
{
void M1<T>() { }
void M2()
{
M1<bool>();
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestInvocationAddTypeParameter_AddTypeParameterIfUserSpecifiesOne_TypeArgumentAndParameterArgument()
{
var code =
@"
class C1
{
void M1() { }
void M2()
{
[|M1|]<bool>(true);
}
}
";
var fix0 =
@"
class C1
{
void M1<T>(T v) { }
void M2()
{
M1<bool>(true);
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestInvocation_ExisitingTypeArgumentIsNotGeneralized()
{
var code =
@"
class C1
{
void M1<T>(T v) { }
void M2()
{
[|M1|](true, true);
}
}
";
var fix0 =
@"
class C1
{
void M1<T>(T v, bool v1) { }
void M2()
{
M1(true, true);
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
[Trait("TODO", "Fix all partial definitions")]
public async Task TestInvocationPartialClasses()
{
var code =
@"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace N1
{
partial class C1
{
partial void PartialM();
}
}
</Document>
<Document>
namespace N1
{
partial class C1
{
partial void PartialM() { }
void M1()
{
[|PartialM|](1);
}
}
}
</Document>
</Project>
</Workspace>";
var fix0 =
@"
<Workspace>
<Project Language=""C#"" AssemblyName=""Assembly1"" CommonReferences=""true"">
<Document>
namespace N1
{
partial class C1
{
partial void PartialM(int v);
}
}
</Document>
<Document>
namespace N1
{
partial class C1
{
partial void PartialM() { }
void M1()
{
PartialM(1);
}
}
}
</Document>
</Project>
</Workspace>";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
[Trait("TODO", "Code formatting")]
public async Task TestObjectCreationAddTypeParameter_AddTypeParameterIfUserSpecifiesOne_TypeParameterAndParameterArgument()
{
var code =
@"
class C1
{
C1() { }
void M2()
{
var c = new [|C1<bool>|](true);
}
}
";
var fix0 =
@"
class C1<T>
{
C1(T v) { }
void M2()
{
var c = new C1<bool>(true);
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestObjectCreationAddTypeParameter_GeneralizeGenericArgument1()
{
var code =
@"
class C1<T>
{
C1() { }
void M2()
{
var c = new [|C1<bool>|](true);
}
}
";
var fix0 =
@"
class C1<T>
{
C1(T v) { }
void M2()
{
var c = new C1<bool>(true);
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestObjectCreationAddTypeParameter_GeneralizeGenericArgument2()
{
var code =
@"
class C1<T>
{
C1() { }
void M1<U>() { }
void M2()
{
var c = new C1<bool>();
c.[|M1<int, bool>|]();
}
}
";
var fix0 =
@"
class C1<T>
{
C1() { }
void M1<U, T1>() { }
void M2()
{
var c = new C1<bool>();
c.M1<int, bool>();
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestObjectCreationAddTypeParameter_GeneralizeGenericArgument3()
{
var code =
@"
class C1<T>
{
C1() { }
void M1<U>() { }
void M2()
{
var c = new C1<bool>();
c.[|M1<int, string>|]();
}
}
";
var fix0 =
@"
class C1<T>
{
C1() { }
void M1<U, T1>() { }
void M2()
{
var c = new C1<bool>();
c.M1<int, string>();
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestObjectCreationAddTypeParameter_GeneralizeGenericArgument4()
{
var code =
@"
class C1<T> where T: struct
{
C1() { }
void M1<U>() where U : struct
{
}
void M2()
{
var c = new C1<bool>();
c.[|M1<int, string>|]();
}
}
";
var fix0 =
@"
class C1<T> where T: struct
{
C1() { }
void M1<U, T1>() where U : struct
{
}
void M2()
{
var c = new C1<bool>();
c.M1<int, string>();
}
}
";
await TestInRegularAndScriptAsync(code, fix0);
}
}
}
......@@ -20,7 +20,8 @@ internal class CSharpAddParameterCodeFixProvider : AbstractAddParameterCodeFixPr
ArgumentListSyntax,
AttributeArgumentListSyntax,
InvocationExpressionSyntax,
ObjectCreationExpressionSyntax>
ObjectCreationExpressionSyntax,
TypeSyntax>
{
private static readonly ImmutableArray<string> AddParameterFixableDiagnosticIds =
GenerateConstructorDiagnosticIds.AllDiagnosticIds.Union(
......@@ -28,9 +29,13 @@ internal class CSharpAddParameterCodeFixProvider : AbstractAddParameterCodeFixPr
Enumerable.Repeat("CS1593", 1)). // C# Delegate 'Action' does not take 1 arguments
ToImmutableArray();
public override ImmutableArray<string> FixableDiagnosticIds => AddParameterFixableDiagnosticIds;
public override ImmutableArray<string> FixableDiagnosticIds
=> AddParameterFixableDiagnosticIds;
protected override ImmutableArray<string> TooManyArgumentsDiagnosticIds { get; } =
GenerateConstructorDiagnosticIds.TooManyArgumentsDiagnosticIds;
protected override ImmutableArray<string> TooManyArgumentsDiagnosticIds
=> GenerateConstructorDiagnosticIds.TooManyArgumentsDiagnosticIds;
protected override ImmutableArray<string> CannotConvertDiagnosticIds
=> GenerateConstructorDiagnosticIds.CannotConvertDiagnosticIds;
}
}
......@@ -20,6 +20,7 @@ internal static class GenerateConstructorDiagnosticIds
public const string CS1729 = nameof(CS1729); // CS1729: 'C' does not contain a constructor that takes n arguments
public const string CS1739 = nameof(CS1739); // CS1739: The best overload for 'Program' does not have a parameter named 'v'
public const string CS1503 = nameof(CS1503); // CS1503: Argument 1: cannot convert from 'T1' to 'T2'
public const string CS1660 = nameof(CS1660); // CS1660: Cannot convert lambda expression to type 'string[]' because it is not a delegate type
public const string CS7036 = nameof(CS7036); // CS7036: There is no argument given that corresponds to the required formal parameter 'v' of 'C.C(int)'
public static readonly ImmutableArray<string> AllDiagnosticIds =
......@@ -27,6 +28,9 @@ internal static class GenerateConstructorDiagnosticIds
public static readonly ImmutableArray<string> TooManyArgumentsDiagnosticIds =
ImmutableArray.Create(CS1729);
public static readonly ImmutableArray<string> CannotConvertDiagnosticIds =
ImmutableArray.Create(CS1503, CS1660);
}
/// <summary>
......
......@@ -25,14 +25,17 @@ internal abstract class AbstractAddParameterCodeFixProvider<
TArgumentListSyntax,
TAttributeArgumentListSyntax,
TInvocationExpressionSyntax,
TObjectCreationExpressionSyntax> : CodeFixProvider
TObjectCreationExpressionSyntax,
TTypeSyntax> : CodeFixProvider
where TArgumentSyntax : SyntaxNode
where TArgumentListSyntax : SyntaxNode
where TAttributeArgumentListSyntax : SyntaxNode
where TInvocationExpressionSyntax : SyntaxNode
where TObjectCreationExpressionSyntax : SyntaxNode
where TTypeSyntax : SyntaxNode
{
protected abstract ImmutableArray<string> TooManyArgumentsDiagnosticIds { get; }
protected abstract ImmutableArray<string> CannotConvertDiagnosticIds { get; }
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
......@@ -69,6 +72,11 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
return null;
}
if (this.CannotConvertDiagnosticIds.Contains(diagnostic.Id))
{
return null;
}
return initialNode.GetAncestorsOrThis<TArgumentSyntax>()
.LastOrDefault(a => a.AncestorsAndSelf().Contains(node));
}
......@@ -89,6 +97,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken);
var candidates = symbolInfo.CandidateSymbols.OfType<IMethodSymbol>().ToImmutableArray();
if (candidates.Length == 0)
{
// Invocation might be on an delegate. We try to find the declaration of the delegate.
......@@ -108,7 +117,9 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
}
var arguments = (SeparatedSyntaxList<TArgumentSyntax>)syntaxFacts.GetArgumentsOfInvocationExpression(invocationExpression);
var argumentInsertPositionInMethodCandidates = GetArgumentInsertPositionForMethodCandidates(argumentOpt, semanticModel, syntaxFacts, arguments, candidates);
var typeArguments = (SeparatedSyntaxList<TTypeSyntax>)syntaxFacts.GetTypeArgumentsOfInvocationExpression(invocationExpression);
var argumentInsertPositionInMethodCandidates = GetArgumentInsertPositionAndTypeArgumentForMethodCandidates(semanticModel, syntaxFacts, candidates, arguments, argumentOpt, typeArguments);
RegisterFixForMethodOverloads(context, arguments, argumentInsertPositionInMethodCandidates);
}
......@@ -144,18 +155,17 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
var arguments = (SeparatedSyntaxList<TArgumentSyntax>)syntaxFacts.GetArgumentsOfObjectCreationExpression(objectCreation);
var methodCandidates = type.InstanceConstructors;
var constructorsAndArgumentToAdd = GetArgumentInsertPositionForMethodCandidates(argumentOpt, semanticModel, syntaxFacts, arguments, methodCandidates);
RegisterFixForMethodOverloads(context, arguments, constructorsAndArgumentToAdd);
var typeArguments = (SeparatedSyntaxList<TTypeSyntax>)syntaxFacts.GetTypeArgumentsOfObjectCreationExpression(objectCreation);
var candidates = GetArgumentInsertPositionAndTypeArgumentForMethodCandidates(semanticModel, syntaxFacts, methodCandidates, arguments, argumentOpt, typeArguments);
RegisterFixForMethodOverloads(context, arguments, candidates);
}
private void RegisterFixForMethodOverloads(CodeFixContext context, SeparatedSyntaxList<TArgumentSyntax> arguments, ImmutableArray<(IMethodSymbol method, TArgumentSyntax argument, int index)> methodsAndArgumentToAdd)
private void RegisterFixForMethodOverloads(CodeFixContext context, SeparatedSyntaxList<TArgumentSyntax> arguments, ImmutableArray<(IMethodSymbol method, TArgumentSyntax argument, int index, TTypeSyntax typeArgument)> methodsAndArgumentToAdd)
{
// Order by the furthest argument index to the nearest argument index. The ones with
// larger argument indexes mean that we matched more earlier arguments (and thus are
// likely to be the correct match).
foreach (var (method, argumentToAdd, _) in methodsAndArgumentToAdd.OrderByDescending(t => t.index))
foreach (var (method, argumentToAdd, _, typeArgument) in methodsAndArgumentToAdd.OrderByDescending(t => t.index))
{
var parameters = method.Parameters.Select(p => p.ToDisplayString(SimpleFormat));
var signature = $"{method.Name}({string.Join(", ", parameters)})";
......@@ -163,38 +173,32 @@ private void RegisterFixForMethodOverloads(CodeFixContext context, SeparatedSynt
var title = string.Format(FeaturesResources.Add_parameter_to_0, signature);
context.RegisterCodeFix(
new MyCodeAction(title, c => FixAsync(context.Document, method, argumentToAdd, arguments, c)),
new MyCodeAction(title, c => FixAsync(context.Document, method, argumentToAdd, arguments, typeArgument, c)),
context.Diagnostics);
}
}
private ImmutableArray<(IMethodSymbol method, TArgumentSyntax argument, int index)> GetArgumentInsertPositionForMethodCandidates(TArgumentSyntax argumentOpt, SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, SeparatedSyntaxList<TArgumentSyntax> arguments, ImmutableArray<IMethodSymbol> methodCandidates)
private ImmutableArray<(IMethodSymbol method, TArgumentSyntax argument, int index, TTypeSyntax typeArgument)> GetArgumentInsertPositionAndTypeArgumentForMethodCandidates(
SemanticModel semanticModel,
ISyntaxFactsService syntaxFacts,
ImmutableArray<IMethodSymbol> methodCandidates,
SeparatedSyntaxList<TArgumentSyntax> arguments,
TArgumentSyntax argumentOpt,
SeparatedSyntaxList<TTypeSyntax> typeArguments)
{
var comparer = syntaxFacts.StringComparer;
var methodsAndArgumentToAdd = ArrayBuilder<(IMethodSymbol constructor, TArgumentSyntax argument, int index)>.GetInstance();
var methodsAndArgumentToAdd = ArrayBuilder<(IMethodSymbol constructor, TArgumentSyntax argument, int index, TTypeSyntax typeArgument)>.GetInstance();
foreach (var method in methodCandidates.OrderBy(m => m.Parameters.Length))
{
if (method.IsNonImplicitAndFromSource() &&
NonParamsParameterCount(method) < arguments.Count)
if (method.IsNonImplicitAndFromSource())
{
var argumentToAdd = DetermineFirstArgumentToAdd(
semanticModel, syntaxFacts, comparer, method,
arguments, argumentOpt);
var argumentToAdd = GetArgumentInsertPositionForMethodCandidate(semanticModel, syntaxFacts, method, arguments, argumentOpt);
var typeArgumentToAdd = GetTypeArgumentForMethodCandidate(method, typeArguments);
if (argumentToAdd != null)
if (argumentToAdd != null || typeArgumentToAdd != null)
{
if (argumentOpt != null && argumentToAdd != argumentOpt)
{
// We were trying to fix a specific argument, but the argument we want
// to fix is something different. That means there was an error earlier
// than this argument. Which means we're looking at a non-viable
// constructor. Skip this one.
continue;
}
methodsAndArgumentToAdd.Add(
(method, argumentToAdd, arguments.IndexOf(argumentToAdd)));
(method, argumentToAdd, arguments.IndexOf(argumentToAdd), typeArgumentToAdd));
}
}
}
......@@ -202,6 +206,49 @@ private ImmutableArray<(IMethodSymbol method, TArgumentSyntax argument, int inde
return methodsAndArgumentToAdd.ToImmutableArray();
}
private TTypeSyntax GetTypeArgumentForMethodCandidate(IMethodSymbol method, SeparatedSyntaxList<TTypeSyntax> typeArguments)
{
var typeParameter = method.IsConstructor()
? method.ContainingType.TypeParameters
: method.TypeParameters;
if (typeArguments.Count > typeParameter.Length)
{
return typeArguments[typeParameter.Length];
}
return null;
}
private TArgumentSyntax GetArgumentInsertPositionForMethodCandidate(SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, IMethodSymbol method, SeparatedSyntaxList<TArgumentSyntax> arguments, TArgumentSyntax argumentOpt)
{
var comparer = syntaxFacts.StringComparer;
var isNamedArgument = !string.IsNullOrWhiteSpace(syntaxFacts.GetNameForArgument(argumentOpt));
if (isNamedArgument || NonParamsParameterCount(method) < arguments.Count)
{
var argumentToAdd = DetermineFirstArgumentToAdd(
semanticModel, syntaxFacts, comparer, method,
arguments, argumentOpt);
if (argumentToAdd != null)
{
if (argumentOpt != null && argumentToAdd != argumentOpt)
{
// We were trying to fix a specific argument, but the argument we want
// to fix is something different. That means there was an error earlier
// than this argument. Which means we're looking at a non-viable
// constructor. Skip this one.
return null;
}
}
return argumentToAdd;
}
return null;
}
private int NonParamsParameterCount(IMethodSymbol method)
=> method.IsParams() ? method.Parameters.Length - 1 : method.Parameters.Length;
......@@ -210,17 +257,46 @@ private int NonParamsParameterCount(IMethodSymbol method)
IMethodSymbol method,
TArgumentSyntax argument,
SeparatedSyntaxList<TArgumentSyntax> argumentList,
TTypeSyntax newTypeArgument,
CancellationToken cancellationToken)
{
var methodDeclaration = await method.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
var (parameterSymbol, isNamedArgument) = await CreateParameterSymbolAsync(
invocationDocument, method, argument, cancellationToken).ConfigureAwait(false);
var methodDeclarationRoot = methodDeclaration.SyntaxTree.GetRoot(cancellationToken);
var methodDocument = invocationDocument.Project.Solution.GetDocument(methodDeclaration.SyntaxTree);
var syntaxFacts = methodDocument.GetLanguageService<ISyntaxFactsService>();
var methodDeclarationRoot = methodDeclaration.SyntaxTree.GetRoot(cancellationToken);
var editor = new SyntaxEditor(methodDeclarationRoot, methodDocument.Project.Solution.Workspace);
var parameterDeclaration = default(SyntaxNode);
var insertionIndex = default(int);
var newTypeParameter = newTypeArgument == null ? null : CreateTypeParameterSymbol(method);
if (argument != null)
{
(parameterDeclaration, insertionIndex) = await CreateParameterDeclarationAsync(editor, invocationDocument, method, methodDeclaration, argumentList, argument, newTypeArgument, newTypeParameter, cancellationToken).ConfigureAwait(false);
}
AddParameter(
syntaxFacts, editor, method, methodDeclaration, argument,
insertionIndex, parameterDeclaration, newTypeParameter, cancellationToken);
var newRoot = editor.GetChangedRoot();
var newDocument = methodDocument.WithSyntaxRoot(newRoot);
return newDocument;
}
private async Task<(SyntaxNode parameterDeclaration, int insertionIndex)> CreateParameterDeclarationAsync(
SyntaxEditor editor,
Document invocationDocument,
IMethodSymbol method,
SyntaxNode methodDeclaration,
SeparatedSyntaxList<TArgumentSyntax> argumentList,
TArgumentSyntax argument,
TTypeSyntax newTypeArgument,
ITypeParameterSymbol newTypeParameter,
CancellationToken cancellationToken)
{
var (parameterSymbol, isNamedArgument) = await CreateParameterSymbolAsync(
invocationDocument, method, argument, newTypeArgument, newTypeParameter, cancellationToken).ConfigureAwait(false);
var parameterDeclaration = editor.Generator.ParameterDeclaration(parameterSymbol)
.WithAdditionalAnnotations(Formatter.Annotation);
......@@ -235,30 +311,54 @@ private int NonParamsParameterCount(IMethodSymbol method)
insertionIndex++;
}
AddParameter(
syntaxFacts, editor, methodDeclaration, argument,
insertionIndex, parameterDeclaration, cancellationToken);
var newRoot = editor.GetChangedRoot();
var newDocument = methodDocument.WithSyntaxRoot(newRoot);
return (parameterDeclaration, insertionIndex);
}
return newDocument;
private static ITypeParameterSymbol CreateTypeParameterSymbol(IMethodSymbol method)
{
var exisitingTypeParameter = method.GetAllTypeParameters();
var typeParameterName = NameGenerator.GenerateUniqueName("T", n => exisitingTypeParameter.All(symbol => symbol.Name != n));
return CodeGenerationSymbolFactory.CreateTypeParameterSymbol(typeParameterName);
}
private static ImmutableArray<ITypeParameterSymbol> GetTypeParameterOfClassOrMethod(IMethodSymbol method)
=> method.IsConstructor()
? (method.ContainingSymbol as ITypeSymbol).GetAllTypeParameters()
: method.TypeParameters;
private async Task<(IParameterSymbol, bool isNamedArgument)> CreateParameterSymbolAsync(
Document invocationDocument,
IMethodSymbol method,
TArgumentSyntax argument,
TTypeSyntax newTypeArgument,
ITypeParameterSymbol newTypeParameter,
CancellationToken cancellationToken)
{
var syntaxFacts = invocationDocument.GetLanguageService<ISyntaxFactsService>();
var semanticFacts = invocationDocument.GetLanguageService<ISemanticFactsService>();
var argumentName = syntaxFacts.GetNameForArgument(argument);
var expression = syntaxFacts.GetExpressionOfArgument(argument);
var semanticModel = await invocationDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var parameterType = semanticModel.GetTypeInfo(expression).Type ?? semanticModel.Compilation.ObjectType;
if (newTypeArgument != null)
{
var typeArgumentType = semanticModel.GetTypeInfo(newTypeArgument).Type;
if (typeArgumentType == parameterType)
{
parameterType = newTypeParameter;
}
}
var typeArguments = method.GetAllTypeArguments();
var fittingTypeArgumentIndex = typeArguments.IndexOf(parameterType);
if (fittingTypeArgumentIndex >= 0)
{
var typeParameter = method.GetAllTypeParameters();
parameterType = typeParameter[fittingTypeArgumentIndex];
}
if (!string.IsNullOrWhiteSpace(argumentName))
{
var newParameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol(
......@@ -282,15 +382,26 @@ private int NonParamsParameterCount(IMethodSymbol method)
private static void AddParameter(
ISyntaxFactsService syntaxFacts,
SyntaxEditor editor,
IMethodSymbol methodSymbol,
SyntaxNode declaration,
TArgumentSyntax argument,
int insertionIndex,
SyntaxNode parameterDeclaration,
ITypeParameterSymbol typeParameter,
CancellationToken cancellationToken)
{
var sourceText = declaration.SyntaxTree.GetText(cancellationToken);
var generator = editor.Generator;
//Append the new type parameter to the end of the type parameter list
if (typeParameter != null)
{
var typeParameters = GetTypeParameterOfClassOrMethod(methodSymbol).Add(typeParameter);
var typeDeclarationSyntax = syntaxFacts.GetContainingTypeDeclaration(declaration, declaration.SpanStart);
var typeParameterDeclaration = methodSymbol.IsConstructor() ? typeDeclarationSyntax : declaration;
editor.SetTypeParameters(typeParameterDeclaration, typeParameters.Select(tp => tp.Name));
}
var existingParameters = generator.GetParameters(declaration);
var placeOnNewLine = ShouldPlaceParametersOnNewLine(existingParameters, cancellationToken);
......
......@@ -17,12 +17,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.AddParameter
ArgumentListSyntax,
ArgumentListSyntax,
InvocationExpressionSyntax,
ObjectCreationExpressionSyntax)
ObjectCreationExpressionSyntax,
TypeSyntax)
Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) =
GenerateConstructorDiagnosticIds.AllDiagnosticIds
Protected Overrides ReadOnly Property TooManyArgumentsDiagnosticIds As ImmutableArray(Of String) =
GenerateConstructorDiagnosticIds.TooManyArgumentsDiagnosticIds
Protected Overrides ReadOnly Property CannotConvertDiagnosticIds As ImmutableArray(Of String) =
GenerateConstructorDiagnosticIds.CannotConvertDiagnosticIds
End Class
End Namespace
......@@ -22,6 +22,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateConstructor
Friend Shared ReadOnly AllDiagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(BC30057, BC30272, BC30274, BC30389, BC30455, BC32006, BC30512, BC30387)
Friend Shared ReadOnly TooManyArgumentsDiagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(BC30057)
Friend Shared ReadOnly CannotConvertDiagnosticIds As ImmutableArray(Of String) = ImmutableArray.Create(BC30512, BC32006)
End Class
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.GenerateConstructor), [Shared]>
......
......@@ -42,7 +42,7 @@ public SyntaxTrivia ElasticCarriageReturnLineFeed
protected override IDocumentationCommentService DocumentationCommentService
=> CSharpDocumentationCommentService.Instance;
public bool SupportsIndexingInitializer(ParseOptions options)
public bool SupportsIndexingInitializer(ParseOptions options)
=> ((CSharpParseOptions)options).LanguageVersion >= LanguageVersion.CSharp6;
public bool SupportsThrowExpression(ParseOptions options)
......@@ -259,10 +259,10 @@ public bool IsReturnStatement(SyntaxNode node)
public bool IsStatement(SyntaxNode node)
=> node is StatementSyntax;
public bool IsParameter(SyntaxNode node)
=> node is ParameterSyntax;
public bool IsVariableDeclarator(SyntaxNode node)
=> node is VariableDeclaratorSyntax;
......@@ -1343,6 +1343,25 @@ public SeparatedSyntaxList<SyntaxNode> GetArgumentsOfObjectCreationExpression(Sy
public SeparatedSyntaxList<SyntaxNode> GetArgumentsOfArgumentList(SyntaxNode argumentList)
=> (argumentList as ArgumentListSyntax)?.Arguments ?? default(SeparatedSyntaxList<SyntaxNode>);
public SeparatedSyntaxList<SyntaxNode> GetTypeArgumentsOfInvocationExpression(SyntaxNode invocationExpression)
{
if (invocationExpression is InvocationExpressionSyntax invocationExpressionSyntax)
{
switch (invocationExpressionSyntax.Expression)
{
case GenericNameSyntax genericName:
return genericName.TypeArgumentList.Arguments;
case MemberAccessExpressionSyntax memberAccessExpression when memberAccessExpression.Name is GenericNameSyntax genericName:
return genericName.TypeArgumentList.Arguments;
}
}
return default(SeparatedSyntaxList<SyntaxNode>);
}
public SeparatedSyntaxList<SyntaxNode> GetTypeArgumentsOfObjectCreationExpression(SyntaxNode objectCreationExpression)
=> ((objectCreationExpression as ObjectCreationExpressionSyntax)?.Type as GenericNameSyntax)?.TypeArgumentList?.Arguments ?? default(SeparatedSyntaxList<SyntaxNode>);
public bool IsRegularComment(SyntaxTrivia trivia)
=> trivia.IsRegularComment();
......@@ -1422,7 +1441,7 @@ public bool IsDeclaration(SyntaxNode node)
private static readonly SyntaxAnnotation s_annotation = new SyntaxAnnotation();
public void AddFirstMissingCloseBrace(
SyntaxNode root, SyntaxNode contextNode,
SyntaxNode root, SyntaxNode contextNode,
out SyntaxNode newRoot, out SyntaxNode newContextNode)
{
// First, annotate the context node in the tree so that we can find it again
......@@ -1587,9 +1606,9 @@ public override bool IsShebangDirectiveTrivia(SyntaxTrivia trivia)
public override bool IsPreprocessorDirective(SyntaxTrivia trivia)
=> SyntaxFacts.IsPreprocessorDirective(trivia.Kind());
private class AddFirstMissingCloseBaceRewriter: CSharpSyntaxRewriter
private class AddFirstMissingCloseBaceRewriter : CSharpSyntaxRewriter
{
private readonly SyntaxNode _contextNode;
private readonly SyntaxNode _contextNode;
private bool _seenContextNode = false;
private bool _addedFirstCloseCurly = false;
......@@ -1626,7 +1645,7 @@ public override SyntaxNode Visit(SyntaxNode node)
// then still ask to format its close curly to make sure all the
// curlies up the stack are properly formatted.
var braces = rewritten.GetBraces();
if (braces.openBrace.Kind() == SyntaxKind.None &&
if (braces.openBrace.Kind() == SyntaxKind.None &&
braces.closeBrace.Kind() == SyntaxKind.None)
{
// Not an item with braces. Just pass it up.
......
......@@ -172,6 +172,8 @@ internal interface ISyntaxFactsService : ILanguageService
SeparatedSyntaxList<SyntaxNode> GetArgumentsOfInvocationExpression(SyntaxNode node);
SeparatedSyntaxList<SyntaxNode> GetArgumentsOfObjectCreationExpression(SyntaxNode node);
SeparatedSyntaxList<SyntaxNode> GetArgumentsOfArgumentList(SyntaxNode node);
SeparatedSyntaxList<SyntaxNode> GetTypeArgumentsOfInvocationExpression(SyntaxNode node);
SeparatedSyntaxList<SyntaxNode> GetTypeArgumentsOfObjectCreationExpression(SyntaxNode node);
bool IsUsingDirectiveName(SyntaxNode node);
bool IsIdentifierName(SyntaxNode node);
......
......@@ -1273,6 +1273,16 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return If(arguments.HasValue, arguments.Value, Nothing)
End Function
Public Function GetTypeArgumentsOfInvocationExpression(invocationExpression As SyntaxNode) As SeparatedSyntaxList(Of SyntaxNode) Implements ISyntaxFactsService.GetTypeArgumentsOfInvocationExpression
Dim arguments = TryCast(TryCast(invocationExpression, InvocationExpressionSyntax)?.Expression, GenericNameSyntax)?.TypeArgumentList?.Arguments
Return If(arguments.HasValue, arguments.Value, Nothing)
End Function
Public Function GetTypeArgumentsOfObjectCreationExpression(objectCreationExpression As SyntaxNode) As SeparatedSyntaxList(Of SyntaxNode) Implements ISyntaxFactsService.GetTypeArgumentsOfObjectCreationExpression
Dim arguments = TryCast(TryCast(objectCreationExpression, ObjectCreationExpressionSyntax)?.Type, GenericNameSyntax)?.TypeArgumentList?.Arguments
Return If(arguments.HasValue, arguments.Value, Nothing)
End Function
Public Function ConvertToSingleLine(node As SyntaxNode, Optional useElasticTrivia As Boolean = False) As SyntaxNode Implements ISyntaxFactsService.ConvertToSingleLine
Return node.ConvertToSingleLine(useElasticTrivia)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册