Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support changing return types of methods, properties, events #63486

Merged
merged 19 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14277,6 +14277,140 @@ .maxstack 8
.Verify();
}

[Fact]
public void Method_ChangeReturnType()
{
using var _ = new EditAndContinueTest(options: TestOptions.DebugDll, targetFramework: TargetFramework.NetStandard20)
.AddGeneration(
source: $$"""
class C
{
string M(int someInt) { return someInt.ToString(); }
}
""",
validator: g =>
{
g.VerifyTypeDefNames("<Module>", "C");
g.VerifyMethodDefNames("M", ".ctor");
})
.AddGeneration(
source: $$"""
class C
{
int M(int someInt) { return someInt; }
}
""",
edits: new[] {
Edit(SemanticEditKind.Delete, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetTypeOrReturnType().SpecialType == SpecialType.System_String)?.ISymbol, newSymbolProvider: c=>c.GetMember("C")),
Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetTypeOrReturnType().SpecialType == SpecialType.System_Int32)?.ISymbol),
},
validator: g =>
{
g.VerifyTypeDefNames();
g.VerifyMethodDefNames("M", "M");
g.VerifyDeletedMembers("C: {M}");

g.VerifyEncLogDefinitions(new[]
{
Row(2, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddMethod),
Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(1, TableIndex.Param, EditAndContinueOperation.Default),
Row(3, TableIndex.MethodDef, EditAndContinueOperation.AddParameter),
Row(2, TableIndex.Param, EditAndContinueOperation.Default)
});
g.VerifyEncMapDefinitions(new[]
{
Handle(1, TableIndex.MethodDef),
Handle(3, TableIndex.MethodDef),
Handle(1, TableIndex.Param),
Handle(2, TableIndex.Param),
Handle(2, TableIndex.StandAloneSig)
});

var expectedIL = """
{
// Code size 6 (0x6)
.maxstack 8
IL_0000: newobj 0x0A000006
IL_0005: throw
}
{
// Code size 7 (0x7)
.maxstack 1
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
}
""";

// Can't verify the IL of individual methods because that requires IMethodSymbolInternal implementations
g.VerifyIL(expectedIL);
})
.AddGeneration(
source: $$"""
class C
{
string M(int someInt) { return someInt.ToString(); }
}
""",
edits: new[] {
Edit(SemanticEditKind.Delete, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetTypeOrReturnType().SpecialType == SpecialType.System_Int32)?.ISymbol, newSymbolProvider: c=>c.GetMember("C")),
Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetTypeOrReturnType().SpecialType == SpecialType.System_String)?.ISymbol),
},
validator: g =>
{
g.VerifyTypeDefNames();
g.VerifyMethodDefNames("M", "M");
g.VerifyDeletedMembers("C: {M}");

g.VerifyEncLogDefinitions(new[]
{
Row(3, TableIndex.StandAloneSig, EditAndContinueOperation.Default),
Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(3, TableIndex.MethodDef, EditAndContinueOperation.Default),
Row(1, TableIndex.Param, EditAndContinueOperation.Default),
Row(2, TableIndex.Param, EditAndContinueOperation.Default)
});
g.VerifyEncMapDefinitions(new[]
{
Handle(1, TableIndex.MethodDef),
Handle(3, TableIndex.MethodDef),
Handle(1, TableIndex.Param),
Handle(2, TableIndex.Param),
Handle(3, TableIndex.StandAloneSig)
});

var expectedIL = """
{
// Code size 13 (0xd)
.maxstack 1
IL_0000: nop
IL_0001: ldarga.s V_1
IL_0003: call 0x0A000007
IL_0008: stloc.0
IL_0009: br.s IL_000b
IL_000b: ldloc.0
IL_000c: ret
}
{
// Code size 6 (0x6)
.maxstack 8
IL_0000: newobj 0x0A000008
IL_0005: throw
}
""";

// Can't verify the IL of individual methods because that requires IMethodSymbolInternal implementations
g.VerifyIL(expectedIL);
})
.Verify();
}

