Skip to content

Commit

Permalink
Support changing property and event types
Browse files Browse the repository at this point in the history
  • Loading branch information
davidwengier committed Aug 22, 2022
1 parent cb3c277 commit 0777608
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.EditAndContinue.UnitTests;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
Expand Down Expand Up @@ -13628,17 +13629,26 @@ public void Field_Event_Type_Update_RuntimeTypeUnchanged(string oldType, string
[Theory]
[InlineData("int", "string")]
[InlineData("int", "int?")]
[InlineData("(int a, int b)", "(int a, double b)")]
public void Field_Event_Type_Update_RuntimeTypeChanged(string oldType, string newType)
{
var src1 = "class C { event System.Action<" + oldType + "> F, G; }";
var src2 = "class C { event System.Action<" + newType + "> F, G; }";
var src1 = "class C { event System.Action<" + oldType + "> E; }";
var src2 = "class C { event System.Action<" + newType + "> E; }";

var edits = GetTopEdits(src1, src2);

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.add_E").FirstOrDefault(m => m.GetParameterTypes()[0].Type.GetMemberTypeArgumentsNoUseSiteDiagnostics()[0].SpecialType == SpecialType.System_Int32)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.remove_E").FirstOrDefault(m => m.GetParameterTypes()[0].Type.GetMemberTypeArgumentsNoUseSiteDiagnostics()[0].SpecialType == SpecialType.System_Int32)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.add_E")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.remove_E"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "event System.Action<" + newType + "> F, G", FeaturesResources.event_),
Diagnostic(RudeEditKind.TypeUpdate, "event System.Action<" + newType + "> F, G", FeaturesResources.event_));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "event System.Action<" + newType + "> E", FeaturesResources.event_) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand Down Expand Up @@ -14282,8 +14292,19 @@ public void PropertyTypeUpdate()
edits.VerifyEdits(
"Update [int P { get; set; }]@10 -> [char P { get; set; }]@10");

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.set_P").FirstOrDefault(p => p.GetParameters()[0].Type.SpecialType == SpecialType.System_Int32)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_P"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "char P", FeaturesResources.property_));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "char P", FeaturesResources.property_) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand Down Expand Up @@ -15509,8 +15530,19 @@ public void Indexer_TypeUpdate()
edits.VerifyEdits(
"Update [int this[int a] { get; set; }]@10 -> [string this[int a] { get; set; }]@10");

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.set_Item").FirstOrDefault(p => p.GetParameters()[1].Type.SpecialType == SpecialType.System_Int32)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Item"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "string this[int a]", CSharpFeaturesResources.indexer));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "string this[int a]", CSharpFeaturesResources.indexer) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7806,8 +7806,15 @@ End Class
edits.VerifyEdits(
"Update [Property P As Integer]@10 -> [Property P As Char]@10")

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "Property P", FeaturesResources.property_))
edits.VerifySemantics(
semanticEdits:=
{
SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMember("C.get_P"), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.set_P").FirstOrDefault(Function(m) m.GetParameters().Any(Function(p) p.Type.SpecialType = SpecialType.System_Int32)), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.set_P"))
},
capabilities:=EditAndContinueCapabilities.AddMethodToExistingType Or EditAndContinueCapabilities.AddInstanceFieldToExistingType)
End Sub

<Fact>
Expand Down Expand Up @@ -10290,8 +10297,17 @@ End Class
"Update [value As Action]@78 -> [value As Action(Of String)]@89",
"Update [value As Action]@142 -> [value As Action(Of String)]@164")

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "Event E", FeaturesResources.event_))
edits.VerifySemantics(
semanticEdits:=
{
SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.add_E").FirstOrDefault(Function(m) m.GetParameters()(0).Type.GetTypeParameters().Length = 0), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.remove_E").FirstOrDefault(Function(m) m.GetParameters()(0).Type.GetTypeParameters().Length = 0), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.raise_E").OfType(Of MethodSymbol).FirstOrDefault(Function(m) m.AssociatedSymbol.GetMemberType().GetTypeParameters().Length = 0), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.add_E")),
SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.remove_E")),
SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.raise_E"))
},
capabilities:=EditAndContinueCapabilities.AddMethodToExistingType)
End Sub

