提交 6552e132 编写于 作者: M Martin Strecker

Initial commit. Re-factored AbstractAddParametercodeFixProvider to reuse...

Initial commit. Re-factored AbstractAddParametercodeFixProvider to reuse insert position logic. First simple C# tests.
上级 0846ba67
......@@ -582,6 +582,92 @@ void M()
{
new C(default, 1);
}
}");
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestInvocationInstanceMethod1()
{
await TestInRegularAndScriptAsync(
@"
class C
{
void M1()
{
}
void M2()
{
int i=0;
[|M1|](i);
}
}
",
@"
class C
{
void M1(int i)
{
}
void M2()
{
int i=0;
M1(i);
}
}
");
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestInvocationInheritedMethodGetFixed()
{
await TestInRegularAndScriptAsync(
@"
class Base
{
protected void M1()
{
}
}
class C1 : Base
{
void M2()
{
int i = 0;
[|M1|](i);
}
}",
@"
class Base
{
protected void M1(int i)
{
}
}
class C1 : Base
{
void M2()
{
int i = 0;
M1(i);
}
}");
}
[WorkItem(21446, "https://github.com/dotnet/roslyn/issues/21446")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddParameter)]
public async Task TestInvocationInheritedMethodInMetadatGetsNotFixed()
{
await TestMissingAsync(
@"
class C1
{
void M2()
{
int i = 0;
[|GetHashCode|](i);
}
}");
}
}
......
......@@ -3,8 +3,10 @@
using System;
using System.Collections.Immutable;
using System.Composition;
using System.Linq;
using Microsoft.CodeAnalysis.AddParameter;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateMethod;
using Microsoft.CodeAnalysis.CSharp.GenerateConstructor;
using Microsoft.CodeAnalysis.CSharp.Syntax;
......@@ -20,8 +22,11 @@ internal class CSharpAddParameterCodeFixProvider : AbstractAddParameterCodeFixPr
InvocationExpressionSyntax,
ObjectCreationExpressionSyntax>
{
public override ImmutableArray<string> FixableDiagnosticIds { get; } =
GenerateConstructorDiagnosticIds.AllDiagnosticIds;
private static readonly ImmutableArray<string> AddParameterFixableDiagnosticIds =
GenerateConstructorDiagnosticIds.AllDiagnosticIds.Union(
GenerateMethodDiagnosticIds.FixableDiagnosticIds).ToImmutableArray();
public override ImmutableArray<string> FixableDiagnosticIds => AddParameterFixableDiagnosticIds;
protected override ImmutableArray<string> TooManyArgumentsDiagnosticIds { get; } =
GenerateConstructorDiagnosticIds.TooManyArgumentsDiagnosticIds;
......
......@@ -73,11 +73,25 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
.LastOrDefault(a => a.AncestorsAndSelf().Contains(node));
}
private Task HandleInvocationExpressionAsync(
private async Task HandleInvocationExpressionAsync(
CodeFixContext context, TInvocationExpressionSyntax invocationExpression, TArgumentSyntax argumentOpt)
{
// Currently we only support this for 'new obj' calls.
return SpecializedTasks.EmptyTask;
var document = context.Document;
var cancellationToken = context.CancellationToken;
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
var expression = syntaxFacts.GetExpressionOfInvocationExpression(invocationExpression);
if (expression == null)
{
return;
}
var symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken);
var candidates = symbolInfo.CandidateSymbols.OfType<IMethodSymbol>().ToImmutableArray();
var arguments = (SeparatedSyntaxList<TArgumentSyntax>)syntaxFacts.GetArgumentsOfInvocationExpression(invocationExpression);
var argumentInsertPositionInMethodCandidates = GetArgumentInsertPositionForMethodCandidates(argumentOpt, semanticModel, syntaxFacts, arguments, candidates);
RegisterFixForMethodOverloads(context, arguments, argumentInsertPositionInMethodCandidates);
}
private async Task HandleObjectCreationExpressionAsync(
......@@ -111,17 +125,43 @@ 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);
}
private void RegisterFixForMethodOverloads(CodeFixContext context, SeparatedSyntaxList<TArgumentSyntax> arguments, ImmutableArray<(IMethodSymbol method, TArgumentSyntax argument, int index)> 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))
{
var parameters = method.Parameters.Select(p => p.ToDisplayString(SimpleFormat));
var signature = $"{method.Name}({string.Join(", ", parameters)})";
var title = string.Format(FeaturesResources.Add_parameter_to_0, signature);
context.RegisterCodeFix(
new MyCodeAction(title, c => FixAsync(context.Document, method, argumentToAdd, arguments, c)),
context.Diagnostics);
}
}
private ImmutableArray<(IMethodSymbol method, TArgumentSyntax argument, int index)> GetArgumentInsertPositionForMethodCandidates(TArgumentSyntax argumentOpt, SemanticModel semanticModel, ISyntaxFactsService syntaxFacts, SeparatedSyntaxList<TArgumentSyntax> arguments, ImmutableArray<IMethodSymbol> methodCandidates)
{
var comparer = syntaxFacts.StringComparer;
var constructorsAndArgumentToAdd = ArrayBuilder<(IMethodSymbol constructor, TArgumentSyntax argument, int index)>.GetInstance();
var methodsAndArgumentToAdd = ArrayBuilder<(IMethodSymbol constructor, TArgumentSyntax argument, int index)>.GetInstance();
foreach (var constructor in type.InstanceConstructors.OrderBy(m => m.Parameters.Length))
foreach (var method in methodCandidates.OrderBy(m => m.Parameters.Length))
{
if (constructor.IsNonImplicitAndFromSource() &&
NonParamsParameterCount(constructor) < arguments.Count)
if (method.IsNonImplicitAndFromSource() &&
NonParamsParameterCount(method) < arguments.Count)
{
var argumentToAdd = DetermineFirstArgumentToAdd(
semanticModel, syntaxFacts, comparer, constructor,
semanticModel, syntaxFacts, comparer, method,
arguments, argumentOpt);
if (argumentToAdd != null)
......@@ -135,29 +175,13 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
continue;
}
constructorsAndArgumentToAdd.Add(
(constructor, argumentToAdd, arguments.IndexOf(argumentToAdd)));
methodsAndArgumentToAdd.Add(
(method, argumentToAdd, arguments.IndexOf(argumentToAdd)));
}
}
}
// 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 tuple in constructorsAndArgumentToAdd.OrderByDescending(t => t.index))
{
var constructor = tuple.constructor;
var argumentToAdd = tuple.argument;
var parameters = constructor.Parameters.Select(p => p.ToDisplayString(SimpleFormat));
var signature = $"{type.Name}({string.Join(", ", parameters)})";
var title = string.Format(FeaturesResources.Add_parameter_to_0, signature);
context.RegisterCodeFix(
new MyCodeAction(title, c => FixAsync(document, constructor, argumentToAdd, arguments, c)),
context.Diagnostics);
}
return methodsAndArgumentToAdd.ToImmutableArray();
}
private int NonParamsParameterCount(IMethodSymbol method)
......@@ -477,7 +501,7 @@ private int NonParamsParameterCount(IMethodSymbol method)
}
private bool TypeInfoMatchesWithParamsExpansion(
TypeInfo argumentTypeInfo, IParameterSymbol parameter,
TypeInfo argumentTypeInfo, IParameterSymbol parameter,
bool isNullLiteral, bool isDefaultLiteral)
{
if (parameter.IsParams && parameter.Type is IArrayTypeSymbol arrayType)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册