提交 9672d4c1 编写于 作者: J Jonathon Marolf

Correctly detect existing constructors during code generation

上级 7cb4f224
......@@ -173,5 +173,12 @@ public async Task TestContextualKeywordName()
@"class Program { [|int yield ;|] } ",
@"class Program { int yield ; public Program ( int yield ) { this . yield = yield ; } } ");
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public async Task TestGenerateConstructorNotOfferedForDuplicate()
{
await TestMissingAsync(
"using System ; class X { public X ( string v ) { } static void Test ( ) { new X ( new [|string|] ( ) ) ; } } ");
}
}
}
......@@ -910,6 +910,11 @@ public TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree tree, int positio
{
throw new NotImplementedException();
}
public string GetNameForArgument(SyntaxNode argument)
{
throw new NotImplementedException();
}
}
}
}
......
......@@ -816,6 +816,10 @@ End Class]]></a>.Value.NormalizeLineEndings()
Public Function GetInactiveRegionSpanAroundPosition(tree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As TextSpan Implements ISyntaxFactsService.GetInactiveRegionSpanAroundPosition
Throw New NotImplementedException()
End Function
Public Function GetNameForArgument(argument As SyntaxNode) As String Implements ISyntaxFactsService.GetNameForArgument
Throw New NotImplementedException()
End Function
End Class
End Class
End Namespace
......@@ -578,6 +578,24 @@ End Class
Public Class [|;;|]Derived
Inherits Base
End Class")
End Function
<Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)>
Public Async Function TestGenerateConstructorNotOfferedForDuplicate() As Task
Await TestMissingAsync(
"Imports System
Class X
Private v As String
Public Sub New(v As String)
Me.v = v
End Sub
Sub Test()
Dim x As X = New X(New [|String|]())
End Sub
End Class")
End Function
End Class
......
// 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;
......@@ -10,7 +11,6 @@
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor
{
......@@ -93,23 +93,74 @@ private State()
this.ParameterTypes = this.ParameterTypes ?? GetParameterTypes(service, document, cancellationToken);
this.ParameterRefKinds = this.ParameterRefKinds ?? this.Arguments.Select(service.GetRefKind).ToList();
return !ClashesWithExistingConstructor(service, document, cancellationToken);
return !ClashesWithExistingConstructor(document, cancellationToken);
}
private bool ClashesWithExistingConstructor(TService service, SemanticDocument document, CancellationToken cancellationToken)
private bool ClashesWithExistingConstructor(SemanticDocument document, CancellationToken cancellationToken)
{
var parameters = this.ParameterTypes.Zip(this.ParameterRefKinds, (t, r) => CodeGenerationSymbolFactory.CreateParameterSymbol(
attributes: null,
refKind: r,
isParams: false,
type: t,
name: string.Empty)).ToList();
if (this.ParameterTypes == null ||
this.ParameterRefKinds == null)
{
return false;
}
if(this.ParameterTypes.Count != this.ParameterRefKinds.Count)
{
return false;
}
if (!this.TypeToGenerateIn.InstanceConstructors.Any(c => c.Parameters.Length == this.ParameterTypes.Count))
{
return false;
}
var destinationProvider = document.Project.Solution.Workspace.Services.GetLanguageServices(this.TypeToGenerateIn.Language);
var syntaxFacts = destinationProvider.GetService<ISyntaxFactsService>();
for (int i = 0; i < ParameterTypes.Count; i++)
{
var compareParameterName = false;
var type = this.ParameterTypes[i];
var refKind = this.ParameterRefKinds[i];
string parameterName = GetParameterName(syntaxFacts, i);
if (!string.IsNullOrEmpty(parameterName))
{
compareParameterName = true;
}
var parameterSymbol = CodeGenerationSymbolFactory.CreateParameterSymbol(
attributes: null,
refKind: refKind,
isParams: false,
type: type,
name: parameterName);
var result = this.TypeToGenerateIn.InstanceConstructors.Any(c =>
{
if (i >= c.Parameters.Length)
{
return false;
}
return SymbolEquivalenceComparer
.Instance
.ParameterEquivalenceComparer
.Equals(parameterSymbol, c.Parameters[i], compareParameterName, syntaxFacts.IsCaseSensitive);
});
if (result == false)
{
return false;
}
}
return true;
}
private string GetParameterName(ISyntaxFactsService service, int i)
{
if (i >= this.Arguments?.Count)
{
return string.Empty;
}
return this.TypeToGenerateIn.InstanceConstructors.Any(
c => SignatureComparer.Instance.HaveSameSignature(parameters, c.Parameters, compareParameterName: true, isCaseSensitive: syntaxFacts.IsCaseSensitive));
return service.GetNameForArgument(this.Arguments?[i]);
}
internal List<ITypeSymbol> GetParameterTypes(
......
......@@ -28,7 +28,6 @@ protected AbstractGenerateConstructorService()
protected abstract bool TryInitializeClassDeclarationGenerationState(SemanticDocument document, SyntaxNode classDeclaration, CancellationToken cancellationToken, out SyntaxToken token, out IMethodSymbol constructor, out INamedTypeSymbol typeToGenerateIn);
protected abstract bool TryInitializeConstructorInitializerGeneration(SemanticDocument document, SyntaxNode constructorInitializer, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn);
protected abstract bool TryInitializeSimpleAttributeNameGenerationState(SemanticDocument document, SyntaxNode simpleName, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, out IList<TAttributeArgumentSyntax> attributeArguments, out INamedTypeSymbol typeToGenerateIn);
protected abstract IList<string> GenerateParameterNames(SemanticModel semanticModel, IEnumerable<TArgumentSyntax> arguments, IList<string> reservedNames = null);
protected virtual IList<string> GenerateParameterNames(SemanticModel semanticModel, IEnumerable<TAttributeArgumentSyntax> arguments, IList<string> reservedNames = null) { return null; }
protected abstract string GenerateNameForArgument(SemanticModel semanticModel, TArgumentSyntax argument);
......
......@@ -1429,5 +1429,15 @@ public TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree syntaxTree, int p
return default(TextSpan);
}
public string GetNameForArgument(SyntaxNode argument)
{
if ((argument as ArgumentSyntax)?.NameColon != null)
{
return (argument as ArgumentSyntax).NameColon.Name.Identifier.ValueText;
}
return string.Empty;
}
}
}
......@@ -156,6 +156,12 @@ internal interface ISyntaxFactsService : ILanguageService
IEnumerable<SyntaxNode> GetConstructors(SyntaxNode root, CancellationToken cancellationToken);
bool TryGetCorrespondingOpenBrace(SyntaxToken token, out SyntaxToken openBrace);
/// <summary>
/// Given a <see cref="SyntaxNode"/>, that represents and argument return the string representation of
/// that arguments name.
/// </summary>
string GetNameForArgument(SyntaxNode argument);
}
[Flags]
......
......@@ -1177,5 +1177,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return Nothing
End Function
End Class
Public Function GetNameForArgument(argument As SyntaxNode) As String Implements ISyntaxFactsService.GetNameForArgument
If TryCast(argument, ArgumentSyntax)?.IsNamed Then
Return DirectCast(argument, SimpleArgumentSyntax).NameColonEquals.Name.Identifier.ValueText
End If
Return String.Empty
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册