[Fact]
public void FileTypes_01()
{
Expand Down
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 @@ -6905,8 +6906,17 @@ public void Method_ReturnType_Update_RuntimeTypeChanged(string oldType, string n

var edits = GetTopEdits(src1, src2);

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, newType + " M()", FeaturesResources.method));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, newType + " M()", FeaturesResources.method) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand Down Expand Up @@ -8976,8 +8986,17 @@ public void Method_ReadOnlyRef_ReturnType_Update()
edits.VerifyEdits(
"Update [int M() => throw null;]@13 -> [ref readonly int M() => throw null;]@13");

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("Test.M"), deletedSymbolContainerProvider: c => c.GetMember("Test")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("Test.M"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "ref readonly int M()", FeaturesResources.method));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "ref readonly int M()", FeaturesResources.method) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand Down Expand Up @@ -13610,17 +13629,26 @@ public void Field_Event_Type_Update_RuntimeTypeUnchanged(string oldType, string
[Theory]
[InlineData("int", "string")]
[InlineData("int", "int?")]
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
[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 @@ -14264,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 @@ -15491,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 All @@ -15506,8 +15556,17 @@ public void Tuple_TypeUpdate()
edits.VerifyEdits(
"Update [(int, int) M() { throw new System.Exception(); }]@10 -> [(string, int) M() { throw new System.Exception(); }]@10");

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "(string, int) M()", FeaturesResources.method));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "(string, int) M()", FeaturesResources.method) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand All @@ -15521,8 +15580,17 @@ public void TupleElementDelete()
edits.VerifyEdits(
"Update [(int, int, int a) M() { return (1, 2, 3); }]@10 -> [(int, int) M() { return (1, 2); }]@10");

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "(int, int) M()", FeaturesResources.method));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "(int, int) M()", FeaturesResources.method) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand All @@ -15536,8 +15604,17 @@ public void TupleElementAdd()
edits.VerifyEdits(
"Update [(int, int) M() { return (1, 2); }]@10 -> [(int, int, int a) M() { return (1, 2, 3); }]@10");

edits.VerifySemantics(
new[]
{
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
},
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);

edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "(int, int, int a) M()", FeaturesResources.method));
new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "(int, int, int a) M()", FeaturesResources.method) },
capabilities: EditAndContinueCapabilities.Baseline);
}

[Fact]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4449,7 +4449,7 @@ public async Task WatchHotReloadServiceTest()

var source1 = "class C { void M() { System.Console.WriteLine(1); } }";
var source2 = "class C { void M() { System.Console.WriteLine(2); } }";
var source3 = "class C { int M() { System.Console.WriteLine(2); } }";
var source3 = "class C { void M<T>() { System.Console.WriteLine(2); } }";
var source4 = "class C { void M() { System.Console.WriteLine(2)/* missing semicolon */ }";

var dir = Temp.CreateDirectory();
Expand Down Expand Up @@ -4496,7 +4496,7 @@ public async Task WatchHotReloadServiceTest()

result = await hotReload.EmitSolutionUpdateAsync(solution, CancellationToken.None);
AssertEx.Equal(
new[] { "ENC0009: " + string.Format(FeaturesResources.Updating_the_type_of_0_requires_restarting_the_application, FeaturesResources.method) },
new[] { "ENC0021: " + string.Format(FeaturesResources.Adding_0_requires_restarting_the_application, FeaturesResources.type_parameter) },
result.diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}"));

Assert.Empty(result.updates);
Expand All @@ -4516,7 +4516,7 @@ public async Task UnitTestingHotReloadServiceTest()
{
var source1 = "class C { void M() { System.Console.WriteLine(1); } }";
var source2 = "class C { void M() { System.Console.WriteLine(2); } }";
var source3 = "class C { int M() { System.Console.WriteLine(2); } }";
var source3 = "class C { void M<T>() { System.Console.WriteLine(2); } }";
var source4 = "class C { void M() { System.Console.WriteLine(2)/* missing semicolon */ }";

var dir = Temp.CreateDirectory();
Expand Down Expand Up @@ -4562,7 +4562,7 @@ public async Task UnitTestingHotReloadServiceTest()
// Rude edit
result = await hotReload.EmitSolutionUpdateAsync(solution, commitUpdates: true, CancellationToken.None);
AssertEx.Equal(
new[] { "ENC0009: " + string.Format(FeaturesResources.Updating_the_type_of_0_requires_restarting_the_application, FeaturesResources.method) },
new[] { "ENC0021: " + string.Format(FeaturesResources.Adding_0_requires_restarting_the_application, FeaturesResources.type_parameter) },
davidwengier marked this conversation as resolved.
Show resolved Hide resolved
result.diagnostics.Select(d => $"{d.Id}: {d.GetMessage()}"));

Assert.Empty(result.updates);
Expand Down
Loading