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

Correctly detect existing constructors during code generation

上级 7cb4f224
...@@ -173,5 +173,12 @@ public async Task TestContextualKeywordName() ...@@ -173,5 +173,12 @@ public async Task TestContextualKeywordName()
@"class Program { [|int yield ;|] } ", @"class Program { [|int yield ;|] } ",
@"class Program { int yield ; public Program ( int yield ) { this . yield = 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 ...@@ -910,6 +910,11 @@ public TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree tree, int positio
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public string GetNameForArgument(SyntaxNode argument)
{
throw new NotImplementedException();
}
} }
} }
} }
......
...@@ -816,6 +816,10 @@ End Class]]></a>.Value.NormalizeLineEndings() ...@@ -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 Public Function GetInactiveRegionSpanAroundPosition(tree As SyntaxTree, position As Integer, cancellationToken As CancellationToken) As TextSpan Implements ISyntaxFactsService.GetInactiveRegionSpanAroundPosition
Throw New NotImplementedException() Throw New NotImplementedException()
End Function End Function
Public Function GetNameForArgument(argument As SyntaxNode) As String Implements ISyntaxFactsService.GetNameForArgument
Throw New NotImplementedException()
End Function
End Class End Class
End Class End Class
End Namespace End Namespace
...@@ -578,6 +578,24 @@ End Class ...@@ -578,6 +578,24 @@ End Class
Public Class [|;;|]Derived Public Class [|;;|]Derived
Inherits Base 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 Class")
End Function End Function
End Class 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. // 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.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading; using System.Threading;
...@@ -10,7 +11,6 @@ ...@@ -10,7 +11,6 @@
using Microsoft.CodeAnalysis.LanguageServices; using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Shared.Utilities;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor namespace Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor
{ {
...@@ -93,23 +93,74 @@ private State() ...@@ -93,23 +93,74 @@ private State()
this.ParameterTypes = this.ParameterTypes ?? GetParameterTypes(service, document, cancellationToken); this.ParameterTypes = this.ParameterTypes ?? GetParameterTypes(service, document, cancellationToken);
this.ParameterRefKinds = this.ParameterRefKinds ?? this.Arguments.Select(service.GetRefKind).ToList(); 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( if (this.ParameterTypes == null ||
attributes: null, this.ParameterRefKinds == null)
refKind: r, {
isParams: false, return false;
type: t, }
name: string.Empty)).ToList();
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 destinationProvider = document.Project.Solution.Workspace.Services.GetLanguageServices(this.TypeToGenerateIn.Language);
var syntaxFacts = destinationProvider.GetService<ISyntaxFactsService>(); 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( return service.GetNameForArgument(this.Arguments?[i]);
c => SignatureComparer.Instance.HaveSameSignature(parameters, c.Parameters, compareParameterName: true, isCaseSensitive: syntaxFacts.IsCaseSensitive));
} }
internal List<ITypeSymbol> GetParameterTypes( internal List<ITypeSymbol> GetParameterTypes(
......
...@@ -28,7 +28,6 @@ protected AbstractGenerateConstructorService() ...@@ -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 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 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 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 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 virtual IList<string> GenerateParameterNames(SemanticModel semanticModel, IEnumerable<TAttributeArgumentSyntax> arguments, IList<string> reservedNames = null) { return null; }
protected abstract string GenerateNameForArgument(SemanticModel semanticModel, TArgumentSyntax argument); protected abstract string GenerateNameForArgument(SemanticModel semanticModel, TArgumentSyntax argument);
......
...@@ -1429,5 +1429,15 @@ public TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree syntaxTree, int p ...@@ -1429,5 +1429,15 @@ public TextSpan GetInactiveRegionSpanAroundPosition(SyntaxTree syntaxTree, int p
return default(TextSpan); 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 ...@@ -156,6 +156,12 @@ internal interface ISyntaxFactsService : ILanguageService
IEnumerable<SyntaxNode> GetConstructors(SyntaxNode root, CancellationToken cancellationToken); IEnumerable<SyntaxNode> GetConstructors(SyntaxNode root, CancellationToken cancellationToken);
bool TryGetCorrespondingOpenBrace(SyntaxToken token, out SyntaxToken openBrace); 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] [Flags]
......
...@@ -1177,5 +1177,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic ...@@ -1177,5 +1177,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return Nothing Return Nothing
End Function 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 End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册