提交 421121ce 编写于 作者: C CyrusNajmabadi

Merge pull request #6659 from CyrusNajmabadi/generateConstructor

Offer 'generate constructor' when a derived type doesn't include a delegating constructor to it's base type
...@@ -420,7 +420,7 @@ public void TestReadonlyFieldDelegation() ...@@ -420,7 +420,7 @@ public void TestReadonlyFieldDelegation()
@"class C { private readonly int x ; public C ( int x ) { this . x = x ; } void Test ( ) { int x = 10 ; C c = new C ( x ) ; } } "); @"class C { private readonly int x ; public C ( int x ) { this . x = x ; } void Test ( ) { int x = 10 ; C c = new C ( x ) ; } } ");
} }
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestNoGenerationIntoEntirelyHiddenType() public void TestNoGenerationIntoEntirelyHiddenType()
{ {
TestMissing( TestMissing(
...@@ -441,7 +441,7 @@ class D ...@@ -441,7 +441,7 @@ class D
"); ");
} }
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestNestedConstructorCall() public void TestNestedConstructorCall()
{ {
Test( Test(
...@@ -569,7 +569,7 @@ public void TestAttributesWithLambda() ...@@ -569,7 +569,7 @@ public void TestAttributesWithLambda()
} }
[WorkItem(889349)] [WorkItem(889349)]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestConstructorGenerationForDifferentNamedParameter() public void TestConstructorGenerationForDifferentNamedParameter()
{ {
Test( Test(
...@@ -611,7 +611,7 @@ public Program(int wde) ...@@ -611,7 +611,7 @@ public Program(int wde)
} }
[WorkItem(528257)] [WorkItem(528257)]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestGenerateInInaccessibleType() public void TestGenerateInInaccessibleType()
{ {
Test( Test(
...@@ -628,7 +628,7 @@ public partial class GenerateConstructorTestsWithFindMissingIdentifiersAnalyzer ...@@ -628,7 +628,7 @@ public partial class GenerateConstructorTestsWithFindMissingIdentifiersAnalyzer
} }
[WorkItem(1241, @"https://github.com/dotnet/roslyn/issues/1241")] [WorkItem(1241, @"https://github.com/dotnet/roslyn/issues/1241")]
[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestGenerateConstructorInIncompleteLambda() public void TestGenerateConstructorInIncompleteLambda()
{ {
Test( Test(
...@@ -638,7 +638,7 @@ public void TestGenerateConstructorInIncompleteLambda() ...@@ -638,7 +638,7 @@ public void TestGenerateConstructorInIncompleteLambda()
} }
[WorkItem(5274, "https://github.com/dotnet/roslyn/issues/5274")] [WorkItem(5274, "https://github.com/dotnet/roslyn/issues/5274")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestGenerateIntoDerivedClassWithAbstractBase() public void TestGenerateIntoDerivedClassWithAbstractBase()
{ {
Test( Test(
...@@ -691,6 +691,70 @@ public Base(bool val = false) ...@@ -691,6 +691,70 @@ public Base(bool val = false)
_val = val; _val = val;
} }
} }
}");
}
[WorkItem(6541, "https://github.com/dotnet/Roslyn/issues/6541")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestGenerateFromDerivedClass()
{
Test(
@"
class Base
{
public Base(string value)
{
}
}
class [||]Derived : Base
{
}",
@"
class Base
{
public Base(string value)
{
}
}
class Derived : Base
{
public Derived(string value) : base(value)
{
}
}");
}
[WorkItem(6541, "https://github.com/dotnet/Roslyn/issues/6541")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)]
public void TestGenerateFromDerivedClass2()
{
Test(
@"
class Base
{
public Base(int a, string value = null)
{
}
}
class [||]Derived : Base
{
}",
@"
class Base
{
public Base(int a, string value = null)
{
}
}
class Derived : Base
{
public Derived(int a, string value = null) : base(a, value)
{
}
}"); }");
} }
} }
......
...@@ -501,5 +501,66 @@ NewLines("Imports System.Linq \n Class C \n Private v As Integer \n Sub New() \n ...@@ -501,5 +501,66 @@ NewLines("Imports System.Linq \n Class C \n Private v As Integer \n Sub New() \n
End Sub End Sub
End Class End Class
<WorkItem(6541, "https://github.com/dotnet/Roslyn/issues/6541")>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)>
Public Sub TestGenerateInDerivedType1()
Test(
"
Public Class Base
Public Sub New(a As String)
End Sub
End Class
Public Class [||]Derived
Inherits Base
End Class",
"
Public Class Base
Public Sub New(a As String)
End Sub
End Class
Public Class Derived
Inherits Base
Public Sub New(a As String)
MyBase.New(a)
End Sub
End Class")
End Sub
<WorkItem(6541, "https://github.com/dotnet/Roslyn/issues/6541")>
<WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateConstructor)>
Public Sub TestGenerateInDerivedType2()
Test(
"
Public Class Base
Public Sub New(a As Integer, Optional b As String = Nothing)
End Sub
End Class
Public Class [||]Derived
Inherits Base
End Class",
"
Public Class Base
Public Sub New(a As Integer, Optional b As String = Nothing)
End Sub
End Class
Public Class Derived
Inherits Base
Public Sub New(a As Integer, Optional b As String = Nothing)
MyBase.New(a, b)
End Sub
End Class")
End Sub
End Class End Class
End Namespace End Namespace
...@@ -36,9 +36,17 @@ protected override Task<IEnumerable<CodeAction>> GetCodeActionsAsync(Document do ...@@ -36,9 +36,17 @@ protected override Task<IEnumerable<CodeAction>> GetCodeActionsAsync(Document do
return service.GenerateConstructorAsync(document, node, cancellationToken); return service.GenerateConstructorAsync(document, node, cancellationToken);
} }
protected override bool IsCandidate(SyntaxNode node) protected override bool IsCandidate(SyntaxNode node, Diagnostic diagnostic)
{ {
return node is SimpleNameSyntax || node is ObjectCreationExpressionSyntax || node is ConstructorInitializerSyntax || node is AttributeSyntax; if (node is SimpleNameSyntax ||
node is ObjectCreationExpressionSyntax ||
node is ConstructorInitializerSyntax ||
node is AttributeSyntax)
{
return true;
}
return diagnostic.Id == CS7036 && node is ClassDeclarationSyntax;
} }
protected override SyntaxNode GetTargetNode(SyntaxNode node) protected override SyntaxNode GetTargetNode(SyntaxNode node)
......
...@@ -31,7 +31,7 @@ protected override Task<IEnumerable<CodeAction>> GetCodeActionsAsync(Document do ...@@ -31,7 +31,7 @@ protected override Task<IEnumerable<CodeAction>> GetCodeActionsAsync(Document do
return service.GenerateEnumMemberAsync(document, node, cancellationToken); return service.GenerateEnumMemberAsync(document, node, cancellationToken);
} }
protected override bool IsCandidate(SyntaxNode node) protected override bool IsCandidate(SyntaxNode node, Diagnostic diagnostic)
{ {
return node is IdentifierNameSyntax; return node is IdentifierNameSyntax;
} }
......
...@@ -27,7 +27,7 @@ public override ImmutableArray<string> FixableDiagnosticIds ...@@ -27,7 +27,7 @@ public override ImmutableArray<string> FixableDiagnosticIds
get { return ImmutableArray.Create(CS0029, CS0030); } get { return ImmutableArray.Create(CS0029, CS0030); }
} }
protected override bool IsCandidate(SyntaxNode node) protected override bool IsCandidate(SyntaxNode node, Diagnostic diagnostic)
{ {
return node.IsKind(SyntaxKind.IdentifierName) || return node.IsKind(SyntaxKind.IdentifierName) ||
node.IsKind(SyntaxKind.MethodDeclaration) || node.IsKind(SyntaxKind.MethodDeclaration) ||
......
...@@ -37,7 +37,7 @@ public override ImmutableArray<string> FixableDiagnosticIds ...@@ -37,7 +37,7 @@ public override ImmutableArray<string> FixableDiagnosticIds
get { return ImmutableArray.Create(CS0103, CS1061, CS0117, CS0122, CS0539, CS1501, CS1503, CS0305, CS0308, CS1660, CS1739, CS7036); } get { return ImmutableArray.Create(CS0103, CS1061, CS0117, CS0122, CS0539, CS1501, CS1503, CS0305, CS0308, CS1660, CS1739, CS7036); }
} }
protected override bool IsCandidate(SyntaxNode node) protected override bool IsCandidate(SyntaxNode node, Diagnostic diagnostic)
{ {
return node.IsKind(SyntaxKind.IdentifierName) || return node.IsKind(SyntaxKind.IdentifierName) ||
node.IsKind(SyntaxKind.MethodDeclaration) || node.IsKind(SyntaxKind.MethodDeclaration) ||
......
...@@ -33,7 +33,7 @@ public override ImmutableArray<string> FixableDiagnosticIds ...@@ -33,7 +33,7 @@ public override ImmutableArray<string> FixableDiagnosticIds
get { return ImmutableArray.Create(CS0103, CS0117, CS0234, CS0246, CS0305, CS0308, CS0426, CS0616); } get { return ImmutableArray.Create(CS0103, CS0117, CS0234, CS0246, CS0305, CS0308, CS0426, CS0616); }
} }
protected override bool IsCandidate(SyntaxNode node) protected override bool IsCandidate(SyntaxNode node, Diagnostic diagnostic)
{ {
var qualified = node as QualifiedNameSyntax; var qualified = node as QualifiedNameSyntax;
if (qualified != null) if (qualified != null)
......
...@@ -30,7 +30,7 @@ public override ImmutableArray<string> FixableDiagnosticIds ...@@ -30,7 +30,7 @@ public override ImmutableArray<string> FixableDiagnosticIds
get { return ImmutableArray.Create(CS1061, CS0103, CS0117, CS0539, CS0246); } get { return ImmutableArray.Create(CS1061, CS0103, CS0117, CS0539, CS0246); }
} }
protected override bool IsCandidate(SyntaxNode node) protected override bool IsCandidate(SyntaxNode node, Diagnostic diagnostic)
{ {
return node is SimpleNameSyntax || node is PropertyDeclarationSyntax || node is MemberBindingExpressionSyntax; return node is SimpleNameSyntax || node is PropertyDeclarationSyntax || node is MemberBindingExpressionSyntax;
} }
......
// 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.Composition; using System.Composition;
using System.Linq; using System.Linq;
...@@ -29,6 +30,11 @@ protected override bool IsConstructorInitializerGeneration(SemanticDocument docu ...@@ -29,6 +30,11 @@ protected override bool IsConstructorInitializerGeneration(SemanticDocument docu
return node is ConstructorInitializerSyntax; return node is ConstructorInitializerSyntax;
} }
protected override bool IsClassDeclarationGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken)
{
return node is ClassDeclarationSyntax;
}
protected override bool TryInitializeConstructorInitializerGeneration( protected override bool TryInitializeConstructorInitializerGeneration(
SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken, SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken,
out SyntaxToken token, out IList<ArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn) out SyntaxToken token, out IList<ArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn)
...@@ -54,6 +60,35 @@ protected override bool IsConstructorInitializerGeneration(SemanticDocument docu ...@@ -54,6 +60,35 @@ protected override bool IsConstructorInitializerGeneration(SemanticDocument docu
return false; return false;
} }
protected override bool TryInitializeClassDeclarationGenerationState(
SemanticDocument document,
SyntaxNode node,
CancellationToken cancellationToken,
out SyntaxToken token,
out IMethodSymbol delegatedConstructor,
out INamedTypeSymbol typeToGenerateIn)
{
token = default(SyntaxToken);
typeToGenerateIn = null;
delegatedConstructor = null;
var semanticModel = document.SemanticModel;
var classDeclaration = (ClassDeclarationSyntax)node;
var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration, cancellationToken);
var baseType = classSymbol.BaseType;
var constructor = baseType.Constructors.FirstOrDefault(c => IsSymbolAccessible(c, document));
if (constructor == null)
{
return false;
}
typeToGenerateIn = classSymbol;
delegatedConstructor = constructor;
token = classDeclaration.Identifier;
return true;
}
protected override bool TryInitializeSimpleNameGenerationState( protected override bool TryInitializeSimpleNameGenerationState(
SemanticDocument document, SemanticDocument document,
SyntaxNode node, SyntaxNode node,
......
...@@ -22,8 +22,9 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) ...@@ -22,8 +22,9 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
return; return;
} }
var diagnostic = context.Diagnostics.First();
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var names = GetTargetNodes(root, context.Span); var names = GetTargetNodes(root, context.Span, diagnostic);
foreach (var name in names) foreach (var name in names)
{ {
var codeActions = await GetCodeActionsAsync(context.Document, name, context.CancellationToken).ConfigureAwait(false); var codeActions = await GetCodeActionsAsync(context.Document, name, context.CancellationToken).ConfigureAwait(false);
...@@ -44,12 +45,12 @@ protected virtual SyntaxNode GetTargetNode(SyntaxNode node) ...@@ -44,12 +45,12 @@ protected virtual SyntaxNode GetTargetNode(SyntaxNode node)
return node; return node;
} }
protected virtual bool IsCandidate(SyntaxNode node) protected virtual bool IsCandidate(SyntaxNode node, Diagnostic diagnostic)
{ {
return false; return false;
} }
protected virtual IEnumerable<SyntaxNode> GetTargetNodes(SyntaxNode root, TextSpan span) protected virtual IEnumerable<SyntaxNode> GetTargetNodes(SyntaxNode root, TextSpan span, Diagnostic diagnostic)
{ {
var token = root.FindToken(span.Start); var token = root.FindToken(span.Start);
if (!token.Span.IntersectsWith(span)) if (!token.Span.IntersectsWith(span))
...@@ -57,7 +58,7 @@ protected virtual IEnumerable<SyntaxNode> GetTargetNodes(SyntaxNode root, TextSp ...@@ -57,7 +58,7 @@ protected virtual IEnumerable<SyntaxNode> GetTargetNodes(SyntaxNode root, TextSp
yield break; yield break;
} }
var nodes = token.GetAncestors<SyntaxNode>().Where(IsCandidate); var nodes = token.GetAncestors<SyntaxNode>().Where(n => IsCandidate(n, diagnostic));
foreach (var node in nodes) foreach (var node in nodes)
{ {
var name = GetTargetNode(node); var name = GetTargetNode(node);
......
...@@ -41,22 +41,28 @@ private partial class Editor ...@@ -41,22 +41,28 @@ private partial class Editor
internal async Task<Document> GetEditAsync() internal async Task<Document> GetEditAsync()
{ {
// First, see if there's an accessible base constructor that would accept these // First, if we were just given the constructor and the type to generate it into
// then just go generat that constructor. There's nothing special we need to do.
var edit = await GenerateDelegatedConstructorAsync().ConfigureAwait(false);
if (edit != null)
{
return edit;
}
// then, see if there's an accessible base constructor that would accept these
// types, then just call into that instead of generating fields. // types, then just call into that instead of generating fields.
// //
// then, see if there are any constructors that would take the first 'n' arguments // then, see if there are any constructors that would take the first 'n' arguments
// we've provided. If so, delegate to those, and then create a field for any // we've provided. If so, delegate to those, and then create a field for any
// remaining arguments. Try to match from largest to smallest. // remaining arguments. Try to match from largest to smallest.
// edit = await GenerateThisOrBaseDelegatingConstructorAsync().ConfigureAwait(false);
// Otherwise, just generate a normal constructor that assigns any provided
// parameters into fields.
var edit = await GenerateThisOrBaseDelegatingConstructorAsync().ConfigureAwait(false);
if (edit != null) if (edit != null)
{ {
return edit; return edit;
} }
// Otherwise, just generate a normal constructor that assigns any provided
// parameters into fields.
return await GenerateFieldDelegatingConstructorAsync().ConfigureAwait(false); return await GenerateFieldDelegatingConstructorAsync().ConfigureAwait(false);
} }
...@@ -88,6 +94,51 @@ private async Task<Document> GenerateThisOrBaseDelegatingConstructorAsync(int ar ...@@ -88,6 +94,51 @@ private async Task<Document> GenerateThisOrBaseDelegatingConstructorAsync(int ar
return null; return null;
} }
private async Task<Document> GenerateDelegatedConstructorAsync()
{
var delegatedConstructor = _state.DelegatedConstructorOpt;
if (delegatedConstructor == null)
{
return null;
}
var namedType = _state.TypeToGenerateIn;
if (namedType == null)
{
return null;
}
// There was a best match. Call it directly.
var provider = _document.Project.Solution.Workspace.Services.GetLanguageServices(_state.TypeToGenerateIn.Language);
var syntaxFactory = provider.GetService<SyntaxGenerator>();
var codeGenerationService = provider.GetService<ICodeGenerationService>();
var isThis = namedType.Equals(delegatedConstructor.ContainingType);
var delegatingArguments = syntaxFactory.CreateArguments(delegatedConstructor.Parameters);
var baseConstructorArguments = isThis ? null : delegatingArguments;
var thisConstructorArguments = isThis ? delegatingArguments : null;
var constructor = CodeGenerationSymbolFactory.CreateConstructorSymbol(
attributes: null,
accessibility: Accessibility.Public,
modifiers: default(DeclarationModifiers),
typeName: _state.TypeToGenerateIn.Name,
parameters: delegatedConstructor.Parameters,
baseConstructorArguments: baseConstructorArguments,
thisConstructorArguments: thisConstructorArguments);
var result = await codeGenerationService.AddMembersAsync(
_document.Project.Solution,
_state.TypeToGenerateIn,
new List<ISymbol> { constructor },
new CodeGenerationOptions(_state.Token.GetLocation()),
_cancellationToken)
.ConfigureAwait(false);
return result;
}
private async Task<Document> GenerateDelegatingConstructorAsync( private async Task<Document> GenerateDelegatingConstructorAsync(
int argumentCount, int argumentCount,
INamedTypeSymbol namedType) INamedTypeSymbol namedType)
......
...@@ -25,15 +25,17 @@ protected internal class State ...@@ -25,15 +25,17 @@ protected internal class State
// The type we're creating a constructor for. Will be a class or struct type. // The type we're creating a constructor for. Will be a class or struct type.
public INamedTypeSymbol TypeToGenerateIn { get; private set; } public INamedTypeSymbol TypeToGenerateIn { get; private set; }
public IList<ITypeSymbol> ParameterTypes { get; private set; }
public IList<RefKind> ParameterRefKinds { get; private set; } public IList<RefKind> ParameterRefKinds { get; private set; }
public IList<ITypeSymbol> ParameterTypes { get; private set; }
public IMethodSymbol DelegatedConstructorOpt { get; private set; }
public SyntaxToken Token { get; private set; } public SyntaxToken Token { get; private set; }
public bool IsConstructorInitializerGeneration { get; private set; } public bool IsConstructorInitializerGeneration { get; private set; }
private State() private State()
{ {
this.IsConstructorInitializerGeneration = false;
} }
public static async Task<State> GenerateAsync( public static async Task<State> GenerateAsync(
...@@ -71,6 +73,13 @@ private State() ...@@ -71,6 +73,13 @@ private State()
return false; return false;
} }
} }
else if (service.IsClassDeclarationGeneration(document, node, cancellationToken))
{
if (!await TryInitializeClassDeclarationGenerationAsync(service, document, node, cancellationToken).ConfigureAwait(false))
{
return false;
}
}
else else
{ {
return false; return false;
...@@ -82,7 +91,7 @@ private State() ...@@ -82,7 +91,7 @@ private State()
} }
this.ParameterTypes = this.ParameterTypes ?? GetParameterTypes(service, document, cancellationToken); this.ParameterTypes = this.ParameterTypes ?? GetParameterTypes(service, document, cancellationToken);
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(service, document, cancellationToken);
} }
...@@ -99,7 +108,8 @@ private bool ClashesWithExistingConstructor(TService service, SemanticDocument d ...@@ -99,7 +108,8 @@ private bool ClashesWithExistingConstructor(TService service, SemanticDocument d
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>();
return this.TypeToGenerateIn.InstanceConstructors.Any(c => SignatureComparer.Instance.HaveSameSignature(parameters, c.Parameters, compareParameterName: true, isCaseSensitive: syntaxFacts.IsCaseSensitive)); return this.TypeToGenerateIn.InstanceConstructors.Any(
c => SignatureComparer.Instance.HaveSameSignature(parameters, c.Parameters, compareParameterName: true, isCaseSensitive: syntaxFacts.IsCaseSensitive));
} }
internal List<ITypeSymbol> GetParameterTypes( internal List<ITypeSymbol> GetParameterTypes(
...@@ -155,6 +165,28 @@ private ITypeSymbol FixType(ITypeSymbol typeSymbol, SemanticModel semanticModel, ...@@ -155,6 +165,28 @@ private ITypeSymbol FixType(ITypeSymbol typeSymbol, SemanticModel semanticModel,
return await TryDetermineTypeToGenerateInAsync(document, typeToGenerateIn, cancellationToken).ConfigureAwait(false); return await TryDetermineTypeToGenerateInAsync(document, typeToGenerateIn, cancellationToken).ConfigureAwait(false);
} }
private async Task<bool> TryInitializeClassDeclarationGenerationAsync(
TService service,
SemanticDocument document,
SyntaxNode simpleName,
CancellationToken cancellationToken)
{
SyntaxToken token;
INamedTypeSymbol typeToGenerateIn;
IMethodSymbol constructor;
if (service.TryInitializeClassDeclarationGenerationState(document, simpleName, cancellationToken,
out token, out constructor, out typeToGenerateIn))
{
this.Token = token;
this.DelegatedConstructorOpt = constructor;
this.ParameterTypes = constructor.Parameters.Select(p => p.Type).ToList();
this.ParameterRefKinds = constructor.Parameters.Select(p => p.RefKind).ToList();
}
cancellationToken.ThrowIfCancellationRequested();
return await TryDetermineTypeToGenerateInAsync(document, typeToGenerateIn, cancellationToken).ConfigureAwait(false);
}
private async Task<bool> TryInitializeSimpleNameGenerationAsync( private async Task<bool> TryInitializeSimpleNameGenerationAsync(
TService service, TService service,
SemanticDocument document, SemanticDocument document,
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Internal.Log; using Microsoft.CodeAnalysis.Internal.Log;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities; using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor namespace Microsoft.CodeAnalysis.GenerateMember.GenerateConstructor
...@@ -20,10 +21,12 @@ protected AbstractGenerateConstructorService() ...@@ -20,10 +21,12 @@ protected AbstractGenerateConstructorService()
} }
protected abstract bool IsSimpleNameGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken); protected abstract bool IsSimpleNameGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken);
protected abstract bool IsClassDeclarationGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken);
protected abstract bool IsConstructorInitializerGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken); protected abstract bool IsConstructorInitializerGeneration(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken);
protected abstract bool TryInitializeConstructorInitializerGeneration(SemanticDocument document, SyntaxNode constructorInitializer, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn);
protected abstract bool TryInitializeSimpleNameGenerationState(SemanticDocument document, SyntaxNode simpleName, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, out INamedTypeSymbol typeToGenerateIn); protected abstract bool TryInitializeSimpleNameGenerationState(SemanticDocument document, SyntaxNode simpleName, CancellationToken cancellationToken, out SyntaxToken token, out IList<TArgumentSyntax> arguments, 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 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);
...@@ -55,5 +58,41 @@ private IEnumerable<CodeAction> GetActions(Document document, State state) ...@@ -55,5 +58,41 @@ private IEnumerable<CodeAction> GetActions(Document document, State state)
{ {
yield return new GenerateConstructorCodeAction((TService)this, document, state); yield return new GenerateConstructorCodeAction((TService)this, document, state);
} }
protected static bool IsSymbolAccessible(
ISymbol symbol, SemanticDocument document)
{
if (symbol == null)
{
return false;
}
if (symbol.Kind == SymbolKind.Property)
{
if (!IsSymbolAccessible(((IPropertySymbol)symbol).SetMethod, document))
{
return false;
}
}
// Public and protected constructors are accessible. Internal constructors are
// accessible if we have friend access. We can't call the normal accessibility
// checkers since they will think that a protected constructor isn't accessible
// (since we don't have the destination type that would have access to them yet).
switch (symbol.DeclaredAccessibility)
{
case Accessibility.ProtectedOrInternal:
case Accessibility.Protected:
case Accessibility.Public:
return true;
case Accessibility.ProtectedAndInternal:
case Accessibility.Internal:
return document.SemanticModel.Compilation.Assembly.IsSameAssemblyOrHasFriendAccessTo(
symbol.ContainingAssembly);
default:
return false;
}
}
} }
} }
...@@ -22,10 +22,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateConstructor ...@@ -22,10 +22,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateConstructor
Friend Const BC30455 As String = "BC30455" ' error BC30455: Argument not specified for parameter 'x' of 'Public Sub New(x As Integer)'. Friend Const BC30455 As String = "BC30455" ' error BC30455: Argument not specified for parameter 'x' of 'Public Sub New(x As Integer)'.
Friend Const BC30512 As String = "BC30512" ' error BC30512: Option Strict On disallows implicit conversions from 'Object' to 'Integer'. Friend Const BC30512 As String = "BC30512" ' error BC30512: Option Strict On disallows implicit conversions from 'Object' to 'Integer'.
Friend Const BC32006 As String = "BC32006" ' error BC32006: 'Char' values cannot be converted to 'Integer'. Friend Const BC32006 As String = "BC32006" ' error BC32006: 'Char' values cannot be converted to 'Integer'.
Friend Const BC30387 As String = "BC30387" ' error BC32006: Class 'Derived' must declare a 'Sub New' because its base class 'Base' does not have an accessible 'Sub New' that can be called with no arguments.
Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String) Public Overrides ReadOnly Property FixableDiagnosticIds As ImmutableArray(Of String)
Get Get
Return ImmutableArray.Create(BC30057, BC30272, BC30274, BC30389, BC30455, BC32006, BC30512) Return ImmutableArray.Create(BC30057, BC30272, BC30274, BC30389, BC30455, BC32006, BC30512, BC30387)
End Get End Get
End Property End Property
...@@ -53,8 +54,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateConstructor ...@@ -53,8 +54,12 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateConstructor
Return node Return node
End Function End Function
Protected Overrides Function IsCandidate(node As SyntaxNode) As Boolean Protected Overrides Function IsCandidate(node As SyntaxNode, diagnostic As Diagnostic) As Boolean
Return TypeOf node Is SimpleNameSyntax OrElse TypeOf node Is InvocationExpressionSyntax OrElse TypeOf node Is ObjectCreationExpressionSyntax OrElse TypeOf node Is AttributeSyntax If TypeOf node Is SimpleNameSyntax OrElse TypeOf node Is InvocationExpressionSyntax OrElse TypeOf node Is ObjectCreationExpressionSyntax OrElse TypeOf node Is AttributeSyntax Then
Return True
End If
Return diagnostic.Id = BC30387 AndAlso TypeOf node Is ClassBlockSyntax
End Function End Function
End Class End Class
End Namespace End Namespace
...@@ -28,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEnumMember ...@@ -28,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateEnumMember
Return service.GenerateEnumMemberAsync(document, node, cancellationToken) Return service.GenerateEnumMemberAsync(document, node, cancellationToken)
End Function End Function
Protected Overrides Function IsCandidate(node As SyntaxNode) As Boolean Protected Overrides Function IsCandidate(node As SyntaxNode, diagnostic As Diagnostic) As Boolean
Return TypeOf node Is MemberAccessExpressionSyntax Return TypeOf node Is MemberAccessExpressionSyntax
End Function End Function
......
...@@ -28,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod ...@@ -28,7 +28,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod
Return service.GenerateConversionAsync(document, node, cancellationToken) Return service.GenerateConversionAsync(document, node, cancellationToken)
End Function End Function
Protected Overrides Function IsCandidate(node As SyntaxNode) As Boolean Protected Overrides Function IsCandidate(node As SyntaxNode, diagnostic As Diagnostic) As Boolean
Return TypeOf node Is QualifiedNameSyntax OrElse Return TypeOf node Is QualifiedNameSyntax OrElse
TypeOf node Is SimpleNameSyntax OrElse TypeOf node Is SimpleNameSyntax OrElse
TypeOf node Is MemberAccessExpressionSyntax OrElse TypeOf node Is MemberAccessExpressionSyntax OrElse
......
...@@ -42,7 +42,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod ...@@ -42,7 +42,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateMethod
Return service.GenerateMethodAsync(document, node, cancellationToken) Return service.GenerateMethodAsync(document, node, cancellationToken)
End Function End Function
Protected Overrides Function IsCandidate(node As SyntaxNode) As Boolean Protected Overrides Function IsCandidate(node As SyntaxNode, diagnostic As Diagnostic) As Boolean
Return TypeOf node Is QualifiedNameSyntax OrElse Return TypeOf node Is QualifiedNameSyntax OrElse
TypeOf node Is SimpleNameSyntax OrElse TypeOf node Is SimpleNameSyntax OrElse
TypeOf node Is MemberAccessExpressionSyntax OrElse TypeOf node Is MemberAccessExpressionSyntax OrElse
......
...@@ -36,7 +36,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateType ...@@ -36,7 +36,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateType
Return service.GenerateTypeAsync(document, node, cancellationToken) Return service.GenerateTypeAsync(document, node, cancellationToken)
End Function End Function
Protected Overrides Function IsCandidate(node As SyntaxNode) As Boolean Protected Overrides Function IsCandidate(node As SyntaxNode, diagnostic As Diagnostic) As Boolean
Dim qualified = TryCast(node, QualifiedNameSyntax) Dim qualified = TryCast(node, QualifiedNameSyntax)
If qualified IsNot Nothing Then If qualified IsNot Nothing Then
Return True Return True
......
...@@ -31,7 +31,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateVariable ...@@ -31,7 +31,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateVariable
Return service.GenerateVariableAsync(document, node, cancellationToken) Return service.GenerateVariableAsync(document, node, cancellationToken)
End Function End Function
Protected Overrides Function IsCandidate(node As SyntaxNode) As Boolean Protected Overrides Function IsCandidate(node As SyntaxNode, diagnostic As Diagnostic) As Boolean
If TypeOf node Is QualifiedNameSyntax OrElse TypeOf node Is MemberAccessExpressionSyntax Then If TypeOf node Is QualifiedNameSyntax OrElse TypeOf node Is MemberAccessExpressionSyntax Then
Return True Return True
End If End If
......
...@@ -18,7 +18,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateConstructor ...@@ -18,7 +18,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateConstructor
End Function End Function
Protected Overrides Function GenerateParameterNames(semanticModel As SemanticModel, arguments As IEnumerable(Of ArgumentSyntax), Optional reservedNames As IList(Of String) = Nothing) As IList(Of String) Protected Overrides Function GenerateParameterNames(semanticModel As SemanticModel, arguments As IEnumerable(Of ArgumentSyntax), Optional reservedNames As IList(Of String) = Nothing) As IList(Of String)
Return semanticModel.GenerateParameterNames(arguments.ToList(), reservedNames) Return semanticModel.GenerateParameterNames(arguments?.ToList(), reservedNames)
End Function End Function
Protected Overrides Function GetArgumentType(semanticModel As SemanticModel, argument As ArgumentSyntax, cancellationToken As CancellationToken) As Microsoft.CodeAnalysis.ITypeSymbol Protected Overrides Function GetArgumentType(semanticModel As SemanticModel, argument As ArgumentSyntax, cancellationToken As CancellationToken) As Microsoft.CodeAnalysis.ITypeSymbol
...@@ -146,6 +146,35 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateConstructor ...@@ -146,6 +146,35 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateConstructor
Return compilation.ClassifyConversion(sourceType, targetType).IsWidening Return compilation.ClassifyConversion(sourceType, targetType).IsWidening
End Function End Function
Protected Overrides Function IsClassDeclarationGeneration(document As SemanticDocument,
node As SyntaxNode,
cancellationToken As CancellationToken) As Boolean
Return TypeOf node Is ClassBlockSyntax
End Function
Protected Overrides Function TryInitializeClassDeclarationGenerationState(
document As SemanticDocument,
classDeclaration As SyntaxNode,
cancellationToken As CancellationToken,
ByRef token As SyntaxToken,
ByRef delegatedConstructor As IMethodSymbol,
ByRef typeToGenerateIn As INamedTypeSymbol) As Boolean
Dim semanticModel = document.SemanticModel
Dim classBlock = DirectCast(classDeclaration, ClassBlockSyntax)
Dim classSymbol = semanticModel.GetDeclaredSymbol(classBlock.BlockStatement, cancellationToken)
Dim baseType = classSymbol.BaseType
Dim constructor = baseType.Constructors.FirstOrDefault(Function(c) IsSymbolAccessible(c, document))
If constructor Is Nothing Then
Return False
End If
typeToGenerateIn = classSymbol
delegatedConstructor = constructor
token = classBlock.BlockStatement.Identifier
Return True
End Function
Private Shared ReadOnly s_annotation As SyntaxAnnotation = New SyntaxAnnotation Private Shared ReadOnly s_annotation As SyntaxAnnotation = New SyntaxAnnotation
Friend Overrides Function GetDelegatingConstructor(state As State, Friend Overrides Function GetDelegatingConstructor(state As State,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册