diff --git a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs index a8bde70403ff6..e16e24a235ee3 100644 --- a/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit/Emit/EditAndContinue/EditAndContinueTests.cs @@ -14096,6 +14096,133 @@ class C test.Verify(); } + [Fact] + public void Method_ChangeParameterType() + { + using var _ = new EditAndContinueTest(options: TestOptions.DebugDll, targetFramework: TargetFramework.NetStandard20) + .AddGeneration( + source: $$""" + class C + { + void M(int someInt) { someInt.ToString(); } + } + """, + validator: g => + { + g.VerifyTypeDefNames("", "C"); + g.VerifyMethodDefNames("M", ".ctor"); + }) + .AddGeneration( + source: $$""" + class C + { + void M(bool someBool) { someBool.ToString(); } + } + """, + edits: new[] { + Edit(SemanticEditKind.Delete, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_Int32)?.ISymbol, newSymbolProvider: c=>c.GetMember("C")), + Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_Boolean)?.ISymbol), + }, + validator: g => + { + g.VerifyTypeDefNames(); + g.VerifyMethodDefNames("M", "M"); + g.VerifyDeletedMembers("C: {M}"); + + g.VerifyEncLogDefinitions(new[] + { + 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) + }); + + var expectedIL = """ + { + // Code size 6 (0x6) + .maxstack 8 + IL_0000: newobj 0x0A000006 + IL_0005: throw + } + { + // Code size 10 (0xa) + .maxstack 8 + IL_0000: nop + IL_0001: ldarga.s V_1 + IL_0003: call 0x0A000007 + IL_0008: pop + IL_0009: ret + } + """; + + // Can't verify the IL of individual methods because that requires IMethodSymbolInternal implementations + g.VerifyIL(expectedIL); + }) + .AddGeneration( + source: $$""" + class C + { + void M(int someInt) { someInt.ToString(); } + } + """, + edits: new[] { + Edit(SemanticEditKind.Delete, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_Boolean)?.ISymbol, newSymbolProvider: c=>c.GetMember("C")), + Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_Int32)?.ISymbol), + }, + validator: g => + { + g.VerifyTypeDefNames(); + g.VerifyMethodDefNames("M", "M"); + g.VerifyDeletedMembers("C: {M}"); + + g.VerifyEncLogDefinitions(new[] + { + 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) + }); + + var expectedIL = """ + { + // Code size 10 (0xa) + .maxstack 8 + IL_0000: nop + IL_0001: ldarga.s V_1 + IL_0003: call 0x0A000008 + IL_0008: pop + IL_0009: ret + } + { + // Code size 6 (0x6) + .maxstack 8 + IL_0000: newobj 0x0A000009 + 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() { diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 4ce270d1d4949..759cb0ffa75f8 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -7617,8 +7617,236 @@ static void Main(string[] args) edits.VerifyEdits( "Insert [string[] args]@35"); + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "string[] args", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); + } + + [Fact] + public void MethodUpdate_AddParameters() + { + var src1 = @" +class C +{ + void M(int a) + { + } +}"; + var src2 = @" +class C +{ + void M(int a, int b, int c) + { + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 3)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] + { + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int b", FeaturesResources.parameter), + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int c", FeaturesResources.parameter) + }, + capabilities: EditAndContinueCapabilities.Baseline); + } + + [Fact] + public void MethodUpdate_AddParameter_Partial() + { + var src1 = @" +class C +{ + partial void M(int a); + + partial void M(int a) + { + } +}"; + var src2 = @" +class C +{ + partial void M(int a, int b, int c); + + partial void M(int a, int b, int c) + { + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => ((IMethodSymbol)c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 3)?.ISymbol)?.PartialImplementationPart) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] + { + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int b", FeaturesResources.parameter), + Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int c", FeaturesResources.parameter) + }, + capabilities: EditAndContinueCapabilities.Baseline); + } + + [Fact] + public void MethodUpdate_ChangeParameterType() + { + var src1 = @" +class C +{ + static void Main(bool x) + { + + } +}"; + var src2 = @" +class C +{ + static void Main(int x) + { + + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [bool x]@35 -> [int x]@35"); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Boolean))?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Int32))?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "int x", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); + } + + [Fact] + public void MethodUpdate_ChangeParameterTypeAndRename() + { + var src1 = @" +class C +{ + static void Main(bool someBool) + { + + } +}"; + var src2 = @" +class C +{ + static void Main(int someInt) + { + + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [bool someBool]@35 -> [int someInt]@35"); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Boolean))?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Int32))?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + } + + [Fact] + public void MethodUpdate_DeleteParameter() + { + var src1 = @" +class C +{ + static void Main(string[] args) + { + + } +}"; + var src2 = @" +class C +{ + static void Main() + { + + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Delete [string[] args]@35"); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.DeleteNotSupportedByRuntime, "static void Main()", DeletedSymbolDisplay(FeaturesResources.parameter, "string[] args")) }, + capabilities: EditAndContinueCapabilities.Baseline); + } + + [Fact] + public void MethodUpdate_DeleteParameters() + { + var src1 = @" +class C +{ + void M(int a, int b, int c) + { + } +}"; + var src2 = @" +class C +{ + void M(int a) + { + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 3)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "string[] args", FeaturesResources.parameter)); + new[] + { + Diagnostic(RudeEditKind.DeleteNotSupportedByRuntime, "void M(int a)", DeletedSymbolDisplay(FeaturesResources.parameter, "int b")), + Diagnostic(RudeEditKind.DeleteNotSupportedByRuntime, "void M(int a)", DeletedSymbolDisplay(FeaturesResources.parameter, "int c")) + }, + capabilities: EditAndContinueCapabilities.Baseline); } [Fact] @@ -7730,7 +7958,7 @@ static void EntryPoint(string[] args) capabilities: EditAndContinueCapabilities.AddMethodToExistingType); edits.VerifySemanticDiagnostics( - new[] { Diagnostic(RudeEditKind.Renamed, "static void EntryPoint(string[] args)", FeaturesResources.method) }, + new[] { Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "static void EntryPoint(string[] args)", FeaturesResources.method) }, capabilities: EditAndContinueCapabilities.Baseline); } @@ -8693,8 +8921,17 @@ public void Method_ReadOnlyRef_Parameter_InsertParameter() edits.VerifyEdits( "Insert [in int b]@19"); + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("Test.M").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("Test")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("Test.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "in int b", FeaturesResources.parameter)); + new[] { Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "in int b", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); } [Fact] @@ -9387,6 +9624,38 @@ public C([System.Obsolete]int a) capabilities: EditAndContinueCapabilities.ChangeCustomAttributes); } + [Fact] + public void Constructor_ChangeParameterType() + { + var src1 = @" +class C +{ + public C(bool x) + { + } +}"; + var src2 = @" +class C +{ + public C(int x) + { + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C..ctor").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Boolean))?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C..ctor").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Int32))?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + + edits.VerifySemanticDiagnostics( + new[] { Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "int x", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); + } + [Fact] [WorkItem(2068, "https://github.com/dotnet/roslyn/issues/2068")] public void Constructor_ExternModifier_Add() @@ -9511,8 +9780,17 @@ public C(int a, int b) { } "Update [(int a)]@26 -> [(int a, int b)]@26", "Insert [int b]@34"); + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C..ctor").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C..ctor").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "int b", FeaturesResources.parameter)); + new[] { Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int b", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); } [Fact] @@ -10930,8 +11208,17 @@ public void Constructor_ReadOnlyRef_Parameter_InsertParameter() edits.VerifyEdits( "Insert [in int b]@18"); + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("Test..ctor").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("Test")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("Test..ctor").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "in int b", FeaturesResources.parameter)); + new[] { Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "in int b", FeaturesResources.parameter) }, + capabilities: EditAndContinueCapabilities.Baseline); } [Fact] @@ -13378,7 +13665,7 @@ public void Event_Rename1() capabilities: EditAndContinueCapabilities.AddMethodToExistingType); edits.VerifySemanticDiagnostics( - new[] { Diagnostic(RudeEditKind.Renamed, "event int F", FeaturesResources.event_) }, + new[] { Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "event int F", FeaturesResources.event_) }, capabilities: EditAndContinueCapabilities.Baseline); } @@ -13760,7 +14047,7 @@ public void Property_Rename1() capabilities: EditAndContinueCapabilities.AddMethodToExistingType); edits.VerifySemanticDiagnostics( - new[] { Diagnostic(RudeEditKind.Renamed, "int Q", FeaturesResources.property_) }, + new[] { Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int Q", FeaturesResources.property_) }, capabilities: EditAndContinueCapabilities.Baseline); } @@ -15216,8 +15503,53 @@ public void Indexer_ParameterUpdate() var edits = GetTopEdits(src1, src2); - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "string a", FeaturesResources.parameter)); + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.get_Item").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_Int32)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.set_Item").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_Int32)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.get_Item").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_String)?.ISymbol), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.set_Item").FirstOrDefault(m => m.GetParameterTypes()[0].SpecialType == SpecialType.System_String)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + } + + [Fact] + public void Indexer_ParameterInsert() + { + var src1 = "class C { int this[int a] { get; set; } }"; + var src2 = "class C { int this[int a, string b] { get; set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.get_Item").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.set_Item").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.get_Item").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.set_Item").FirstOrDefault(m => m.GetParameterCount() == 3)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); + } + + [Fact] + public void Indexer_ParameterDelete() + { + var src1 = "class C { int this[int a, string b] { get; set; } }"; + var src2 = "class C { int this[int a] { get; set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + new[] + { + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.get_Item").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.set_Item").FirstOrDefault(m => m.GetParameterCount() == 3)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.get_Item").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol), + SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.set_Item").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol) + }, + capabilities: EditAndContinueCapabilities.AddMethodToExistingType); } [Fact] @@ -16260,21 +16592,6 @@ public void Parameter_Type_Update_RuntimeTypeUnchanged(string oldType, string ne SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))); } - [Theory] - [InlineData("int", "string")] - [InlineData("int", "int?")] - [InlineData("(int a, int b)", "(int a, double b)")] - public void Parameter_Type_Update_RuntimeTypeChanged(string oldType, string newType) - { - var src1 = "class C { static void M(" + oldType + " a) {} }"; - var src2 = "class C { static void M(" + newType + " a) {} }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, newType + " a", FeaturesResources.parameter)); - } - [Fact] public void Parameter_Type_Nullable() { diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index 6a9e92b5c1411..f856bfccc7b0a 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -5691,8 +5691,13 @@ End Class "Insert [b As Integer]@37", "Insert [b]@37") - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "b As Integer", FeaturesResources.parameter)) + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C..ctor").FirstOrDefault(Function(m) m.GetParameters().Length = 1), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMembers("C..ctor").FirstOrDefault(Function(m) m.GetParameters().Length = 2)) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) End Sub @@ -10244,9 +10249,7 @@ End Class "Update [value As Action]@142 -> [value As Action(Of String)]@164") edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "Event E", FeaturesResources.event_), - Diagnostic(RudeEditKind.TypeUpdate, "value As Action(Of String)", FeaturesResources.parameter), - Diagnostic(RudeEditKind.TypeUpdate, "value As Action(Of String)", FeaturesResources.parameter)) + Diagnostic(RudeEditKind.TypeUpdate, "Event E", FeaturesResources.event_)) End Sub @@ -10496,8 +10499,18 @@ End Class edits.VerifyEdits( "Update [a As Integer]@38 -> [a As Object]@38") + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.get_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.GetMembers("C.get_P").FirstOrDefault(Function(m) m.GetParameters().Any(Function(p) p.Type.SpecialType = SpecialType.System_Object))) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "a As Object", FeaturesResources.parameter)) + {Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "a As Object", FeaturesResources.parameter)}, + capabilities:=EditAndContinueCapabilities.Baseline) End Sub @@ -10509,8 +10522,18 @@ End Class edits.VerifyEdits( "Update [a As Integer]@38 -> [a]@38") + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.get_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.GetMembers("C.get_P").FirstOrDefault(Function(m) m.GetParameters().Any(Function(p) p.Type.SpecialType = SpecialType.System_Object))) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.TypeUpdate, "a", FeaturesResources.parameter)) + {Diagnostic(RudeEditKind.ChangingTypeNotSupportedByRuntime, "a", FeaturesResources.parameter)}, + capabilities:=EditAndContinueCapabilities.Baseline) End Sub @@ -10549,8 +10572,13 @@ End Class "Insert [a As Integer]@24", "Insert [a]@24") - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter)) + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 0), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 1)) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) End Sub @@ -10564,8 +10592,13 @@ End Class "Insert [ByRef b As Integer]@38", "Insert [b]@44") - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "ByRef b As Integer", FeaturesResources.parameter)) + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 1), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 2)) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) End Sub @@ -10591,8 +10624,13 @@ End Class "Insert [a As Integer]@24", "Insert [a]@24") - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter)) + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 0), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 1)) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) End Sub @@ -10605,8 +10643,17 @@ End Class "Delete [a As Integer]@24", "Delete [a]@24") + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 1), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 0)) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M()", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) + {Diagnostic(RudeEditKind.DeleteNotSupportedByRuntime, "Public Sub M()", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))}, + capabilities:=EditAndContinueCapabilities.Baseline) End Sub @@ -10620,8 +10667,17 @@ End Class "Delete [a As Integer]@24", "Delete [a]@24") + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 2), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 1)) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) + edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M(b As Integer)", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) + {Diagnostic(RudeEditKind.DeleteNotSupportedByRuntime, "Public Sub M(b As Integer)", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))}, + capabilities:=EditAndContinueCapabilities.Baseline) End Sub @@ -10647,8 +10703,13 @@ End Class "Delete [a As Integer]@24", "Delete [a]@24") - edits.VerifySemanticDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Sub M", DeletedSymbolDisplay(FeaturesResources.parameter, "a As Integer"))) + edits.VerifySemantics( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Delete, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 1), deletedSymbolContainerProvider:=Function(c) c.GetMember("C")), + SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMembers("C.M").FirstOrDefault(Function(m) m.GetParameters().Length = 0)) + }, + capabilities:=EditAndContinueCapabilities.AddMethodToExistingType) End Sub diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 074fda0603593..6a551f2b92ea5 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -8,19 +8,18 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Linq; -using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Text; using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Differencing; +using Microsoft.CodeAnalysis.EditAndContinue.Contracts; using Microsoft.CodeAnalysis.Emit; using Microsoft.CodeAnalysis.ErrorReporting; using Microsoft.CodeAnalysis.PooledObjects; using Microsoft.CodeAnalysis.Shared.Extensions; using Microsoft.CodeAnalysis.Shared.Utilities; using Microsoft.CodeAnalysis.Text; -using Microsoft.CodeAnalysis.EditAndContinue.Contracts; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.EditAndContinue @@ -2652,6 +2651,8 @@ private async Task> AnalyzeSemanticsAsync( continue; } + var rudeEditKind = RudeEditKind.Delete; + // If the associated member declaration (parameter/type parameter -> method) has also been deleted skip // the delete of the symbol as it will be deleted by the delete of the associated member. We pass the edit kind // in here to avoid property/event accessors from being caught up in this, because those deletes we want to process @@ -2666,6 +2667,20 @@ private async Task> AnalyzeSemanticsAsync( { continue; } + + // We allow deleting parameters, by issuing delete and insert edits for the old and new method + if (oldSymbol is IParameterSymbol) + { + if (TryAddParameterInsertOrDeleteEdits(semanticEdits, oldSymbol.ContainingSymbol, newModel, capabilities, syntaxMap, editScript, cancellationToken, out var notSupportedByRuntime)) + { + continue; + } + + if (notSupportedByRuntime) + { + rudeEditKind = RudeEditKind.DeleteNotSupportedByRuntime; + } + } } else if (oldSymbol.ContainingType != null) { @@ -2678,7 +2693,7 @@ private async Task> AnalyzeSemanticsAsync( continue; } - if (!hasActiveStatement && AllowsDeletion(oldSymbol, willInsertNewMember: false, capabilities)) + if (!hasActiveStatement && AllowsDeletion(oldSymbol)) { AddMemberOrAssociatedMemberSemanticEdits(semanticEdits, SemanticEditKind.Delete, oldSymbol, containingSymbolKey, syntaxMap, partialType: null, cancellationToken); continue; @@ -2688,7 +2703,7 @@ private async Task> AnalyzeSemanticsAsync( // deleting symbol is not allowed diagnostics.Add(new RudeEditDiagnostic( - RudeEditKind.Delete, + rudeEditKind, diagnosticSpan, oldDeclaration, new[] @@ -2836,7 +2851,7 @@ private async Task> AnalyzeSemanticsAsync( // the insert of the accessor as it will be inserted by the property/indexer/event. continue; } - else if (newSymbol is IParameterSymbol or ITypeParameterSymbol) + else if (newSymbol is ITypeParameterSymbol) { diagnostics.Add(new RudeEditDiagnostic( RudeEditKind.Insert, @@ -2846,6 +2861,19 @@ private async Task> AnalyzeSemanticsAsync( continue; } + else if (newSymbol is IParameterSymbol) + { + if (!TryAddParameterInsertOrDeleteEdits(semanticEdits, newSymbol.ContainingSymbol, oldModel, capabilities, syntaxMap, editScript, cancellationToken, out var notSupportedByRuntime)) + { + diagnostics.Add(new RudeEditDiagnostic( + notSupportedByRuntime ? RudeEditKind.InsertNotSupportedByRuntime : RudeEditKind.Insert, + GetDiagnosticSpan(newDeclaration, EditKind.Insert), + newDeclaration, + arguments: new[] { GetDisplayName(newDeclaration, EditKind.Insert) })); + } + + continue; + } else if (newContainingType != null && !IsGlobalMain(newSymbol)) { // The edit actually adds a new symbol into an existing or a new type. @@ -3036,7 +3064,8 @@ private async Task> AnalyzeSemanticsAsync( // when members are moved between documents in partial classes, they can appear as renames, so // we also check that the old symbol can't be resolved in the new compilation if (oldSymbol.Name != newSymbol.Name && - AllowsDeletion(oldSymbol, willInsertNewMember: true, capabilities) && + AllowsDeletion(oldSymbol) && + CanAddNewMember(oldSymbol, capabilities) && SymbolKey.Create(oldSymbol, cancellationToken).Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol is null) { Contract.ThrowIfNull(oldDeclaration); @@ -3218,12 +3247,93 @@ private async Task> AnalyzeSemanticsAsync( } } + /// + /// Adds a delete and insert edit for the old and new symbols that have had a parameter inserted or deleted + /// + /// The symbol that contains the parameter that has been added or deleted (either IMethodSymbol or IPropertySymbol) + /// The semantic model from the old compilation, for parameter inserts, or new compilation, for deletes + /// Whether the edit should be rejected because the runtime doesn't support inserting new methods. Otherwise a normal rude edit is appropriate. + /// Returns whether semantic edits were added, or if not then a rude edit should be created + private static bool TryAddParameterInsertOrDeleteEdits(ArrayBuilder semanticEdits, ISymbol containingSymbol, SemanticModel? otherModel, EditAndContinueCapabilitiesGrantor capabilities, Func? syntaxMap, EditScript editScript, CancellationToken cancellationToken, out bool notSupportedByRuntime) + { + Debug.Assert(containingSymbol is IPropertySymbol or IMethodSymbol); + + notSupportedByRuntime = false; + + // Since we're inserting (or deleting) a parameter node, oldSymbol (or newSymbol) would have been null, + // and a symbolkey won't map to the other compilation because the parameters are different, so we have to go back to the edit map + // to find the declaration that contains the parameter, and its partner, and then its symbol, so we need to be sure we can get + // to syntax, and have a semantic model to get back to symbols. + if (otherModel is null || + containingSymbol.DeclaringSyntaxReferences.Length != 1) + { + return false; + } + + // We can ignore parameter inserts and deletes for partial method definitions, as we'll report them on the implementation. + // We return true here so no rude edit is raised. + if (containingSymbol is IMethodSymbol { IsPartialDefinition: true }) + { + return true; + } + + // We don't support delegate parameters + if (containingSymbol.ContainingType.IsDelegateType()) + { + return false; + } + + // Find the node that matches this declaration + SyntaxNode otherContainingNode; + var containingNode = containingSymbol.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken); + if (editScript.Match.TryGetOldNode(containingNode, out var oldNode)) + { + otherContainingNode = oldNode; + } + else if (editScript.Match.TryGetNewNode(containingNode, out var newNode)) + { + otherContainingNode = newNode; + } + else + { + return false; + } + + var otherContainingSymbol = otherModel.GetDeclaredSymbol(otherContainingNode, cancellationToken); + if (otherContainingSymbol is null || !AllowsDeletion(otherContainingSymbol)) + { + return false; + } + + // Now we can work out which is the old and which is the new, depending on which map we found + // the match in + var oldSymbol = (otherContainingNode == oldNode) ? otherContainingSymbol : containingSymbol; + var newSymbol = (otherContainingNode == oldNode) ? containingSymbol : otherContainingSymbol; + + if (!CanAddNewMember(oldSymbol, capabilities)) + { + notSupportedByRuntime = true; + return false; + } + + var containingSymbolKey = SymbolKey.Create(oldSymbol.ContainingType, cancellationToken); + + AddMemberOrAssociatedMemberSemanticEdits(semanticEdits, SemanticEditKind.Delete, oldSymbol, containingSymbolKey, syntaxMap, partialType: null, cancellationToken); + + var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); + AddMemberOrAssociatedMemberSemanticEdits(semanticEdits, SemanticEditKind.Insert, newSymbol, containingSymbolKey: null, syntaxMap, + partialType: IsPartialEdit(oldSymbol, newSymbol, editScript.Match.OldRoot.SyntaxTree, editScript.Match.NewRoot.SyntaxTree) ? symbolKey : null, + cancellationToken); + + return true; + } + /// /// Returns whether or not the specified symbol can be deleted by the user. Normally deletes are a rude edit /// but for some kinds of symbols we allow deletes, and synthesize an update to an empty method body during /// emit. /// - private static bool AllowsDeletion(ISymbol symbol, bool willInsertNewMember, EditAndContinueCapabilitiesGrantor capabilities) + private static bool AllowsDeletion(ISymbol symbol) { // We don't currently allow deleting virtual or abstract methods, because if those are in the middle of // an inheritance chain then throwing a missing method exception is not expected @@ -3238,10 +3348,6 @@ private static bool AllowsDeletion(ISymbol symbol, bool willInsertNewMember, Edi if (symbol.ContainingType is not { TypeKind: TypeKind.Class or TypeKind.Struct }) return false; - // If this delete will result in a new member being added, then we have to check capabilities too - if (willInsertNewMember && !CanAddNewMember(symbol, capabilities)) - return false; - // We store the containing symbol in NewSymbol of the edit for later use. if (symbol is IMethodSymbol { @@ -3429,6 +3535,7 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( out bool hasGeneratedAttributeChange, out bool hasGeneratedReturnTypeAttributeChange, out bool hasParameterRename, + out bool hasParameterTypeChange, CancellationToken cancellationToken) { var rudeEdit = RudeEditKind.None; @@ -3436,6 +3543,7 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( hasGeneratedAttributeChange = false; hasGeneratedReturnTypeAttributeChange = false; hasParameterRename = false; + hasParameterTypeChange = false; if (oldSymbol.Kind != newSymbol.Kind) { @@ -3445,14 +3553,8 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( { if (oldSymbol is IParameterSymbol && newSymbol is IParameterSymbol) { - if (capabilities.Grant(EditAndContinueCapabilities.UpdateParameters)) - { - hasParameterRename = true; - } - else - { - rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; - } + // We defer checking parameter renames until later, because if their types have also changed + // then we'll be emitting a new method, so it won't be a rename any more } else if (oldSymbol is IMethodSymbol oldMethod && newSymbol is IMethodSymbol newMethod) { @@ -3477,10 +3579,14 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( // Can't change from explicit to implicit interface implementation, or one interface to another rudeEdit = RudeEditKind.Renamed; } - else if (!AllowsDeletion(oldSymbol, willInsertNewMember: true, capabilities)) + else if (!AllowsDeletion(oldSymbol)) { rudeEdit = RudeEditKind.Renamed; } + else if (!CanAddNewMember(oldSymbol, capabilities)) + { + rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; + } } else if (oldSymbol is IPropertySymbol oldProperty && newSymbol is IPropertySymbol newProperty) { @@ -3489,10 +3595,14 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( // Can't change from explicit to implicit interface implementation, or one interface to another rudeEdit = RudeEditKind.Renamed; } - else if (!AllowsDeletion(oldSymbol, willInsertNewMember: true, capabilities)) + else if (!AllowsDeletion(oldSymbol)) { rudeEdit = RudeEditKind.Renamed; } + else if (!CanAddNewMember(oldSymbol, capabilities)) + { + rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; + } } else if (oldSymbol is IEventSymbol oldEvent && newSymbol is IEventSymbol newEvent) { @@ -3501,10 +3611,14 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( // Can't change from explicit to implicit interface implementation, or one interface to another rudeEdit = RudeEditKind.Renamed; } - else if (!AllowsDeletion(oldSymbol, willInsertNewMember: true, capabilities)) + else if (!AllowsDeletion(oldSymbol)) { rudeEdit = RudeEditKind.Renamed; } + else if (!CanAddNewMember(oldSymbol, capabilities)) + { + rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; + } } else { @@ -3673,7 +3787,19 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( } else { - AnalyzeParameterType(oldParameter, newParameter, ref rudeEdit, ref hasGeneratedAttributeChange); + AnalyzeParameterType(oldParameter, newParameter, capabilities, ref rudeEdit, ref hasGeneratedAttributeChange, ref hasParameterTypeChange); + + if (!hasParameterTypeChange && oldParameter.Name != newParameter.Name) + { + if (capabilities.Grant(EditAndContinueCapabilities.UpdateParameters)) + { + hasParameterRename = true; + } + else + { + rudeEdit = RudeEditKind.RenamingNotSupportedByRuntime; + } + } } } else if (oldSymbol is ITypeParameterSymbol oldTypeParameter && newSymbol is ITypeParameterSymbol newTypeParameter) @@ -3741,7 +3867,7 @@ private static void AnalyzeBaseTypes(INamedTypeSymbol oldType, INamedTypeSymbol } } - private static void AnalyzeParameterType(IParameterSymbol oldParameter, IParameterSymbol newParameter, ref RudeEditKind rudeEdit, ref bool hasGeneratedAttributeChange) + private static void AnalyzeParameterType(IParameterSymbol oldParameter, IParameterSymbol newParameter, EditAndContinueCapabilitiesGrantor capabilities, ref RudeEditKind rudeEdit, ref bool hasGeneratedAttributeChange, ref bool hasParameterTypeChange) { if (!ParameterTypesEquivalent(oldParameter, newParameter, exact: true)) { @@ -3749,6 +3875,22 @@ private static void AnalyzeParameterType(IParameterSymbol oldParameter, IParamet { hasGeneratedAttributeChange = true; } + else if (newParameter.ContainingType.IsDelegateType()) + { + // We don't allow changing parameter types in delegates + rudeEdit = RudeEditKind.TypeUpdate; + } + else if (AllowsDeletion(newParameter.ContainingSymbol)) + { + if (CanAddNewMember(newParameter.ContainingSymbol, capabilities)) + { + hasParameterTypeChange = true; + } + else + { + rudeEdit = RudeEditKind.ChangingTypeNotSupportedByRuntime; + } + } else { rudeEdit = RudeEditKind.TypeUpdate; @@ -3840,14 +3982,14 @@ private void AnalyzeSymbolUpdate( ReportCustomAttributeRudeEdits(diagnostics, oldSymbol, newSymbol, newNode, newCompilation, capabilities, out var hasAttributeChange, out var hasReturnTypeAttributeChange, cancellationToken); - ReportUpdatedSymbolDeclarationRudeEdits(diagnostics, oldSymbol, newSymbol, newNode, newCompilation, capabilities, out var hasGeneratedAttributeChange, out var hasGeneratedReturnTypeAttributeChange, out var hasParameterRename, cancellationToken); + ReportUpdatedSymbolDeclarationRudeEdits(diagnostics, oldSymbol, newSymbol, newNode, newCompilation, capabilities, out var hasGeneratedAttributeChange, out var hasGeneratedReturnTypeAttributeChange, out var hasParameterRename, out var hasParameterTypeChange, cancellationToken); hasAttributeChange |= hasGeneratedAttributeChange; hasReturnTypeAttributeChange |= hasGeneratedReturnTypeAttributeChange; - if (hasParameterRename) + if (hasParameterRename || hasParameterTypeChange) { Debug.Assert(newSymbol is IParameterSymbol); - AddParameterUpdateSemanticEdit(semanticEdits, (IParameterSymbol)newSymbol, syntaxMap, cancellationToken); + AddParameterUpdateSemanticEdit(semanticEdits, (IParameterSymbol)oldSymbol, (IParameterSymbol)newSymbol, syntaxMap, reportDeleteAndInsertEdits: hasParameterTypeChange, cancellationToken); } else if (hasAttributeChange || hasReturnTypeAttributeChange) { @@ -3905,18 +4047,30 @@ private static void AddCustomAttributeSemanticEdits( } else if (newSymbol is IParameterSymbol newParameterSymbol) { - AddParameterUpdateSemanticEdit(semanticEdits, newParameterSymbol, syntaxMap, cancellationToken); + AddParameterUpdateSemanticEdit(semanticEdits, (IParameterSymbol)oldSymbol, newParameterSymbol, syntaxMap, reportDeleteAndInsertEdits: false, cancellationToken); } } - private static void AddParameterUpdateSemanticEdit(ArrayBuilder semanticEdits, IParameterSymbol newParameterSymbol, Func? syntaxMap, CancellationToken cancellationToken) + private static void AddParameterUpdateSemanticEdit(ArrayBuilder semanticEdits, IParameterSymbol oldParameterSymbol, IParameterSymbol newParameterSymbol, Func? syntaxMap, bool reportDeleteAndInsertEdits, CancellationToken cancellationToken) { var newContainingSymbol = newParameterSymbol.ContainingSymbol; - semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newContainingSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + + if (reportDeleteAndInsertEdits) + { + var oldContainingSymbol = oldParameterSymbol.ContainingSymbol; + var containingSymbolKey = SymbolKey.Create(oldContainingSymbol.ContainingSymbol, cancellationToken); + AddMemberOrAssociatedMemberSemanticEdits(semanticEdits, SemanticEditKind.Delete, oldContainingSymbol, containingSymbolKey, syntaxMap, partialType: null, cancellationToken); + AddMemberOrAssociatedMemberSemanticEdits(semanticEdits, SemanticEditKind.Insert, newContainingSymbol, containingSymbolKey: null, syntaxMap, partialType: null, cancellationToken); + } + else + { + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newContainingSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + } // attributes applied on parameters of a delegate are applied to both Invoke and BeginInvoke methods if (newContainingSymbol.ContainingSymbol is INamedTypeSymbol { TypeKind: TypeKind.Delegate } newContainingDelegateType) { + Debug.Assert(reportDeleteAndInsertEdits == false); AddDelegateBeginInvokeEdit(semanticEdits, newContainingDelegateType, syntaxMap, cancellationToken); } } diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index 20aff5aba372d..581f1a07d5bd0 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -154,6 +154,8 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.RenamingNotSupportedByRuntime, nameof(FeaturesResources.Renaming_0_requires_restarting_the_application_because_it_is_not_supported_by_the_runtime)); AddRudeEdit(RudeEditKind.ChangingNonCustomAttribute, nameof(FeaturesResources.Changing_pseudo_custom_attribute_0_of_1_requires_restarting_th_application)); AddRudeEdit(RudeEditKind.ChangingNamespace, nameof(FeaturesResources.Changing_the_containing_namespace_of_0_from_1_to_2_requires_restarting_th_application)); + AddRudeEdit(RudeEditKind.ChangingTypeNotSupportedByRuntime, nameof(FeaturesResources.Changing_the_type_of_0_requires_restarting_the_application)); + AddRudeEdit(RudeEditKind.DeleteNotSupportedByRuntime, nameof(FeaturesResources.Deleting_0_requires_restarting_the_application_because_is_not_supported_by_the_runtime)); // VB specific AddRudeEdit(RudeEditKind.HandlesClauseUpdate, nameof(FeaturesResources.Updating_the_Handles_clause_of_0_requires_restarting_the_application)); diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index 51651d78ee10a..1a80974d810b6 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -136,5 +136,7 @@ internal enum RudeEditKind : ushort RenamingNotSupportedByRuntime = 107, ChangingNonCustomAttribute = 108, ChangingNamespace = 109, + ChangingTypeNotSupportedByRuntime = 110, + DeleteNotSupportedByRuntime = 111, } } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 5d22c93b49f96..00713bd6ad116 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -3160,4 +3160,7 @@ Zero-width positive lookbehind assertions are typically used at the beginning of required Used in the object initializer completion. + + Deleting {0} requires restarting the application because is not supported by the runtime. + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index fc81964687648..6d4a2f0509c96 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -625,6 +625,11 @@ Ujistěte se, že specifikátor tt použijete pro jazyky, pro které je nezbytn Odstranění {0} vyžaduje restartování aplikace. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Odstranění zachycené proměnné {0} vyžaduje restartování aplikace. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index dfcefd7d2f568..fcb9a7f7fa708 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -625,6 +625,11 @@ Stellen Sie sicher, dass Sie den Bezeichner "tt" für Sprachen verwenden, für d Das Löschen von {0} erfordert einen Neustart der Anwendung. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Das Löschen der erfassten Variable „{0}“ erfordert einen Neustart der Anwendung. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index 0476042de34b8..e4c7b91321829 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -625,6 +625,11 @@ Asegúrese de usar el especificador "tt" para los idiomas para los que es necesa Para eliminar {0} es necesario reiniciar la aplicación. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Para eliminar la variable capturada "{0}" se requiere reiniciar la aplicación. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 786a4ca4349a0..ab6877ec74b6f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -625,6 +625,11 @@ Veillez à utiliser le spécificateur "tt" pour les langues où il est nécessai La suppression de {0} requiert le redémarrage de l’application. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. La suppression de la variable capturée « {0} » requiert le redémarrage de l’application. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 46ff3f18ceb58..b0e17a0668950 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -625,6 +625,11 @@ Assicurarsi di usare l'identificatore "tt" per le lingue per le quali è necessa Se si elimina {0}, è necessario riavviare l'applicazione. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Se si elimina la variabile catturata '{0}', è necessario riavviare l'applicazione. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 13dba9a5b47c3..c43450b07f983 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -625,6 +625,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {0} を削除すると、アプリケーションを再起動する必要があります。 + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. キャプチャした変数 '{0}' を削除するには、アプリケーションを再起動する必要があります。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index b46cb7fe768eb..2ff4e56ae595a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -625,6 +625,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma {0}을(를) 삭제하려면 애플리케이션을 다시 시작해야 합니다. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. 캡처된 변수 '{0}'을(를) 삭제하려면 응용 프로그램을 다시 시작해야 합니다. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index ab1d1d1b5288a..050a8e23bd1ba 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -625,6 +625,11 @@ Pamiętaj, aby nie używać specyfikatora „tt” dla wszystkich języków, w k Usunięcie elementu {0} wymaga ponownego uruchomienia aplikacji. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Usuwanie przechwyconej zmiennej "{0}" wymaga ponownego uruchomienia aplikacji. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 8941ba79a07d4..178728bae5b1b 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -625,6 +625,11 @@ Verifique se o especificador "tt" foi usado para idiomas para os quais é necess A exclusão de {0} requer o reinício do aplicativo. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Excluir a variável capturada '{0}' requer a reinicialização do aplicativo. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index 772e70a1005c4..0dde4f21239ea 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -625,6 +625,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma Для удаления {0} требуется перезапустить приложение. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Для удаления зафиксированной переменной "{0}" требуется перезапустить приложение. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 4517309894f21..88c4a53f74427 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -625,6 +625,11 @@ AM ve PM arasındaki farkın korunmasının gerekli olduğu diller için "tt" be {0} öğesinin silinmesi, uygulamanın yeniden başlatılmasını gerektirir. + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. Yakalanan '{0}' değişkenini silmek, uygulamanın yeniden başlatılmasını gerektirir. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 48e3229a4a009..9198cfda6708e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -625,6 +625,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 删除 {0} 需要重启应用程序。 + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. 删除捕获的变量“{0}”需要重新启动应用程序。 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 2ccbbd30eb5bb..f10b6283c4384 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -625,6 +625,11 @@ Make sure to use the "tt" specifier for languages for which it's necessary to ma 刪除 {0} 需要重新啟動應用程式。 + + Deleting {0} requires restarting the application because is not supported by the runtime. + Deleting {0} requires restarting the application because is not supported by the runtime. + + Deleting captured variable '{0}' requires restarting the application. 刪除擷取到的變數 '{0}' 需要重新啟動應用程式。