<Fact>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2261,11 +2261,14 @@ protected static bool ReturnTypesEquivalent(IMethodSymbol oldMethod, IMethodSymb
TypesEquivalent(oldMethod.ReturnType, newMethod.ReturnType, exact);

protected static bool ReturnTypesEquivalent(IPropertySymbol oldProperty, IPropertySymbol newProperty, bool exact)
=> ReturnTypeRefsAndModifiersEquivalent(oldProperty, newProperty, exact) &&
TypesEquivalent(oldProperty.Type, newProperty.Type, exact);

private static bool ReturnTypeRefsAndModifiersEquivalent(IPropertySymbol oldProperty, IPropertySymbol newProperty, bool exact)
=> oldProperty.ReturnsByRef == newProperty.ReturnsByRef &&
oldProperty.ReturnsByRefReadonly == newProperty.ReturnsByRefReadonly &&
CustomModifiersEquivalent(oldProperty.TypeCustomModifiers, newProperty.TypeCustomModifiers, exact) &&
CustomModifiersEquivalent(oldProperty.RefCustomModifiers, newProperty.RefCustomModifiers, exact) &&
TypesEquivalent(oldProperty.Type, newProperty.Type, exact);
CustomModifiersEquivalent(oldProperty.RefCustomModifiers, newProperty.RefCustomModifiers, exact);

protected static bool ReturnTypesEquivalent(IEventSymbol oldEvent, IEventSymbol newEvent, bool exact)
=> TypesEquivalent(oldEvent.Type, newEvent.Type, exact);
Expand Down Expand Up @@ -3777,7 +3780,7 @@ private void ReportUpdatedSymbolDeclarationRudeEdits(
}
else if (oldSymbol is IPropertySymbol oldProperty && newSymbol is IPropertySymbol newProperty)
{
AnalyzeReturnType(oldProperty, newProperty, ref rudeEdit, ref hasGeneratedReturnTypeAttributeChange);
AnalyzeReturnType(oldProperty, newProperty, capabilities, ref rudeEdit, ref hasGeneratedReturnTypeAttributeChange, ref hasReturnTypeChange, cancellationToken);
}
else if (oldSymbol is IEventSymbol oldEvent && newSymbol is IEventSymbol newEvent)
{
Expand All @@ -3789,7 +3792,7 @@ private void ReportUpdatedSymbolDeclarationRudeEdits(
}
else
{
AnalyzeReturnType(oldEvent, newEvent, ref rudeEdit, ref hasGeneratedReturnTypeAttributeChange);
AnalyzeReturnType(oldEvent, newEvent, capabilities, ref rudeEdit, ref hasGeneratedReturnTypeAttributeChange, ref hasReturnTypeChange, cancellationToken);
}
}
else if (oldSymbol is IParameterSymbol oldParameter && newSymbol is IParameterSymbol newParameter)
Expand Down Expand Up @@ -3974,29 +3977,60 @@ private void AnalyzeReturnType(IMethodSymbol oldMethod, IMethodSymbol newMethod,
}
}

private static void AnalyzeReturnType(IEventSymbol oldEvent, IEventSymbol newEvent, ref RudeEditKind rudeEdit, ref bool hasGeneratedReturnTypeAttributeChange)
private void AnalyzeReturnType(IEventSymbol oldEvent, IEventSymbol newEvent, EditAndContinueCapabilitiesGrantor capabilities, ref RudeEditKind rudeEdit, ref bool hasGeneratedReturnTypeAttributeChange, ref bool hasReturnTypeChange, CancellationToken cancellationToken)
{
if (!ReturnTypesEquivalent(oldEvent, newEvent, exact: true))
{
if (ReturnTypesEquivalent(oldEvent, newEvent, exact: false))
{
hasGeneratedReturnTypeAttributeChange = true;
}
else if (oldEvent.ContainingType.IsDelegateType())
{
rudeEdit = RudeEditKind.TypeUpdate;
}
else if (AllowsDeletion(newEvent))
{
if (CanAddNewMember(newEvent, capabilities, cancellationToken))
{
hasReturnTypeChange = true;
}
else
{
rudeEdit = RudeEditKind.ChangingTypeNotSupportedByRuntime;
}
}
else
{
rudeEdit = RudeEditKind.TypeUpdate;
}
}
}

private static void AnalyzeReturnType(IPropertySymbol oldProperty, IPropertySymbol newProperty, ref RudeEditKind rudeEdit, ref bool hasGeneratedReturnTypeAttributeChange)
private void AnalyzeReturnType(IPropertySymbol oldProperty, IPropertySymbol newProperty, EditAndContinueCapabilitiesGrantor capabilities, ref RudeEditKind rudeEdit, ref bool hasGeneratedReturnTypeAttributeChange, ref bool hasReturnTypeChange, CancellationToken cancellationToken)
{
if (!ReturnTypesEquivalent(oldProperty, newProperty, exact: true))
{
if (ReturnTypesEquivalent(oldProperty, newProperty, exact: false))
{
hasGeneratedReturnTypeAttributeChange = true;
}
else if (oldProperty.ContainingType.IsDelegateType())
{
rudeEdit = RudeEditKind.TypeUpdate;
}
else if (ReturnTypeRefsAndModifiersEquivalent(oldProperty, newProperty, exact: true) &&
AllowsDeletion(newProperty))
{
if (CanAddNewMember(newProperty, capabilities, cancellationToken))
{
hasReturnTypeChange = true;
}
else
{
rudeEdit = RudeEditKind.ChangingTypeNotSupportedByRuntime;
}
}
else
{
rudeEdit = RudeEditKind.TypeUpdate;
Expand Down

0 comments on commit 0777608

Please sign in to comment.