提交 be4f77ad 编写于 作者: C CyrusNajmabadi

Fix crash in Implement-Abstract-Class when dealing with partial classes.

上级 40e22cd2
......@@ -382,5 +382,63 @@ public async Task ImplementClassWithInaccessibleMembers()
@"using System ; using System . Globalization ; public class [|x|] : EastAsianLunisolarCalendar { } ",
@"using System ; using System . Globalization ; public class x : EastAsianLunisolarCalendar { public override int [ ] Eras { get { throw new NotImplementedException ( ) ; } } internal override EraInfo [ ] CalEraInfo { get { throw new NotImplementedException ( ) ; } } internal override int MaxCalendarYear { get { throw new NotImplementedException ( ) ; } } internal override DateTime MaxDate { get { throw new NotImplementedException ( ) ; } } internal override int MinCalendarYear { get { throw new NotImplementedException ( ) ; } } internal override DateTime MinDate { get { throw new NotImplementedException ( ) ; } } public override int GetEra ( DateTime time ) { throw new NotImplementedException ( ) ; } internal override int GetGregorianYear ( int year , int era ) { throw new NotImplementedException ( ) ; } internal override int GetYear ( int year , DateTime time ) { throw new NotImplementedException ( ) ; } internal override int GetYearInfo ( int LunarYear , int Index ) { throw new NotImplementedException ( ) ; } } ");
}
[WorkItem(13149, "https://github.com/dotnet/roslyn/issues/13149")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementAbstractClass)]
public async Task TestPartialClass1()
{
await TestAsync(
@"
using System;
public abstract class Base { public abstract void Dispose(); }
partial class [|A|] : Base { }
partial class A { }
",
@"
using System;
public abstract class Base { public abstract void Dispose(); }
partial class A : Base {
public override void Dispose() {
throw new NotImplementedException();
}
}
partial class A { }
");
}
[WorkItem(13149, "https://github.com/dotnet/roslyn/issues/13149")]
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsImplementAbstractClass)]
public async Task TestPartialClass2()
{
await TestAsync(
@"
using System;
public abstract class Base { public abstract void Dispose(); }
partial class [|A|] { }
partial class A : Base { }
",
@"
using System;
public abstract class Base { public abstract void Dispose(); }
partial class A {
public override void Dispose() {
throw new NotImplementedException();
}
}
partial class A : Base { }
");
}
}
}
\ No newline at end of file
......@@ -489,4 +489,4 @@ class C3 : A1, I1
#endregion
}
}
}
\ No newline at end of file
......@@ -30,8 +30,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.ImplementAbstractClass
Return Nothing
End If
Dim classBlock = TryCast(typeSyntax.Parent.Parent, ClassBlockSyntax)
If classBlock Is Nothing Then
Return Nothing
End If
Dim service = document.GetLanguageService(Of IImplementAbstractClassService)()
Dim updatedDocument = service.ImplementAbstractClassAsync(document, document.GetSemanticModelAsync(cancellationToken).WaitAndGetResult(cancellationToken), typeSyntax, cancellationToken).WaitAndGetResult(cancellationToken)
Dim updatedDocument = service.ImplementAbstractClassAsync(document, classBlock, cancellationToken).WaitAndGetResult(cancellationToken)
If updatedDocument IsNot Nothing AndAlso
updatedDocument.GetTextChangesAsync(document, cancellationToken).WaitAndGetResult(cancellationToken).Count = 0 Then
Return Nothing
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Option Strict Off
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.ImplementAbstractClass
......@@ -206,4 +205,4 @@ NewLines("Imports System \n Imports System.Globalization \n Class [|x|] \n Inher
NewLines("Imports System \n Imports System.Globalization \n Class x \n Inherits EastAsianLunisolarCalendar \n Public Overrides ReadOnly Property Eras As Integer() \n Get \n Throw New NotImplementedException() \n End Get \n End Property \n Friend Overrides ReadOnly Property CalEraInfo As EraInfo() \n Get \n Throw New NotImplementedException() \n End Get \n End Property \n Friend Overrides ReadOnly Property MaxCalendarYear As Integer \n Get \n Throw New NotImplementedException() \n End Get \n End Property \n Friend Overrides ReadOnly Property MaxDate As Date \n Get \n Throw New NotImplementedException() \n End Get \n End Property \n Friend Overrides ReadOnly Property MinCalendarYear As Integer \n Get \n Throw New NotImplementedException() \n End Get \n End Property \n Friend Overrides ReadOnly Property MinDate As Date \n Get \n Throw New NotImplementedException() \n End Get \n End Property \n Public Overrides Function GetEra(time As Date) As Integer \n Throw New NotImplementedException() \n End Function \n Friend Overrides Function GetGregorianYear(year As Integer, era As Integer) As Integer \n Throw New NotImplementedException() \n End Function \n Friend Overrides Function GetYear(year As Integer, time As Date) As Integer \n Throw New NotImplementedException() \n End Function \n Friend Overrides Function GetYearInfo(LunarYear As Integer, Index As Integer) As Integer \n Throw New NotImplementedException() \n End Function \n End Class"))
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -19,19 +19,18 @@ internal class ImplementAbstractClassCodeFixProvider : CodeFixProvider
{
private const string CS0534 = nameof(CS0534); // 'Program' does not implement inherited abstract member 'Foo.bar()'
public sealed override ImmutableArray<string> FixableDiagnosticIds
{
get { return ImmutableArray.Create(CS0534); }
}
public sealed override ImmutableArray<string> FixableDiagnosticIds =>
ImmutableArray.Create(CS0534);
public sealed override FixAllProvider GetFixAllProvider()
{
return WellKnownFixAllProviders.BatchFixer;
}
public sealed override FixAllProvider GetFixAllProvider() =>
WellKnownFixAllProviders.BatchFixer;
public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var cancellationToken = context.CancellationToken;
var document = context.Document;
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(context.Span.Start);
if (!token.Span.IntersectsWith(context.Span))
......@@ -45,28 +44,23 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
return;
}
var service = context.Document.GetLanguageService<IImplementAbstractClassService>();
var model = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
foreach (var baseTypeSyntax in classNode.BaseList.Types)
var service = document.GetLanguageService<IImplementAbstractClassService>();
if (await service.CanImplementAbstractClassAsync(
document,
classNode,
cancellationToken).ConfigureAwait(false))
{
var node = baseTypeSyntax.Type;
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
if (service.CanImplementAbstractClass(
context.Document,
model,
node,
context.CancellationToken))
{
var title = CSharpFeaturesResources.Implement_Abstract_Class;
var abstractType = model.GetTypeInfo(node, context.CancellationToken).Type;
var id = GetCodeActionId(abstractType.ContainingAssembly.Name, abstractType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
context.RegisterCodeFix(
new MyCodeAction(title,
c => ImplementAbstractClassAsync(context.Document, node, c),
id),
context.Diagnostics);
return;
}
var classSymbol = semanticModel.GetDeclaredSymbol(classNode);
var abstractType = classSymbol.BaseType;
var id = GetCodeActionId(abstractType.ContainingAssembly.Name, abstractType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
context.RegisterCodeFix(
new MyCodeAction(
c => ImplementAbstractClassAsync(document, classNode, c),
id),
context.Diagnostics);
}
}
......@@ -78,22 +72,19 @@ internal static string GetCodeActionId(string assemblyName, string abstractTypeF
abstractTypeFullyQualifiedName;
}
private async Task<Document> ImplementAbstractClassAsync(Document document, SyntaxNode node, CancellationToken cancellationToken)
private Task<Document> ImplementAbstractClassAsync(
Document document, ClassDeclarationSyntax classNode, CancellationToken cancellationToken)
{
var service = document.GetLanguageService<IImplementAbstractClassService>();
return await service.ImplementAbstractClassAsync(
document,
await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false),
node,
cancellationToken).ConfigureAwait(false);
return service.ImplementAbstractClassAsync(document, classNode, cancellationToken);
}
private class MyCodeAction : CodeAction.DocumentChangeAction
{
public MyCodeAction(string title, Func<CancellationToken, Task<Document>> createChangedDocument, string id) :
base(title, createChangedDocument, id)
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument, string id) :
base(CSharpFeaturesResources.Implement_Abstract_Class, createChangedDocument, id)
{
}
}
}
}
}
\ No newline at end of file
......@@ -2,7 +2,6 @@
using System.Composition;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.ImplementAbstractClass;
......@@ -11,35 +10,17 @@
namespace Microsoft.CodeAnalysis.CSharp.ImplementAbstractClass
{
[ExportLanguageService(typeof(IImplementAbstractClassService), LanguageNames.CSharp), Shared]
internal class CSharpImplementAbstractClassService : AbstractImplementAbstractClassService
internal class CSharpImplementAbstractClassService :
AbstractImplementAbstractClassService<ClassDeclarationSyntax>
{
protected override bool TryInitializeState(
Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken,
Document document, SemanticModel model, ClassDeclarationSyntax classNode, CancellationToken cancellationToken,
out INamedTypeSymbol classType, out INamedTypeSymbol abstractClassType)
{
var baseClassNode = node as TypeSyntax;
if (baseClassNode != null && baseClassNode.Parent is BaseTypeSyntax &&
baseClassNode.Parent.IsParentKind(SyntaxKind.BaseList) &&
((BaseTypeSyntax)baseClassNode.Parent).Type == baseClassNode)
{
if (baseClassNode.Parent.Parent.IsParentKind(SyntaxKind.ClassDeclaration))
{
abstractClassType = model.GetTypeInfo(baseClassNode, cancellationToken).Type as INamedTypeSymbol;
cancellationToken.ThrowIfCancellationRequested();
classType = model.GetDeclaredSymbol(classNode);
abstractClassType = classType?.BaseType;
if (abstractClassType.IsAbstractClass())
{
var classDecl = baseClassNode.Parent.Parent.Parent as ClassDeclarationSyntax;
classType = model.GetDeclaredSymbol(classDecl, cancellationToken) as INamedTypeSymbol;
return classType != null && abstractClassType != null;
}
}
}
classType = null;
abstractClassType = null;
return false;
return classType != null && abstractClassType != null && abstractClassType.IsAbstractClass();
}
}
}
}
\ No newline at end of file
......@@ -13,7 +13,7 @@
namespace Microsoft.CodeAnalysis.ImplementAbstractClass
{
internal partial class AbstractImplementAbstractClassService
internal partial class AbstractImplementAbstractClassService<TClassSyntax>
{
private partial class Editor
{
......
......@@ -9,18 +9,18 @@
namespace Microsoft.CodeAnalysis.ImplementAbstractClass
{
internal partial class AbstractImplementAbstractClassService
internal partial class AbstractImplementAbstractClassService<TClassSyntax>
{
private class State
{
public SyntaxNode Location { get; }
public TClassSyntax Location { get; }
public INamedTypeSymbol ClassType { get; }
public INamedTypeSymbol AbstractClassType { get; }
// The members that are not implemented at all.
public IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> UnimplementedMembers { get; }
private State(SyntaxNode node, INamedTypeSymbol classType, INamedTypeSymbol abstractClassType, IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> unimplementedMembers)
private State(TClassSyntax node, INamedTypeSymbol classType, INamedTypeSymbol abstractClassType, IList<Tuple<INamedTypeSymbol, IList<ISymbol>>> unimplementedMembers)
{
this.Location = node;
this.ClassType = classType;
......@@ -29,10 +29,10 @@ private State(SyntaxNode node, INamedTypeSymbol classType, INamedTypeSymbol abst
}
public static State Generate(
AbstractImplementAbstractClassService service,
AbstractImplementAbstractClassService<TClassSyntax> service,
Document document,
SemanticModel model,
SyntaxNode node,
TClassSyntax node,
CancellationToken cancellationToken)
{
INamedTypeSymbol classType, abstractClassType;
......
......@@ -7,31 +7,37 @@
namespace Microsoft.CodeAnalysis.ImplementAbstractClass
{
internal abstract partial class AbstractImplementAbstractClassService : IImplementAbstractClassService
internal abstract partial class AbstractImplementAbstractClassService<TClassSyntax> :
IImplementAbstractClassService
where TClassSyntax : SyntaxNode
{
protected AbstractImplementAbstractClassService()
{
}
protected abstract bool TryInitializeState(Document document, SemanticModel model, SyntaxNode classNode, CancellationToken cancellationToken, out INamedTypeSymbol classType, out INamedTypeSymbol abstractClassType);
protected abstract bool TryInitializeState(Document document, SemanticModel model, TClassSyntax classNode, CancellationToken cancellationToken, out INamedTypeSymbol classType, out INamedTypeSymbol abstractClassType);
public Task<Document> ImplementAbstractClassAsync(Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
public async Task<Document> ImplementAbstractClassAsync(
Document document, SyntaxNode classNode, CancellationToken cancellationToken)
{
using (Logger.LogBlock(FunctionId.Refactoring_ImplementAbstractClass, cancellationToken))
{
var state = State.Generate(this, document, model, node, cancellationToken);
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var state = State.Generate(this, document, model, (TClassSyntax)classNode, cancellationToken);
if (state == null)
{
return SpecializedTasks.Default<Document>();
return null;
}
return new Editor(document, model, state).GetEditAsync(cancellationToken);
return await new Editor(document, model, state).GetEditAsync(cancellationToken).ConfigureAwait(false);
}
}
public bool CanImplementAbstractClass(Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken)
public async Task<bool> CanImplementAbstractClassAsync(
Document document, SyntaxNode node, CancellationToken cancellationToken)
{
return State.Generate(this, document, model, node, cancellationToken) != null;
var model = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
return State.Generate(this, document, model, (TClassSyntax)node, cancellationToken) != null;
}
}
}
}
\ No newline at end of file
......@@ -8,7 +8,7 @@ namespace Microsoft.CodeAnalysis.ImplementAbstractClass
{
internal interface IImplementAbstractClassService : ILanguageService
{
bool CanImplementAbstractClass(Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken);
Task<Document> ImplementAbstractClassAsync(Document document, SemanticModel model, SyntaxNode node, CancellationToken cancellationToken);
Task<bool> CanImplementAbstractClassAsync(Document document, SyntaxNode classNode, CancellationToken cancellationToken);
Task<Document> ImplementAbstractClassAsync(Document document, SyntaxNode classNode, CancellationToken cancellationToken);
}
}
}
\ No newline at end of file
......@@ -9,8 +9,8 @@ Imports Microsoft.CodeAnalysis.ImplementAbstractClass
Imports System.Composition
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.ImplementAbstractClass
<ExportCodeFixProvider(LanguageNames.VisualBasic, Name:=PredefinedCodeFixProviderNames.ImplementAbstractClass), [Shared]>
<ExportCodeFixProvider(LanguageNames.VisualBasic,
Name:=PredefinedCodeFixProviderNames.ImplementAbstractClass), [Shared]>
<ExtensionOrder(After:=PredefinedCodeFixProviderNames.GenerateType)>
Friend Class ImplementAbstractClassCodeFixProvider
Inherits CodeFixProvider
......@@ -28,7 +28,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.ImplementAbstractClass
End Function
Public NotOverridable Overrides Async Function RegisterCodeFixesAsync(context As CodeFixContext) As Task
Dim root = Await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(False)
Dim cancellationToken = context.CancellationToken
Dim document = context.Document
Dim root = Await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(False)
Dim token = root.FindToken(context.Span.Start)
If Not token.Span.IntersectsWith(context.Span) Then
......@@ -36,35 +39,29 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.ImplementAbstractClass
End If
Dim classNode = token.GetAncestors(Of ClassBlockSyntax)() _
.FirstOrDefault(Function(c) c.Span.IntersectsWith(context.Span))
.FirstOrDefault(Function(c) c.Span.IntersectsWith(context.Span))
If classNode Is Nothing Then
Return
End If
Dim service = context.Document.GetLanguageService(Of IImplementAbstractClassService)()
Dim model = Await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(False)
For Each inheritsNode In classNode.Inherits
For Each node In inheritsNode.Types
If service.CanImplementAbstractClass(
context.Document,
model,
node,
context.CancellationToken) Then
Dim title = VBFeaturesResources.Implement_Abstract_Class
Dim abstractType = model.GetTypeInfo(node, context.CancellationToken).Type
Dim typeName = abstractType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
Dim id = GetCodeActionId(abstractType.ContainingAssembly.Name, typeName)
context.RegisterCodeFix(
New MyCodeAction(title,
Function(c) ImplementAbstractClassAsync(context.Document, node, c),
id),
context.Diagnostics)
Return
End If
Next
Next
Dim service = document.GetLanguageService(Of IImplementAbstractClassService)()
If Await service.CanImplementAbstractClassAsync(
document, classNode, cancellationToken).ConfigureAwait(False) Then
Dim model = Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False)
Dim classSymbol = model.GetDeclaredSymbol(classNode)
Dim typeName = classSymbol.BaseType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
Dim id = GetCodeActionId(classSymbol.BaseType.ContainingAssembly.Name, typeName)
context.RegisterCodeFix(
New MyCodeAction(
Function(c) ImplementAbstractClassAsync(document, classNode, c),
id),
context.Diagnostics)
Return
End If
End Function
Friend Shared Function GetCodeActionId(assemblyName As String, abstractTypeFullyQualifiedName As String) As String
......@@ -73,23 +70,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.ImplementAbstractClass
abstractTypeFullyQualifiedName
End Function
Private Async Function ImplementAbstractClassAsync(document As Document, node As TypeSyntax, cancellationToken As CancellationToken) As Task(Of Document)
Private Function ImplementAbstractClassAsync(
document As Document, classBlock As ClassBlockSyntax, cancellationToken As CancellationToken) As Task(Of Document)
Dim service = document.GetLanguageService(Of IImplementAbstractClassService)()
Dim updatedDocument = Await service.ImplementAbstractClassAsync(
document,
Await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(False),
node,
cancellationToken).ConfigureAwait(False)
Return updatedDocument
Return service.ImplementAbstractClassAsync(document, classBlock, cancellationToken)
End Function
Private Class MyCodeAction
Inherits CodeAction.DocumentChangeAction
Public Sub New(title As String, createChangedDocument As Func(Of CancellationToken, Task(Of Document)), id As String)
MyBase.New(title, createChangedDocument, id)
Public Sub New(createChangedDocument As Func(Of CancellationToken, Task(Of Document)), id As String)
MyBase.New(VBFeaturesResources.Implement_Abstract_Class, createChangedDocument, id)
End Sub
End Class
End Class
End Namespace
End Namespace
\ No newline at end of file
......@@ -9,34 +9,17 @@ Imports System.Composition
Namespace Microsoft.CodeAnalysis.VisualBasic.ImplementAbstractClass
<ExportLanguageService(GetType(IImplementAbstractClassService), LanguageNames.VisualBasic), [Shared]>
Partial Friend Class VisualBasicImplementAbstractClassService
Inherits AbstractImplementAbstractClassService
Inherits AbstractImplementAbstractClassService(Of ClassBlockSyntax)
Protected Overrides Function TryInitializeState(
document As Document, model As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken,
document As Document, model As SemanticModel, classBlock As ClassBlockSyntax, cancellationToken As CancellationToken,
ByRef classType As INamedTypeSymbol, ByRef abstractClassType As INamedTypeSymbol) As Boolean
If cancellationToken.IsCancellationRequested Then
Return False
End If
classType = model.GetDeclaredSymbol(classBlock.BlockStatement)
abstractClassType = classType?.BaseType
Dim baseClassNode = TryCast(node, TypeSyntax)
If baseClassNode.IsParentKind(SyntaxKind.InheritsStatement) Then
If baseClassNode.Parent.IsParentKind(SyntaxKind.ClassBlock) Then
abstractClassType = TryCast(model.GetTypeInfo(baseClassNode, cancellationToken).Type, INamedTypeSymbol)
cancellationToken.ThrowIfCancellationRequested()
If abstractClassType.IsAbstractClass() Then
Dim classDecl = TryCast(baseClassNode.Parent.Parent, ClassBlockSyntax)
classType = model.GetDeclaredSymbol(classDecl.BlockStatement, cancellationToken)
cancellationToken.ThrowIfCancellationRequested()
Return abstractClassType IsNot Nothing AndAlso classType IsNot Nothing
End If
End If
End If
classType = Nothing
abstractClassType = Nothing
Return False
Return classType IsNot Nothing AndAlso
abstractClassType IsNot Nothing AndAlso
abstractClassType.IsAbstractClass()
End Function
End Class
End Namespace
End Namespace
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册