From a948f33fb742d5dadd40af92746c4e16be164748 Mon Sep 17 00:00:00 2001 From: tmat Date: Thu, 15 Jul 2021 16:46:29 -0700 Subject: [PATCH 1/3] Implements support for reloadable types in the IDE --- .../EditAndContinue/ActiveStatementTests.cs | 106 ++- .../Helpers/EditingTestBase.cs | 5 + .../EditAndContinue/LineEditTests.cs | 51 ++ .../EditAndContinue/TopLevelEditingTests.cs | 694 +++++++++++++++--- .../ActiveStatementsDescription.cs | 2 +- .../EditAndContinueTestHelpers.cs | 12 +- .../EditAndContinue/ActiveStatementTests.vb | 93 ++- .../Helpers/EditingTestBase.vb | 9 + .../EditAndContinue/LineEditTests.vb | 44 ++ .../EditAndContinue/TopLevelEditingTests.vb | 645 ++++++++++++++-- .../AbstractEditAndContinueAnalyzer.cs | 196 ++++- .../EditAndContinue/ActiveStatementsMap.cs | 3 + .../EditAndContinueDiagnosticDescriptors.cs | 4 +- .../Portable/EditAndContinue/EditSession.cs | 49 +- .../Portable/EditAndContinue/RudeEditKind.cs | 3 +- .../EditAndContinue/SemanticEditInfo.cs | 3 +- .../Core/Portable/FeaturesResources.resx | 3 + .../Portable/xlf/FeaturesResources.cs.xlf | 5 + .../Portable/xlf/FeaturesResources.de.xlf | 5 + .../Portable/xlf/FeaturesResources.es.xlf | 5 + .../Portable/xlf/FeaturesResources.fr.xlf | 5 + .../Portable/xlf/FeaturesResources.it.xlf | 5 + .../Portable/xlf/FeaturesResources.ja.xlf | 5 + .../Portable/xlf/FeaturesResources.ko.xlf | 5 + .../Portable/xlf/FeaturesResources.pl.xlf | 5 + .../Portable/xlf/FeaturesResources.pt-BR.xlf | 5 + .../Portable/xlf/FeaturesResources.ru.xlf | 5 + .../Portable/xlf/FeaturesResources.tr.xlf | 5 + .../xlf/FeaturesResources.zh-Hans.xlf | 5 + .../xlf/FeaturesResources.zh-Hant.xlf | 5 + 30 files changed, 1785 insertions(+), 202 deletions(-) diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs index d0dd3d4d2180e..ebbb1ab30742a 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -63,6 +63,87 @@ static void Goo(int a) Diagnostic(RudeEditKind.ActiveStatementUpdate, "Goo(2);")); } + [Fact] + public void Update_Inner_NewCommentAtEndOfActiveStatement() + { + var src1 = @" +class C +{ + static void Main(string[] args) + { + Goo(1); + } + + static void Goo(int a) + { + Console.WriteLine(a); + } +}"; + var src2 = @" +class C +{ + static void Main(string[] args) + { + Goo(1);// + } + + static void Goo(int a) + { + Console.WriteLine(a); + } +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active); + } + + /// + /// CreateNewOnMetadataUpdate has no effect in presence of active statements (in break mode). + /// + [Fact] + public void Update_Inner_Reloadable() + { + var src1 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ + static void Main() + { + Goo(1); + } + + static void Goo(int a) + { + Console.WriteLine(a); + } +}"; + var src2 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ + static void Main() + { + while (true) + { + Goo(2); + } + } + + static void Goo(int a) + { + Console.WriteLine(a); + } +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementUpdate, "Goo(2);")); + } + [Fact] public void Update_Leaf() { @@ -138,10 +219,14 @@ static void Goo(int a) edits.VerifyRudeDiagnostics(active); } + /// + /// CreateNewOnMetadataUpdate has no effect in presence of active statements (in break mode). + /// [Fact] - public void Update_Inner_NewCommentAtEndOfActiveStatement() + public void Update_Leaf_Reloadable() { - var src1 = @" + var src1 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] class C { static void Main(string[] args) @@ -154,24 +239,33 @@ static void Goo(int a) Console.WriteLine(a); } }"; - var src2 = @" + var src2 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] class C { static void Main(string[] args) { - Goo(1);// + while (true) + { + Goo(1); + } } static void Goo(int a) { - Console.WriteLine(a); + Console.WriteLine(a + 1); } } "; var edits = GetTopEdits(src1, src2); var active = GetActiveStatements(src1, src2); - edits.VerifyRudeDiagnostics(active); + edits.VerifySemantics(active, + expectedSemanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Main"), preserveLocalVariables: true), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Goo"), preserveLocalVariables: true) + }); } [WorkItem(846588, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/846588")] diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs index fd25bce6cc13c..95d02f9154777 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs @@ -22,6 +22,11 @@ namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests { public abstract class EditingTestBase : CSharpTestBase { + public static readonly string ReloadableAttributeSrc = @" +using System.Runtime.CompilerServices; +namespace System.Runtime.CompilerServices { class CreateNewOnMetadataUpdateAttribute : Attribute {} } +"; + internal static CSharpEditAndContinueAnalyzer CreateAnalyzer() { return new CSharpEditAndContinueAnalyzer(testFaultInjector: null); diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs index 2ff87944d57d0..b635fe864d72f 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/LineEditTests.cs @@ -1012,6 +1012,32 @@ class C new SourceLineUpdate[] { new SourceLineUpdate(3, 4) }); } + [Fact] + public void Field_LineChange_Reloadable() + { + var src1 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ + int Goo = 1, Bar = 2; +} +"; + var src2 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ + int Goo = 1, + Bar = 2; +}"; + var edits = GetTopEdits(src1, src2); + edits.VerifyLineEdits( + Array.Empty(), + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")) + }); + } + [Fact] public void Field_Recompile1a() { @@ -1178,6 +1204,31 @@ class C }); } + [Fact] + public void Field_Generic_Reloadable() + { + var src1 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ + static int Goo = 1 + 1; +} +"; + var src2 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ + static int Goo = 1 + 1; +}"; + var edits = GetTopEdits(src1, src2); + edits.VerifyLineEdits( + Array.Empty(), + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")) + }); + } + #endregion #region Properties diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index 610c6ff3870b8..20e6cbce4bf11 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -500,6 +500,7 @@ public void Reorder_TopLevelAttribute() [InlineData("class", "struct")] [InlineData("class", "record")] // TODO: Allow this conversion: https://github.com/dotnet/roslyn/issues/51874 [InlineData("class", "record struct")] + [InlineData("class", "interface")] [InlineData("struct", "record struct")] // TODO: Allow this conversion: https://github.com/dotnet/roslyn/issues/51874 public void Type_Kind_Update(string oldKeyword, string newKeyword) { @@ -515,6 +516,26 @@ public void Type_Kind_Update(string oldKeyword, string newKeyword) Diagnostic(RudeEditKind.TypeKindUpdate, newKeyword + " C")); } + [Theory] + [InlineData("class", "struct")] + [InlineData("class", "record")] + [InlineData("class", "record struct")] + [InlineData("class", "interface")] + [InlineData("struct", "record struct")] + public void Type_Kind_Update_Reloadable(string oldKeyword, string newKeyword) + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]" + oldKeyword + " C { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]" + newKeyword + " C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[CreateNewOnMetadataUpdate]" + oldKeyword + " C { }]@145 -> [[CreateNewOnMetadataUpdate]" + newKeyword + " C { }]@145"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + [Fact] public void Type_Modifiers_Static_Remove() { @@ -595,6 +616,21 @@ public void Type_Modifiers_Internal_Add() edits.VerifySemantics(); } + [Fact] + public void Type_Modifiers_Accessibility_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public class C { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[CreateNewOnMetadataUpdate]public class C { }]@145 -> [[CreateNewOnMetadataUpdate]internal class C { }]@145"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + [Theory] [InlineData("class")] [InlineData("struct")] @@ -851,6 +887,65 @@ public void Type_Attribute_Delete_NotSupportedByRuntime2() Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); } + [Fact] + public void Type_Attribute_Change_Reloadable() + { + var attributeSrc = @" +public class A1 : System.Attribute { } +public class A2 : System.Attribute { } +public class A3 : System.Attribute { } +"; + + var src1 = ReloadableAttributeSrc + attributeSrc + "[CreateNewOnMetadataUpdate, A1, A2]class C { }"; + var src2 = ReloadableAttributeSrc + attributeSrc + "[CreateNewOnMetadataUpdate, A2, A3]class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[CreateNewOnMetadataUpdate, A1, A2]class C { }]@267 -> [[CreateNewOnMetadataUpdate, A2, A3]class C { }]@267"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + + [Fact] + public void Type_Attribute_ReloadableRemove() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }"; + var src2 = ReloadableAttributeSrc + "class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + + [Fact] + public void Type_Attribute_ReloadableAdd() + { + var src1 = ReloadableAttributeSrc + "class C { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")) }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + [Fact] + public void Type_Attribute_ReloadableBase() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class B { } class C : B { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class B { } class C : B { void F() {} }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + [Fact] public void Type_Attribute_Add() { @@ -952,61 +1047,83 @@ public void Type_Attribute_ReorderAndUpdate_NotSupportedByRuntime() Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); } - [Fact] - public void Class_Name_Update1() + [Theory] + [InlineData("class")] + [InlineData("struct")] + [InlineData("interface")] + [InlineData("record")] + [InlineData("record struct")] + public void Type_Rename(string keyword) { - var src1 = "class C { }"; - var src2 = "class D { }"; + var src1 = keyword + " C { }"; + var src2 = keyword + " D { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [class C { }]@0 -> [class D { }]@0"); + "Update [" + keyword + " C { }]@0 -> [" + keyword + " D { }]@0"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "class D", FeaturesResources.class_)); + Diagnostic(RudeEditKind.Renamed, keyword + " D", GetResource(keyword))); } [Fact] - public void Class_Name_Update2() + public void Type_Rename_AddAndDeleteMember() { - var src1 = "class LongerName { }"; - var src2 = "class LongerMame { }"; + var src1 = "class C { int x = 1; }"; + var src2 = "class D { void F() { } }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [class LongerName { }]@0 -> [class LongerMame { }]@0"); + "Update [class C { int x = 1; }]@0 -> [class D { void F() { } }]@0", + "Insert [void F() { }]@10", + "Insert [()]@16", + "Delete [int x = 1;]@10", + "Delete [int x = 1]@10", + "Delete [x = 1]@14"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "class LongerMame", FeaturesResources.class_)); + Diagnostic(RudeEditKind.Renamed, "class D", FeaturesResources.class_)); } [Fact] - public void Interface_Name_Update() + [WorkItem(54886, "https://github.com/dotnet/roslyn/issues/54886")] + public void Type_Rename_Reloadable() { - var src1 = "interface C { }"; - var src2 = "interface D { }"; + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class D { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [interface C { }]@0 -> [interface D { }]@0"); + "Update [[CreateNewOnMetadataUpdate]class C { }]@145 -> [[CreateNewOnMetadataUpdate]class D { }]@145"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "interface D", FeaturesResources.interface_)); + // TODO: expected: Replace edit of D + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Renamed, "class D", FeaturesResources.class_)); } [Fact] - public void Struct_Name_Update() + [WorkItem(54886, "https://github.com/dotnet/roslyn/issues/54886")] + public void Type_Rename_Reloadable_AddAndDeleteMember() { - var src1 = "struct C { }"; - var src2 = "struct D { }"; + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { int x = 1; }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class D { void F() { } }"; var edits = GetTopEdits(src1, src2); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "struct D", CSharpFeaturesResources.struct_)); + edits.VerifyEdits( + "Update [[CreateNewOnMetadataUpdate]class C { int x = 1; }]@145 -> [[CreateNewOnMetadataUpdate]class D { void F() { } }]@145", + "Insert [void F() { }]@182", + "Insert [()]@188", + "Delete [int x = 1;]@182", + "Delete [int x = 1]@182", + "Delete [x = 1]@186"); + + // TODO: expected: Replace edit of D + edits.VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Renamed, "class D", FeaturesResources.class_)); } [Fact] @@ -1332,7 +1449,29 @@ interface J {}"; } [Fact] - public void ClassInsert_AbstractVirtualOverride() + public void Type_Reloadable_NotSupportedByRuntime() + { + var src1 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +public class C +{ + void F() { System.Console.WriteLine(1); } +}"; + var src2 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +public class C +{ + void F() { System.Console.WriteLine(2); } +}"; + + var edits = GetTopEdits(src1, src2); + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.BaselineCapabilities, + Diagnostic(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, "void F()", "CreateNewOnMetadataUpdateAttribute")); + } + + [Fact] + public void Type_Insert_AbstractVirtualOverride() { var src1 = ""; var src2 = @" @@ -1340,7 +1479,7 @@ public abstract class C { public abstract void F(); public virtual void G() {} - public override void H() {} + public override string ToString() => null; }"; var edits = GetTopEdits(src1, src2); @@ -1348,7 +1487,7 @@ public override void H() {} } [Fact] - public void ClassInsert_NotSupportedByRuntime() + public void Type_Insert_NotSupportedByRuntime() { var src1 = @" public class C @@ -1378,6 +1517,18 @@ void M() Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "public class D", FeaturesResources.class_)); } + [Fact] + public void Type_Insert_Reloadable() + { + var src1 = ReloadableAttributeSrc + ""; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { void F() {} }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"))); + } + [Fact] public void InterfaceInsert() { @@ -1665,7 +1816,7 @@ static void M() { } } [Fact] - public void GenericType_InsertMembers() + public void Type_Generic_InsertMembers() { var src1 = @" using System; @@ -1702,6 +1853,85 @@ class D {} Diagnostic(RudeEditKind.InsertIntoGenericType, "F2", FeaturesResources.field)); } + [Fact] + public void Type_Generic_InsertMembers_Reloadable() + { + var src1 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ +} +"; + var src2 = ReloadableAttributeSrc + @" +[CreateNewOnMetadataUpdate] +class C +{ + void M() {} + int P1 { get; set; } + int P2 { get => 1; set {} } + int this[int i] { get => 1; set {} } + event System.Action E { add {} remove {} } + event System.Action EF; + int F1, F2; + + enum E {} + interface I {} + class D {} +} +"; + var edits = GetTopEdits(src1, src2); + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + + [Fact] + public void Type_Generic_DeleteInsert() + { + var srcA1 = @" +class C { void F() {} } +struct S { void F() {} } +interface I { void F() {} } +"; + var srcB1 = ""; + + var srcA2 = srcB1; + var srcB2 = srcA1; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + + DocumentResults( + diagnostics: new[] + { + Diagnostic(RudeEditKind.GenericTypeUpdate, "class C"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "struct S"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "interface I"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "void F()"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "void F()"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "void F()"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), + Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), + }) + }); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/54881")] + [WorkItem(54881, "https://github.com/dotnet/roslyn/issues/54881")] + public void Type_TypeParameter_Insert_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public class C { void F() { } }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal class C { int x = 1; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + [Fact] public void Type_Delete() { @@ -1719,7 +1949,17 @@ interface I { void F() {} } } [Fact] - public void PartialType_Delete() + public void Type_Delete_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { void F() {} }"; + var src2 = ReloadableAttributeSrc; + + GetTopEdits(src1, src2).VerifySemanticDiagnostics( + Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.class_, "C"))); + } + + [Fact] + public void Type_Partial_DeleteDeclaration() { var srcA1 = "partial class C { void F() {} void M() { } }"; var srcB1 = "partial class C { void G() {} }"; @@ -1742,7 +1982,7 @@ public void PartialType_Delete() } [Fact] - public void PartialType_InsertFirstDeclaration() + public void Type_Partial_InsertFirstDeclaration() { var src1 = ""; var src2 = "partial class C { void F() {} }"; @@ -1753,11 +1993,11 @@ public void PartialType_InsertFirstDeclaration() } [Fact] - public void PartialType_InsertSecondDeclaration() + public void Type_Partial_InsertSecondDeclaration() { var srcA1 = "partial class C { void F() {} }"; var srcB1 = ""; - var srcA2 = "partial class C { void F() {} }"; + var srcA2 = "partial class C { void F() {} }"; var srcB2 = "partial class C { void G() {} }"; EditAndContinueValidation.VerifySemantics( @@ -1774,6 +2014,27 @@ public void PartialType_InsertSecondDeclaration() }); } + [Fact] + public void Type_Partial_Reloadable() + { + var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { void F() {} }"; + var srcB1 = ""; + var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { void F() {} }"; + var srcB2 = "partial class C { void G() {} }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C") + }), + }); + } + [Fact] public void Type_DeleteInsert() { @@ -1804,36 +2065,23 @@ interface I { void F() {} } } [Fact] - public void GenericType_DeleteInsert() + public void Type_DeleteInsert_Reloadable() { - var srcA1 = @" -class C { void F() {} } -struct S { void F() {} } -interface I { void F() {} } -"; + var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { void F() {} }"; var srcB1 = ""; - var srcA2 = srcB1; - var srcB2 = srcA1; + var srcA2 = ReloadableAttributeSrc; + var srcB2 = "[CreateNewOnMetadataUpdate]class C { void F() {} }"; EditAndContinueValidation.VerifySemantics( new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, new[] { DocumentResults(), - DocumentResults( - diagnostics: new[] + semanticEdits: new[] { - Diagnostic(RudeEditKind.GenericTypeUpdate, "class C"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "struct S"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "interface I"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "void F()"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "void F()"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "void F()"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), - Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")), }) }); } @@ -1972,7 +2220,7 @@ class C { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P").GetMethod), SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P").SetMethod), - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), partialType: "C", preserveLocalVariables: true), + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").InstanceConstructors.Single(), preserveLocalVariables: true), }) }); } @@ -3381,6 +3629,20 @@ public void EnumInitializerUpdate3() Diagnostic(RudeEditKind.InitializerUpdate, "Red = int.MaxValue", FeaturesResources.enum_value)); } + [Fact] + public void EnumInitializerUpdate_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]enum Color { Red = 1 }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]enum Color { Red = 2 }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits("Update [Red = 1]@185 -> [Red = 2]@185"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("Color"))); + } + [Fact] public void EnumInitializerAdd() { @@ -3731,6 +3993,18 @@ public void Delegates_Parameter_Insert() Diagnostic(RudeEditKind.Insert, "int a", FeaturesResources.parameter)); } + [Fact] + public void Delegates_Parameter_Insert_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public delegate int D();"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal delegate bool D(int a);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("D"))); + } + [Fact] public void Delegates_Parameter_Delete() { @@ -3832,6 +4106,19 @@ public void Delegates_TypeParameter_Insert() Diagnostic(RudeEditKind.Insert, "T", FeaturesResources.type_parameter)); } + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/54881")] + [WorkItem(54881, "https://github.com/dotnet/roslyn/issues/54881")] + public void Delegates_TypeParameter_Insert_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public delegate int D();"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal delegate bool D(int a);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("D"))); + } + [Fact] public void Delegates_TypeParameter_Delete() { @@ -4183,6 +4470,66 @@ public void NestedClass_Insert4() edits.VerifyRudeDiagnostics(); } + [Fact] + public void NestedClass_Insert_ReloadableIntoReloadable1() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { [CreateNewOnMetadataUpdate]class D { } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + + [Fact] + public void NestedClass_Insert_ReloadableIntoReloadable2() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { [CreateNewOnMetadataUpdate]class D { [CreateNewOnMetadataUpdate]class E { } } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + + [Fact] + public void NestedClass_Insert_ReloadableIntoReloadable3() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { class D { [CreateNewOnMetadataUpdate]class E { } } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + + [Fact] + public void NestedClass_Insert_ReloadableIntoReloadable4() + { + var src1 = ReloadableAttributeSrc + "class C { }"; + var src2 = ReloadableAttributeSrc + "class C { [CreateNewOnMetadataUpdate]class D { [CreateNewOnMetadataUpdate]class E { } } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.D"))); + } + + [Fact] + public void NestedClass_Insert_Member_Reloadable() + { + var src1 = ReloadableAttributeSrc + "class C { [CreateNewOnMetadataUpdate]class D { } }"; + var src2 = ReloadableAttributeSrc + "class C { [CreateNewOnMetadataUpdate]class D { int x; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C.D"))); + } + [Fact] public void NestedClass_InsertMemberWithInitializer1() { @@ -4575,7 +4922,32 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange() } [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Attribute() + public void Type_Partial_AddMultiple() + { + var srcA1 = ""; + var srcB1 = ""; + + var srcA2 = "partial class C { }"; + var srcB2 = "partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"), partialType: "C") + }), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"), partialType: "C") + }), + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + [Fact] + public void Type_Partial_InsertDeleteAndChange_Attribute() { var srcA1 = "partial class C { }"; var srcB1 = ""; @@ -4592,7 +4964,7 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Attribute() DocumentResults(), DocumentResults(semanticEdits: new[] { - SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")) + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"), partialType: "C") }), DocumentResults(), }, @@ -4600,7 +4972,7 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Attribute() } [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteAndChange_TypeParameterAttribute_NotSupportedByRuntime() + public void Type_Partial_InsertDeleteAndChange_TypeParameterAttribute_NotSupportedByRuntime() { var srcA1 = "partial class C { }"; var srcB1 = ""; @@ -4629,7 +5001,7 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange_TypeParameterAt } [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Constraint() + public void Type_Partial_InsertDeleteAndChange_Constraint() { var srcA1 = "partial class C { }"; var srcB1 = ""; @@ -4657,11 +5029,8 @@ public void NestedPartialTypeInPartialType_InsertDeleteAndChange_Constraint() }); } - /// - /// Moves partial classes to different files while moving around their attributes and base interfaces. - /// [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteRefactor() + public void Type_Partial_InsertDeleteRefactor() { var srcA1 = "partial class C : I { void F() { } }"; var srcB1 = "[A][B]partial class C : J { void G() { } }"; @@ -4692,12 +5061,38 @@ public void NestedPartialTypeInPartialType_InsertDeleteRefactor() }); } - /// - /// Moves partial classes to different files while moving around their attributes and base interfaces. - /// Currently we do not support splitting attribute lists. - /// [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteRefactor_AttributeListSplitting() + public void Type_Partial_Attribute_AddMultiple() + { + var attributes = @" +class A : System.Attribute {} +class B : System.Attribute {} +"; + + var srcA1 = "partial class C { }" + attributes; + var srcB1 = "partial class C { }"; + + var srcA2 = "[A]partial class C { }" + attributes; + var srcB2 = "[B]partial class C { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"), partialType: "C") + }), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"), partialType: "C") + }), + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + [Fact] + public void Type_Partial_InsertDeleteRefactor_AttributeListSplitting() { var srcA1 = "partial class C { void F() { } }"; var srcB1 = "[A,B]partial class C { void G() { } }"; @@ -4727,7 +5122,7 @@ public void NestedPartialTypeInPartialType_InsertDeleteRefactor_AttributeListSpl } [Fact] - public void NestedPartialTypeInPartialType_InsertDeleteChangeMember() + public void Type_Partial_InsertDeleteChangeMember() { var srcA1 = "partial class C { void F(int y = 1) { } }"; var srcB1 = "partial class C { void G(int x = 1) { } }"; @@ -7403,7 +7798,7 @@ public class SubClass : BaseClass, IConflict } [Fact] - public void PartialMethod_DeleteInsert_DefinitionPart() + public void Method_Partial_DeleteInsert_DefinitionPart() { var srcA1 = "partial class C { partial void F(); }"; var srcB1 = "partial class C { partial void F() { } }"; @@ -7424,7 +7819,7 @@ public void PartialMethod_DeleteInsert_DefinitionPart() } [Fact] - public void PartialMethod_DeleteInsert_ImplementationPart() + public void Method_Partial_DeleteInsert_ImplementationPart() { var srcA1 = "partial class C { partial void F(); }"; var srcB1 = "partial class C { partial void F() { } }"; @@ -7446,7 +7841,7 @@ public void PartialMethod_DeleteInsert_ImplementationPart() } [Fact, WorkItem(51011, "https://github.com/dotnet/roslyn/issues/51011")] - public void PartialMethod_Swap_ImplementationAndDefinitionParts() + public void Method_Partial_Swap_ImplementationAndDefinitionParts() { var srcA1 = "partial class C { partial void F(); }"; var srcB1 = "partial class C { partial void F() { } }"; @@ -7465,7 +7860,7 @@ public void PartialMethod_Swap_ImplementationAndDefinitionParts() } [Fact] - public void PartialMethod_DeleteImplementation() + public void Method_Partial_DeleteImplementation() { var srcA1 = "partial class C { partial void F(); }"; var srcB1 = "partial class C { partial void F() { } }"; @@ -7485,7 +7880,7 @@ public void PartialMethod_DeleteImplementation() } [Fact] - public void PartialMethod_DeleteBoth() + public void Method_Partial_DeleteBoth() { var srcA1 = "partial class C { partial void F(); }"; var srcB1 = "partial class C { partial void F() { } }"; @@ -7505,7 +7900,7 @@ public void PartialMethod_DeleteBoth() } [Fact] - public void PartialMethod_DeleteInsertBoth() + public void Method_Partial_DeleteInsertBoth() { var srcA1 = "partial class C { partial void F(); }"; var srcB1 = "partial class C { partial void F() { } }"; @@ -7530,7 +7925,7 @@ public void PartialMethod_DeleteInsertBoth() } [Fact] - public void PartialMethod_Insert() + public void Method_Partial_Insert() { var srcA1 = "partial class C { }"; var srcB1 = "partial class C { }"; @@ -7549,6 +7944,25 @@ public void PartialMethod_Insert() }); } + [Fact] + public void Method_Partial_Insert_Reloadable() + { + var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { }"; + var srcB1 = "partial class C { }"; + + var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { partial void F(); }"; + var srcB2 = "partial class C { partial void F() { } }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(), + DocumentResults( + semanticEdits: new[] { SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C") }), + }); + } + #endregion #region Operators @@ -7569,6 +7983,18 @@ public void Operator_Modifiers_Update(string oldModifiers, string newModifiers) Diagnostic(RudeEditKind.ModifiersUpdate, "public static " + newModifiers + " operator int (C c)", CSharpFeaturesResources.conversion_operator)); } + [Fact] + public void Operator_Modifiers_Update_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { public static implicit operator int (C c) => 0; }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { public static explicit operator int (C c) => 0; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + [Fact] public void Operator_Conversion_ExternModifiers_Add() { @@ -8041,7 +8467,7 @@ public void ConstructorUpdate_AnonymousTypeInFieldInitializer() } [Fact] - public void StaticCtorDelete() + public void Constructor_Static_Delete() { var src1 = "class C { static C() { } }"; var src2 = "class C { }"; @@ -8052,6 +8478,31 @@ public void StaticCtorDelete() Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.static_constructor, "C()"))); } + [Fact] + public void Constructor_Static_Delete_Reloadable() + { + var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { static C() { } }"; + var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))); + } + + [Fact] + public void Constructor_Static_Insert() + { + var src1 = "class C { }"; + var src2 = "class C { static C() { } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").StaticConstructors.Single()) }); + } + [Fact] public void InstanceCtorDelete_Public() { @@ -8105,19 +8556,6 @@ public void InstanceCtorDelete_Public_PartialWithInitializerUpdate() }); } - [Fact] - public void StaticCtorInsert() - { - var src1 = "class C { }"; - var src2 = "class C { static C() { } }"; - - var edits = GetTopEdits(src1, src2); - - edits.VerifySemantics( - ActiveStatementsDescription.Empty, - new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").StaticConstructors.Single()) }); - } - [Fact] public void InstanceCtorInsert_Public_Implicit() { @@ -9082,6 +9520,32 @@ public void PartialDeclaration_Insert() }); } + [Fact] + public void PartialDeclaration_Insert_Reloadable() + { + var srcA1 = ""; + var srcB1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int x = 1; void F() { } }"; + + var srcA2 = "partial class C { public C() { } void F() { } }"; + var srcB2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int x = 2; }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults( + semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C") + }), + + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C") + }), + }); + } + [Fact, WorkItem(17681, "https://github.com/dotnet/roslyn/issues/17681")] public void Constructor_BlockBodyToExpressionBody() { @@ -14753,6 +15217,66 @@ public void TypeTypeParameterAttributeUpdate() Diagnostic(RudeEditKind.GenericTypeUpdate, "T")); } + [Fact] + public void TypeTypeParameter_Partial_Attribute_AddMultiple() + { + var attributes = @" +class A : System.Attribute {} +class B : System.Attribute {} +"; + + var srcA1 = "partial class C { }" + attributes; + var srcB1 = "partial class C { }"; + + var srcA2 = "partial class C<[A]T> { }" + attributes; + var srcB2 = "partial class C<[B]T> { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(diagnostics: new[] + { + Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), + }), + DocumentResults(diagnostics: new[] + { + Diagnostic(RudeEditKind.GenericTypeUpdate, "T"), + }), + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + + [Fact] + public void TypeTypeParameter_Partial_Attribute_AddMultiple_Reloadable() + { + var attributes = @" +class A : System.Attribute {} +class B : System.Attribute {} +"; + + var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { }" + attributes; + var srcB1 = "partial class C { }"; + + var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C<[A]T> { }" + attributes; + var srcB2 = "partial class C<[B]T> { }"; + + EditAndContinueValidation.VerifySemantics( + new[] { GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2) }, + new[] + { + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C") + }), + DocumentResults(semanticEdits: new[] + { + SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C") + }), + }, + capabilities: EditAndContinueTestHelpers.Net6RuntimeCapabilities); + } + #endregion #region Type Parameter Constraints diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs index f329fc8af81e2..8424a41fc7ae3 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/ActiveStatementsDescription.cs @@ -50,7 +50,7 @@ public ActiveStatementsDescription(string oldMarkedSource, string newMarkedSourc OldStatementsMap = new ActiveStatementsMap( documentPathMap: oldDocumentMap.ToImmutableDictionary(e => e.Key, e => e.Value.OrderBy(ActiveStatementsMap.Comparer).ToImmutableArray()), - instructionMap: ActiveStatementsMap.Empty.InstructionMap); + instructionMap: OldStatements.ToDictionary(s => new ManagedInstructionId(new ManagedMethodId(Guid.NewGuid(), 0x060000001, version: 1), ilOffset: 0), s => s.Statement)); var newActiveStatementMarkers = GetActiveSpans(newMarkedSource).ToArray(); diff --git a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs index 8c4f13303c736..a7f89cabeae84 100644 --- a/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs +++ b/src/EditorFeatures/TestUtilities/EditAndContinue/EditAndContinueTestHelpers.cs @@ -202,6 +202,14 @@ internal void VerifySemantics(EditScript[] editScripts, TargetFramew } } + var duplicateNonPartial = allEdits + .Where(e => e.PartialType == null) + .GroupBy(e => e.Symbol, SymbolKey.GetComparer(ignoreCase: false, ignoreAssemblyKeys: true)) + .Where(g => g.Count() > 1) + .Select(g => g.Key); + + AssertEx.Empty(duplicateNonPartial, "Duplicate non-partial symbols"); + // check if we can merge edits without throwing: EditSession.MergePartialEdits(oldProject.GetCompilationAsync().Result!, newProject.GetCompilationAsync().Result!, allEdits, out var _, out var _, CancellationToken.None); } @@ -244,13 +252,13 @@ private void VerifySemanticEdits( Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol); Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol); } - else if (editKind == SemanticEditKind.Insert) + else if (editKind is SemanticEditKind.Insert or SemanticEditKind.Replace) { Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol); } else { - Assert.False(true, "Only Update or Insert allowed"); + Assert.False(true, "Only Update, Insert or Replace allowed"); } // Partial types must match: diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb index dbe805088b668..d579ee52514a1 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb @@ -3,6 +3,7 @@ ' See the LICENSE file in the project root for more information. Imports Microsoft.CodeAnalysis.EditAndContinue +Imports Microsoft.CodeAnalysis.Emit Imports Microsoft.VisualStudio.Debugger.Contracts.EditAndContinue Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests @@ -45,6 +46,72 @@ End Class Diagnostic(RudeEditKind.ActiveStatementUpdate, "Goo(2)")) End Sub + + Public Sub Update_Inner_NewCommentAtEndOfActiveStatement() + Dim src1 = " +Class C + Shared Sub Main() + Goo(1) + End Sub + + Shared Sub Goo(a As Integer) + Console.WriteLine(a) + End Sub +End Class +" + Dim src2 = " +Class C + Shared Sub Main() + Goo(1)' comment + End Sub + + Shared Sub Goo(a As Integer) + Console.WriteLine(a) + End Sub +End Class +" + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + edits.VerifyRudeDiagnostics(active) + End Sub + + + Public Sub Update_Inner_Reloadable() + Dim src1 = ReloadableAttributeSrc & " + +Class C + Shared Sub Main() + Goo(1) + End Sub + + Shared Sub Goo(a As Integer) + Console.WriteLine(a) + End Sub +End Class +" + + Dim src2 = ReloadableAttributeSrc & " + +Class C + Shared Sub Main() + While True + Goo(2) + End While + End Sub + + Shared Sub Goo(a As Integer) + Console.WriteLine(a) + End Sub +End Class +" + + Dim edits = GetTopEdits(src1, src2) + Dim active = GetActiveStatements(src1, src2) + + edits.VerifyRudeDiagnostics(active, + Diagnostic(RudeEditKind.ActiveStatementUpdate, "Goo(2)")) + End Sub + Public Sub Update_Leaf() Dim src1 = " @@ -109,9 +176,14 @@ End Class edits.VerifyRudeDiagnostics(active) End Sub + ' + ' CreateNewOnMetadataUpdate has no effect in presence of active statements (in break mode). + ' - Public Sub Update_Inner_NewCommentAtEndOfActiveStatement() - Dim src1 = " + Public Sub Update_Leaf_Reloadable() + + Dim src1 = ReloadableAttributeSrc + " +[CreateNewOnMetadataUpdate] Class C Shared Sub Main() Goo(1) @@ -122,20 +194,29 @@ Class C End Sub End Class " - Dim src2 = " + Dim src2 = ReloadableAttributeSrc + " +[CreateNewOnMetadataUpdate] Class C Shared Sub Main() - Goo(1)' comment + While True + Goo(1) + End While End Sub Shared Sub Goo(a As Integer) - Console.WriteLine(a) + Console.WriteLine(a + 1) End Sub End Class " Dim edits = GetTopEdits(src1, src2) Dim active = GetActiveStatements(src1, src2) - edits.VerifyRudeDiagnostics(active) + + edits.VerifySemantics(active, + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C.Main"), preserveLocalVariables:=True), + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C.Goo"), preserveLocalVariables:=True) + }) End Sub diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb index 062db44057304..eb728104e3244 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/Helpers/EditingTestBase.vb @@ -19,6 +19,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue.UnitTests Public MustInherit Class EditingTestBase Inherits BasicTestBase + Public Shared ReadOnly ReloadableAttributeSrc As String = " +Imports System.Runtime.CompilerServices +Namespace System.Runtime.CompilerServices + Class CreateNewOnMetadataUpdateAttribute + Inherits Attribute + End Class +End Namespace +" + Friend Shared Function CreateAnalyzer() As VisualBasicEditAndContinueAnalyzer Return New VisualBasicEditAndContinueAnalyzer() End Function diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb index 4f9a5ed622202..a9a9802c6fde8 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/LineEditTests.vb @@ -772,6 +772,28 @@ End Class {SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True)}) End Sub + + Public Sub Field_Init_Recompile_Reloadable() + Dim src1 = ReloadableAttributeSrc & " + +Class C + Dim Goo As Integer = 1 + 1 +End Class +" + + Dim src2 = ReloadableAttributeSrc & " + +Class C + Dim Goo As Integer = 1 + 1 +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifyLineEdits( + Array.Empty(Of SequencePointUpdates), + {SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + Public Sub Field_SingleAsNew_Recompile1() Dim src1 = " @@ -938,6 +960,28 @@ End Class Array.Empty(Of SequencePointUpdates), diagnostics:={Diagnostic(RudeEditKind.GenericTypeUpdate, "Class C(Of T)")}) End Sub + + + Public Sub Field_Generic_Reloadable() + Dim src1 = ReloadableAttributeSrc & " + +Class C(Of T) + Dim Goo As Integer = 1 + 1 +End Class +" + + Dim src2 = ReloadableAttributeSrc & " + +Class C(Of T) + Dim Goo As Integer = 1 + 1 +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifyLineEdits( + Array.Empty(Of SequencePointUpdates), + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub #End Region #Region "Auto-Properties" diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index 76ea68af92414..ff30e3a7a95b0 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -441,6 +441,19 @@ Option Strict Off Diagnostic(RudeEditKind.TypeKindUpdate, newKeyword & " C")) End Sub + + + + + Public Sub Type_Kind_Update_Reloadable(oldKeyword As String, newKeyword As String) + Dim src1 = ReloadableAttributeSrc & "" & oldKeyword & " C : End " & oldKeyword + Dim src2 = ReloadableAttributeSrc & "" & newKeyword & " C : End " & newKeyword + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + Public Sub Type_Modifiers_Accessibility_Change(accessibility As String) @@ -507,6 +520,15 @@ Option Strict Off edits.VerifySemantics() End Sub + + Public Sub Type_Modifiers_Accessibility_Reloadable() + Dim src1 = ReloadableAttributeSrc + "Friend Class C : End Class" + Dim src2 = ReloadableAttributeSrc + "Public Class C : End Class" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics(semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + @@ -543,6 +565,34 @@ Option Strict Off edits.VerifySemantics() End Sub + + + + + + Public Sub Type_PartialModifier_Add(keyword As String) + Dim src1 = keyword & " C : End " & keyword + Dim src2 = "Partial " & keyword & " C : End " & keyword + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [" & keyword & " C]@0 -> [Partial " & keyword & " C]@0") + + edits.VerifyRudeDiagnostics() + End Sub + + + Public Sub Module_PartialModifier_Remove() + Dim src1 = "Partial Module C : End Module" + Dim src2 = "Module C : End Module" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Partial Module C]@0 -> [Module C]@0") + + edits.VerifyRudeDiagnostics() + End Sub + Public Sub Type_Attribute_Update_NotSupportedByRuntime1() Dim attribute = "Public Class A1Attribute : Inherits System.Attribute : End Class" & vbCrLf & @@ -576,32 +626,165 @@ Option Strict Off Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, keyword & " C", GetResource(keyword))) End Sub + + Public Sub Type_Attribute_Change_Reloadable() + Dim attributeSrc = " +Public Class A1 : Inherits System.Attribute : End Class +Public Class A2 : Inherits System.Attribute : End Class +Public Class A3 : Inherits System.Attribute : End Class +" + Dim src1 = ReloadableAttributeSrc & attributeSrc & "Class C : End Class" + Dim src2 = ReloadableAttributeSrc & attributeSrc & "Class C : End Class" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifyEdits( + "Update [Class C]@363 -> [Class C]@363") + + edits.VerifySemantics(semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + + + Public Sub Type_Attribute_ReloadableRemove() + + Dim src1 = ReloadableAttributeSrc & "Class C : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : End Class" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics(semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + + + Public Sub Type_Attribute_ReloadableAdd() + + Dim src1 = ReloadableAttributeSrc & "Class C : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : End Class" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C"))}, + capabilities:=EditAndContinueTestHelpers.Net6RuntimeCapabilities) + End Sub + + + Public Sub Type_Attribute_ReloadableBase() + + Dim src1 = ReloadableAttributeSrc & " + +Class B +End Class + +Class C + Inherits B +End Class +" + Dim src2 = ReloadableAttributeSrc & " + +Class B +End Class + +Class C + Inherits B + + Sub F() + End Sub +End Class +" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + - + - Public Sub Type_PartialModifier_Add(keyword As String) + Public Sub Type_Rename1(keyword As String) Dim src1 = keyword & " C : End " & keyword - Dim src2 = "Partial " & keyword & " C : End " & keyword + Dim src2 = keyword & " c : End " & keyword Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits( - "Update [" & keyword & " C]@0 -> [Partial " & keyword & " C]@0") + edits.VerifyEdits("Update [" & keyword & " C]@0 -> [" & keyword & " c]@0") - edits.VerifyRudeDiagnostics() + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, keyword & " c", GetResource(keyword))) End Sub - Public Sub Module_PartialModifier_Remove() - Dim src1 = "Partial Module C : End Module" - Dim src2 = "Module C : End Module" + Public Sub Type_Rename2() + Dim src1 = "Class C : End Class" + Dim src2 = "Class D : End Class" + Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Partial Module C]@0 -> [Module C]@0") + "Update [Class C]@0 -> [Class D]@0") - edits.VerifyRudeDiagnostics() + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "Class D", FeaturesResources.class_)) + End Sub + + + Public Sub Type_Rename_AddAndDeleteMember() + Dim src1 = " +Class C + Dim x As Integer = 1 +End Class" + Dim src2 = " +Class D + Sub F() + End Sub +End Class" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "Class D", FeaturesResources.class_)) + End Sub + + + + Public Sub Type_Rename_Reloadable() + Dim src1 = ReloadableAttributeSrc & " + +Class C +End Class" + Dim src2 = ReloadableAttributeSrc & " + +Class D +End Class" + + Dim edits = GetTopEdits(src1, src2) + + ' TODO: expected: Replace edit of D + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "Class D", FeaturesResources.class_)) + End Sub + + + + Public Sub Type_Rename_Reloadable_AddAndDeleteMember() + Dim src1 = ReloadableAttributeSrc & " + +Class C + Dim x As Integer = 1 +End Class" + Dim src2 = ReloadableAttributeSrc & " + +Class D + Sub F() + End Sub +End Class" + + Dim edits = GetTopEdits(src1, src2) + + ' TODO: expected: Replace edit of D + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Renamed, "Class D", FeaturesResources.class_)) End Sub @@ -674,32 +857,6 @@ Option Strict Off Diagnostic(RudeEditKind.Delete, Nothing, DeletedSymbolDisplay(VBFeaturesResources.module_, "C"))) End Sub - - Public Sub ClassRename1() - Dim src1 = "Class C : End Class" - Dim src2 = "class c : end class" - Dim edits = GetTopEdits(src1, src2) - - edits.VerifyEdits("Update [Class C]@0 -> [class c]@0") - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "class c", FeaturesResources.class_)) - End Sub - - - Public Sub ClassRename2() - Dim src1 = "Class C : End Class" - Dim src2 = "Class D : End Class" - - Dim edits = GetTopEdits(src1, src2) - - edits.VerifyEdits( - "Update [Class C]@0 -> [Class D]@0") - - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Renamed, "Class D", FeaturesResources.class_)) - End Sub - Public Sub InterfaceNameReplace() Dim src1 = "Interface C : End Interface" @@ -884,7 +1041,32 @@ Interface J : End Interface End Sub - Public Sub ClassInsert_AbstractVirtualOverride() + Public Sub Type_Reloadable_NotSupportedByRuntime() + Dim src1 = ReloadableAttributeSrc & " + +Class C + Sub F() + System.Console.WriteLine(1) + End Sub +End Class +" + Dim src2 = ReloadableAttributeSrc & " + +Class C + Sub F() + System.Console.WriteLine(2) + End Sub +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics( + capabilities:=EditAndContinueTestHelpers.BaselineCapabilities, + diagnostics:={Diagnostic(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, "Sub F()", "CreateNewOnMetadataUpdateAttribute")}) + End Sub + + + Public Sub Type_Insert_AbstractVirtualOverride() Dim src1 = "" Dim src2 = " Public MustInherit Class C(Of T) @@ -898,6 +1080,49 @@ End Class edits.VerifyRudeDiagnostics() End Sub + + Public Sub Type_Insert_NotSupportedByRuntime() + Dim src1 = " +Class C + Sub F() + End Sub +End Class +" + Dim src2 = " +Class C + Sub F() + End Sub +End Class + +Class D + Sub M() + End Sub +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics( + capabilities:=EditAndContinueTestHelpers.BaselineCapabilities, + diagnostics:={Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "Class D", FeaturesResources.class_)}) + End Sub + + + Public Sub Type_Insert_Reloadable() + Dim src1 = ReloadableAttributeSrc & " +" + Dim src2 = ReloadableAttributeSrc & " + +Class C + Sub F() + End Sub +End Class +" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C"))}) + End Sub + Public Sub InterfaceInsert() Dim src1 = "" @@ -993,7 +1218,7 @@ End Interface End Sub - Public Sub GenericType_InsertMembers() + Public Sub Type_Generic_InsertMembers() Dim src1 = " Imports System Class C(Of T) @@ -1065,6 +1290,65 @@ End Class" Diagnostic(RudeEditKind.GenericTypeUpdate, "Class C(Of T)")) End Sub + + Public Sub Type_Generic_InsertMembers_Reloadable() + Dim src1 = ReloadableAttributeSrc & " + +Class C(Of T) +End Class +" + Dim src2 = ReloadableAttributeSrc & " + +Class C(Of T) + Dim F1, F2 As New Object, F3 As Integer, F4 As New Object, F5(1, 2), F6? As Integer + + Sub M() + End Sub + + Property P1(i As Integer) As Integer + Get + Return 1 + End Get + Set(value As Integer) + End Set + End Property + + Property P2 As Integer + Property P3 As New Object + + Event E1(sender As Object, e As System.EventArgs) + + Event E2 As System.Action + + Custom Event E3 As System.EventHandler + AddHandler(value As System.EventHandler) + End AddHandler + RemoveHandler(value As System.EventHandler) + End RemoveHandler + RaiseEvent(sender As Object, e As System.EventArgs) + End RaiseEvent + End Event + + Dim WithEvents WE As Object + + Enum N + A + End Enum + + Interface I + End Interface + + Class D + End Class + + Delegate Sub G() +End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + Public Sub Type_Delete() Dim src1 = " @@ -1100,7 +1384,7 @@ Delegate Sub D() End Sub - Public Sub PartialType_Delete() + Public Sub Type_Partial_DeleteDeclaration() Dim srcA1 = " Partial Class C Sub F() @@ -1136,7 +1420,7 @@ End Class" End Sub - Public Sub PartialType_InsertFirstDeclaration() + Public Sub Type_Partial_InsertFirstDeclaration() Dim src1 = "" Dim src2 = " Partial Class C @@ -1150,7 +1434,7 @@ End Class" End Sub - Public Sub PartialType_InsertSecondDeclaration() + Public Sub Type_Partial_InsertSecondDeclaration() Dim srcA1 = " Partial Class C Sub F() @@ -1181,6 +1465,43 @@ End Class" }) End Sub + + Public Sub Type_Partial_Reloadable() + Dim srcA1 = ReloadableAttributeSrc & " + +Partial Class C + Sub F() + End Sub +End Class +" + Dim srcB1 = "" + + Dim srcA2 = ReloadableAttributeSrc & " + +Partial Class C + Sub F() + End Sub +End Class +" + Dim srcB2 = " +Partial Class C + Sub G() + End Sub +End Class +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"), partialType:="C") + }) + }) + End Sub + Public Sub Type_DeleteInsert() Dim srcA1 = " @@ -1226,7 +1547,39 @@ Delegate Sub D() End Sub - Public Sub GenericType_DeleteInsert() + Public Sub Type_DeleteInsert_Reloadable() + Dim srcA1 = ReloadableAttributeSrc & " + +Class C + Sub F() + End Sub +End Class +" + Dim srcB1 = "" + + Dim srcA2 = ReloadableAttributeSrc + Dim srcB2 = " + +Class C + Sub F() + End Sub +End Class +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C")) + }) + }) + End Sub + + + Public Sub Type_Generic_DeleteInsert() Dim srcA1 = " Class C(Of T) Sub F() @@ -1267,6 +1620,27 @@ End Interface }) End Sub + + + Public Sub Type_TypeParameter_Insert_Reloadable() + Dim src1 = ReloadableAttributeSrc & " + +Class C(Of T) + Sub F() + End Sub +End Class +" + Dim src2 = ReloadableAttributeSrc & " + +Class C(Of T, S) + Dim x As Integer = 1 +End Class +" + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + Public Sub Type_DeleteInsert_NonInsertableMembers() Dim srcA1 = " @@ -1329,7 +1703,7 @@ End Class DocumentResults( semanticEdits:= { - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True) + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), preserveLocalVariables:=True) }) }) End Sub @@ -1597,6 +1971,19 @@ End Class Diagnostic(RudeEditKind.InitializerUpdate, "Red = Integer.MaxValue", FeaturesResources.enum_value)) End Sub + + Public Sub Enum_MemberInitializer_Update_Reloadable() + Dim src1 = ReloadableAttributeSrc & "Enum Color : Red = 1 : End Enum" + Dim src2 = ReloadableAttributeSrc & "Enum Color : Red = 2 : End Enum" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyEdits( + "Update [Red = 1]@230 -> [Red = 2]@230") + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("Color"))}) + End Sub + Public Sub Enum_MemberInitializer_Insert() Dim src1 = "Enum Color : Red : End Enum" @@ -1796,6 +2183,16 @@ End Class Diagnostic(RudeEditKind.Insert, "a As Integer", FeaturesResources.parameter)) End Sub + + Public Sub Delegates_Parameter_Insert_Reloadable() + Dim src1 = ReloadableAttributeSrc & "Public Delegate Function D() As Integer" + Dim src2 = ReloadableAttributeSrc & "Friend Delegate Function D(a As Integer) As Boolean" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("D"))}) + End Sub + Public Sub Delegates_Parameter_Delete() Dim src1 = "Public Delegate Function D(a As Integer) As Integer" @@ -1868,6 +2265,17 @@ End Class Diagnostic(RudeEditKind.Insert, "T", FeaturesResources.type_parameter)) End Sub + + + Public Sub Delegates_TypeParameter_Insert_Reloadable() + Dim src1 = ReloadableAttributeSrc & "Public Delegate Function D(Of T)() As Integer" + Dim src2 = ReloadableAttributeSrc & "Friend Delegate Function D(Of In T, Out S)(a As Integer) As Integer" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "T", FeaturesResources.type_parameter)) + End Sub + Public Sub Delegates_TypeParameter_Delete() Dim src1 = "Public Delegate Function D(Of T)() As Integer" @@ -2072,6 +2480,56 @@ End Class edits.VerifyRudeDiagnostics() End Sub + + Public Sub NestedClass_Insert_ReloadableIntoReloadable1() + Dim src1 = ReloadableAttributeSrc & "Class C : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : Class D : End Class : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + + + Public Sub NestedClass_Insert_ReloadableIntoReloadable2() + Dim src1 = ReloadableAttributeSrc & "Class C : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : Class D : Class E : End Class : End Class : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + + + Public Sub NestedClass_Insert_ReloadableIntoReloadable3() + Dim src1 = ReloadableAttributeSrc & "Class C : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : Class D : Class E : End Class : End Class : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + + + Public Sub NestedClass_Insert_ReloadableIntoReloadable4() + Dim src1 = ReloadableAttributeSrc & "Class C : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : Class D : Class E : End Class : End Class : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember("C.D"))}) + End Sub + + + Public Sub NestedClass_Insert_Member_Reloadable() + Dim src1 = ReloadableAttributeSrc & "Class C : Class D : End Class : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : Class D : Dim X As Integer = 1 : End Class : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C.D"))}) + End Sub + Public Sub NestedClass_InsertMemberWithInitializer1() Dim src1 = "Public Class C : End Class" @@ -2485,7 +2943,7 @@ End Structure DocumentResults( semanticEdits:= { - SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C")) + SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C"), partialType:="C") }), DocumentResults() }, @@ -4319,7 +4777,7 @@ End Interface End Sub - Public Sub PartialMethod_DeleteInsert_DefinitionPart() + Public Sub Method_Partial_DeleteInsert_DefinitionPart() Dim srcA1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" Dim srcB1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" Dim srcC1 = "Partial Class C : End Class" @@ -4339,7 +4797,7 @@ End Interface End Sub - Public Sub PartialMethod_DeleteInsert_ImplementationPart() + Public Sub Method_Partial_DeleteInsert_ImplementationPart() Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" Dim srcC1 = "Partial Class C : End Class" @@ -4359,7 +4817,7 @@ End Interface End Sub - Public Sub PartialMethod_Swap_ImplementationAndDefinitionParts() + Public Sub Method_Partial_Swap_ImplementationAndDefinitionParts() Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" @@ -4377,7 +4835,7 @@ End Interface End Sub - Public Sub PartialMethod_DeleteImplementation() + Public Sub Method_Partial_DeleteImplementation() Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" @@ -4394,7 +4852,7 @@ End Interface End Sub - Public Sub PartialMethod_DeleteBoth() + Public Sub Method_Partial_DeleteBoth() Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" @@ -4411,7 +4869,7 @@ End Interface End Sub - Public Sub PartialMethod_DeleteInsertBoth() + Public Sub Method_Partial__DeleteInsertBoth() Dim srcA1 = "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" Dim srcB1 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" Dim srcC1 = "Partial Class C : End Class" @@ -4434,7 +4892,7 @@ End Interface End Sub - Public Sub PartialMethod_Insert() + Public Sub Method_Partial_Insert() Dim srcA1 = "Partial Class C : End Class" Dim srcB1 = "Partial Class C : End Class" @@ -4449,6 +4907,23 @@ End Interface semanticEdits:={SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember(Of MethodSymbol)("F").PartialImplementationPart)}) }) End Sub + + + Public Sub Method_Partial_Insert_Reloadable() + Dim srcA1 = ReloadableAttributeSrc & "Partial Class C : End Class" + Dim srcB1 = "Partial Class C : End Class" + + Dim srcA2 = ReloadableAttributeSrc & "Partial Class C" + vbCrLf + "Partial Private Sub F() : End Sub : End Class" + Dim srcB2 = "Partial Class C" + vbCrLf + "Private Sub F() : End Sub : End Class" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults(), + DocumentResults( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"), partialType:="C")}) + }) + End Sub #End Region #Region "Operators" @@ -4468,6 +4943,17 @@ End Interface Diagnostic(RudeEditKind.ModifiersUpdate, "Public Shared " & newModifiers & " Operator CType(d As C)", FeaturesResources.operator_)) End Sub + + Public Sub Operator_Modifiers_Update_Reloadable() + Dim src1 = ReloadableAttributeSrc & "Class C" & vbCrLf & "Public Shared Narrowing Operator CType(d As C) As Integer : End Operator : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C" & vbCrLf & "Public Shared Widening Operator CType(d As C) As Integer : End Operator : End Class" + + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + Public Sub OperatorInsert() Dim src1 = " @@ -4692,7 +5178,7 @@ End Class End Sub - Public Sub StaticCtorDelete() + Public Sub Constructor_Shared_Delete() Dim src1 = "Class C" & vbLf & "Shared Sub New() : End Sub : End Class" Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) @@ -4701,6 +5187,16 @@ End Class Diagnostic(RudeEditKind.Delete, "Class C", DeletedSymbolDisplay(VBFeaturesResources.Shared_constructor, "New()"))) End Sub + + Public Sub Constructor_Shared_Delete_Reloadable() + Dim src1 = ReloadableAttributeSrc & "Class C" & vbLf & "Shared Sub New() : End Sub : End Class" + Dim src2 = ReloadableAttributeSrc & "Class C : End Class" + Dim edits = GetTopEdits(src1, src2) + + edits.VerifySemantics( + semanticEdits:={SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"))}) + End Sub + Public Sub ModuleCtorDelete() Dim src1 = "Module C" & vbLf & "Sub New() : End Sub : End Module" @@ -5629,6 +6125,49 @@ End Class }) End Sub + + Public Sub PartialDeclaration_Insert_Reloadable() + Dim srcA1 = "" + Dim srcB1 = ReloadableAttributeSrc & " + +Partial Class C + Dim x = 1 + + Sub F() + End Sub +End Class +" + Dim srcA2 = " +Partial Class C + Public Sub New() + End Sub + + Sub F() + End Sub +End Class" + Dim srcB2 = ReloadableAttributeSrc & " + +Partial Class C + Dim x = 2 +End Class +" + + EditAndContinueValidation.VerifySemantics( + {GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)}, + { + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"), partialType:="C") + }), + DocumentResults( + semanticEdits:= + { + SemanticEdit(SemanticEditKind.Replace, Function(c) c.GetMember("C"), partialType:="C") + }) + }) + End Sub + #End Region #Region "Declare" diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 176a52ebd9c13..1380886d85ccc 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -1,4 +1,5 @@ // Licensed to the .NET Foundation under one or more agreements. +// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. @@ -28,6 +29,7 @@ namespace Microsoft.CodeAnalysis.EditAndContinue internal abstract class AbstractEditAndContinueAnalyzer : IEditAndContinueAnalyzer { internal const int DefaultStatementPart = 0; + private const string CreateNewOnMetadataUpdateAttributeName = "CreateNewOnMetadataUpdateAttribute"; /// /// Contains enough information to determine whether two symbols have the same signature. @@ -582,6 +584,9 @@ public async Task AnalyzeDocumentAsync( new RudeEditDiagnostic(RudeEditKind.ExperimentalFeaturesEnabled, default)), hasChanges); } + // We are in break state when there are no active statements. + var inBreakState = !oldActiveStatementMap.IsEmpty; + // We do calculate diffs even if there are semantic errors for the following reasons: // 1) We need to be able to find active spans in the new document. // If we didn't calculate them we would only rely on tracking spans (might be ok). @@ -642,6 +647,7 @@ public async Task AnalyzeDocumentAsync( newActiveStatements, newExceptionRegions, capabilities, + inBreakState, cancellationToken).ConfigureAwait(false); cancellationToken.ThrowIfCancellationRequested(); @@ -2451,8 +2457,11 @@ private async Task> AnalyzeSemanticsAsync( ImmutableArray.Builder newActiveStatements, ImmutableArray>.Builder newExceptionRegions, EditAndContinueCapabilities capabilities, + bool inBreakState, CancellationToken cancellationToken) { + Debug.Assert(inBreakState || newActiveStatementSpans.IsEmpty); + if (editScript.Edits.Length == 0 && triviaEdits.Count == 0) { return ImmutableArray.Empty; @@ -2535,6 +2544,77 @@ private async Task> AnalyzeSemanticsAsync( } } + if (!inBreakState) + { + // Delete/insert/update edit of a member of a reloadable type (including nested types) results in Replace edit of the containing type. + // If a Delete edit is part of delete-insert operation (member moved to a different partial type declaration or to a different file) + // skip producing Replace semantic edit for this Delete edit as one will be reported by the corresponding Insert edit. + + var oldContainingType = oldSymbol?.ContainingType; + var newContainingType = newSymbol?.ContainingType; + var containingType = newContainingType ?? oldContainingType; + + if (containingType != null && (syntacticEditKind != EditKind.Delete || newSymbol == null)) + { + var containingTypeSymbolKey = SymbolKey.Create(containingType, cancellationToken); + oldContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + newContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + + if (oldContainingType != null && newContainingType != null && IsReloadable(oldContainingType)) + { + if (processedSymbols.Add(newContainingType)) + { + if (capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)) + { + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Replace, containingTypeSymbolKey, syntaxMap: null, syntaxMapTree: null, + IsPartialEdit(oldContainingType, newContainingType, editScript.Match.OldRoot.SyntaxTree, editScript.Match.NewRoot.SyntaxTree) ? containingTypeSymbolKey : null)); + } + else + { + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, newContainingType, newDeclaration, cancellationToken); + } + } + + continue; + } + } + + var oldType = oldSymbol as INamedTypeSymbol; + var newType = newSymbol as INamedTypeSymbol; + + // Deleting a reloadable type is a rude edit, reported the same as for non-reloadable. + // Adding a reloadable type is a standard type addition (TODO: unless added to a reloadable type?). + // Making reloadable attribute non-reloadable results in a new version of the type that is + // not reloadable but does not update the old version in-place. + if (syntacticEditKind != EditKind.Delete && oldType != null && newType != null && IsReloadable(oldType)) + { + if (symbol == newType || processedSymbols.Add(newType)) + { + if (oldType.Name != newType.Name) + { + // https://github.com/dotnet/roslyn/issues/54886 + ReportUpdateRudeEdit(diagnostics, RudeEditKind.Renamed, newType, newDeclaration, cancellationToken); + } + else if (oldType.Arity != newType.Arity) + { + // https://github.com/dotnet/roslyn/issues/54881 + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingTypeParameters, newType, newDeclaration, cancellationToken); + } + else if (!capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)) + { + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, newType, newDeclaration, cancellationToken); + } + else + { + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Replace, symbolKey, syntaxMap: null, syntaxMapTree: null, + IsPartialEdit(oldType, newType, editScript.Match.OldRoot.SyntaxTree, editScript.Match.NewRoot.SyntaxTree) ? symbolKey : null)); + } + } + + continue; + } + } + switch (syntacticEditKind) { case EditKind.Delete: @@ -2655,8 +2735,8 @@ private async Task> AnalyzeSemanticsAsync( // Check if the symbol being deleted is a member of a type that's also being deleted. // If so, skip the member deletion and only report the containing symbol deletion. var containingSymbolKey = SymbolKey.Create(oldSymbol.ContainingType, cancellationToken); - var newContatiningSymbol = containingSymbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; - if (newContatiningSymbol == null) + var newContainingSymbol = containingSymbolKey.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol; + if (newContainingSymbol == null) { continue; } @@ -2789,7 +2869,7 @@ private async Task> AnalyzeSemanticsAsync( if (isDeclarationWithInitializer) { - AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, editScript.Match, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); } DeferConstructorEdit(oldSymbol.ContainingType, newContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); @@ -2978,7 +3058,7 @@ private async Task> AnalyzeSemanticsAsync( if (isDeclarationWithInitializer) { - AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, editScript.Match, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); } DeferConstructorEdit(oldSymbol.ContainingType, newSymbol.ContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); @@ -3001,16 +3081,15 @@ private async Task> AnalyzeSemanticsAsync( { Contract.ThrowIfNull(oldSymbol); - AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + AnalyzeSymbolUpdate(oldSymbol, newSymbol, edit.NewNode, newCompilation, editScript.Match, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); if (newSymbol is INamedTypeSymbol or IFieldSymbol or IPropertySymbol or IEventSymbol or IParameterSymbol or ITypeParameterSymbol) { continue; } } - // Edits in data member initializers and constructors are deferred, edits of other members (even on partial types) - // do not need merging accross partial type declarations. - semanticEdits.Add(new SemanticEditInfo(editKind, symbolKey, syntaxMap, syntaxMapTree: null, partialType: null)); + semanticEdits.Add(new SemanticEditInfo(editKind, symbolKey, syntaxMap, syntaxMapTree: null, + IsPartialEdit(oldSymbol, newSymbol, editScript.Match.OldRoot.SyntaxTree, editScript.Match.NewRoot.SyntaxTree) ? symbolKey : null)); } } @@ -3037,6 +3116,27 @@ private async Task> AnalyzeSemanticsAsync( var oldContainingType = oldSymbol.ContainingType; var newContainingType = newSymbol.ContainingType; + Contract.ThrowIfNull(oldContainingType); + Contract.ThrowIfNull(newContainingType); + + if (IsReloadable(oldContainingType)) + { + if (processedSymbols.Add(newContainingType)) + { + if (capabilities.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)) + { + var containingTypeSymbolKey = SymbolKey.Create(oldContainingType, cancellationToken); + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Replace, containingTypeSymbolKey, syntaxMap: null, syntaxMapTree: null, + IsPartialEdit(oldContainingType, newContainingType, editScript.Match.OldRoot.SyntaxTree, editScript.Match.NewRoot.SyntaxTree) ? containingTypeSymbolKey : null)); + } + else + { + ReportUpdateRudeEdit(diagnostics, RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, newContainingType, newDeclaration, cancellationToken); + } + } + + continue; + } // We need to provide syntax map to the compiler if the member is active (see member update above): var isActiveMember = @@ -3080,10 +3180,9 @@ private async Task> AnalyzeSemanticsAsync( continue; } - // Edits in data member initializers and constructors are deferred, edits of other members (even on partial types) - // do not need merging accross partial type declarations. var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); - semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, syntaxMapTree: null, partialType: null)); + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, syntaxMapTree: null, + IsPartialEdit(oldSymbol, newSymbol, editScript.Match.OldRoot.SyntaxTree, editScript.Match.NewRoot.SyntaxTree) ? symbolKey : null)); } } @@ -3141,6 +3240,27 @@ private async Task> AnalyzeSemanticsAsync( } } + private static bool IsReloadable(INamedTypeSymbol type) + { + var current = type; + while (current != null) + { + foreach (var attributeData in current.GetAttributes()) + { + // We assume that the attribute System.Runtime.CompilerServices.CreateNewOnMetadataUpdateAttribute, if it exists, is well formed. + // If not an error will be reported during EnC delta emit. + if (attributeData.AttributeClass is { Name: CreateNewOnMetadataUpdateAttributeName, ContainingNamespace: { Name: "CompilerServices", ContainingNamespace: { Name: "Runtime", ContainingNamespace: { Name: "System" } } } }) + { + return true; + } + } + + current = current.BaseType; + } + + return false; + } + private sealed class SemanticEditInfoComparer : IEqualityComparer { public static SemanticEditInfoComparer Instance = new(); @@ -3519,6 +3639,7 @@ private void AnalyzeSymbolUpdate( ISymbol newSymbol, SyntaxNode? newNode, Compilation newCompilation, + Match topMatch, EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics, ArrayBuilder semanticEdits, @@ -3536,7 +3657,7 @@ private void AnalyzeSymbolUpdate( if (hasAttributeChange || hasReturnTypeAttributeChange) { - AddCustomAttributeSemanticEdits(semanticEdits, newSymbol, syntaxMap, hasAttributeChange, hasReturnTypeAttributeChange, cancellationToken); + AddCustomAttributeSemanticEdits(semanticEdits, oldSymbol, newSymbol, topMatch, syntaxMap, hasAttributeChange, hasReturnTypeAttributeChange, cancellationToken); } // updating generic methods and types @@ -3549,7 +3670,9 @@ private void AnalyzeSymbolUpdate( private static void AddCustomAttributeSemanticEdits( ArrayBuilder semanticEdits, + ISymbol oldSymbol, ISymbol newSymbol, + Match topMatch, Func? syntaxMap, bool hasAttributeChange, bool hasReturnTypeAttributeChange, @@ -3570,13 +3693,21 @@ private static void AddCustomAttributeSemanticEdits( AddDelegateBeginInvokeEdit(newDelegateType); } } - else if (newSymbol is INamedTypeSymbol or IFieldSymbol or IPropertySymbol or IEventSymbol) + else if (newSymbol is INamedTypeSymbol) { - semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, syntaxMapTree: null, + IsPartialEdit(oldSymbol, newSymbol, topMatch.OldRoot.SyntaxTree, topMatch.NewRoot.SyntaxTree) ? symbolKey : null)); } else if (newSymbol is ITypeParameterSymbol) { - semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newSymbol.ContainingSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); + var containingTypeSymbolKey = SymbolKey.Create(newSymbol.ContainingSymbol, cancellationToken); + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, containingTypeSymbolKey, syntaxMap, syntaxMapTree: null, + IsPartialEdit(oldSymbol.ContainingSymbol, newSymbol.ContainingSymbol, topMatch.OldRoot.SyntaxTree, topMatch.NewRoot.SyntaxTree) ? containingTypeSymbolKey : null)); + } + else if (newSymbol is IFieldSymbol or IPropertySymbol or IEventSymbol) + { + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, SymbolKey.Create(newSymbol, cancellationToken), syntaxMap, syntaxMapTree: null, partialType: null)); } else if (newSymbol is IParameterSymbol) { @@ -3870,11 +4001,19 @@ private void ReportUpdateRudeEdit(ArrayBuilder diagnostics, var node = newNode ?? GetRudeEditDiagnosticNode(newSymbol, cancellationToken); var span = (rudeEdit == RudeEditKind.ChangeImplicitMainReturnType) ? GetGlobalStatementDiagnosticSpan(node) : GetDiagnosticSpan(node, EditKind.Update); - var arguments = (rudeEdit is + var arguments = rudeEdit switch + { RudeEditKind.TypeKindUpdate or RudeEditKind.ChangeImplicitMainReturnType or RudeEditKind.GenericMethodUpdate or - RudeEditKind.GenericTypeUpdate) ? Array.Empty() : new[] { GetDisplayName(newSymbol) }; + RudeEditKind.GenericTypeUpdate + => Array.Empty(), + + RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime + => new[] { CreateNewOnMetadataUpdateAttributeName }, + + _ => new[] { GetDisplayName(newSymbol) } + }; diagnostics.Add(new RudeEditDiagnostic(rudeEdit, span, node, arguments)); } @@ -4131,15 +4270,7 @@ private void AddConstructorEdits( var oldType = updatesInCurrentDocument.OldType; var anyInitializerUpdatesInCurrentDocument = updatesInCurrentDocument.ChangedDeclarations.Keys.Any(IsDeclarationWithInitializer); - - // If any of the partial declarations of the new or the old type are in another document - // the edit will need to be merged with other partial edits with matching partial type - static bool IsNotInDocument(SyntaxReference reference, SyntaxTree syntaxTree) - => reference.SyntaxTree != syntaxTree; - - var isPartialEdit = - oldType.DeclaringSyntaxReferences.Any(IsNotInDocument, oldSyntaxTree) || - newType.DeclaringSyntaxReferences.Any(IsNotInDocument, newSyntaxTree); + var isPartialEdit = IsPartialEdit(oldType, newType, oldSyntaxTree, newSyntaxTree); // Create a syntax map that aggregates syntax maps of the constructor body and all initializers in this document. // Use syntax maps stored in update.ChangedDeclarations and fallback to 1:1 map for unchanged members. @@ -4298,7 +4429,7 @@ static bool IsNotInDocument(SyntaxReference reference, SyntaxTree syntaxTree) if (oldCtor != null) { - AnalyzeSymbolUpdate(oldCtor, newCtor, newDeclaration, newCompilation, capabilities, diagnostics, semanticEdits, syntaxMapToUse, cancellationToken); + AnalyzeSymbolUpdate(oldCtor, newCtor, newDeclaration, newCompilation, topMatch, capabilities, diagnostics, semanticEdits, syntaxMapToUse, cancellationToken); semanticEdits.Add(new SemanticEditInfo( SemanticEditKind.Update, @@ -4365,6 +4496,17 @@ private bool HasMemberInitializerContainingLambda(INamedTypeSymbol type, bool is } } + private static bool IsPartialEdit(ISymbol? oldSymbol, ISymbol? newSymbol, SyntaxTree oldSyntaxTree, SyntaxTree newSyntaxTree) + { + // If any of the partial declarations of the new or the old type are in another document + // the edit will need to be merged with other partial edits with matching partial type + static bool IsNotInDocument(SyntaxReference reference, SyntaxTree syntaxTree) + => reference.SyntaxTree != syntaxTree; + + return oldSymbol?.Kind == SymbolKind.NamedType && oldSymbol.DeclaringSyntaxReferences.Length > 1 && oldSymbol.DeclaringSyntaxReferences.Any(IsNotInDocument, oldSyntaxTree) || + newSymbol?.Kind == SymbolKind.NamedType && newSymbol.DeclaringSyntaxReferences.Length > 1 && newSymbol.DeclaringSyntaxReferences.Any(IsNotInDocument, newSyntaxTree); + } + #endregion #region Lambdas and Closures diff --git a/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs b/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs index f3ca5140cfa6c..9d6f485272b3a 100644 --- a/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs +++ b/src/Features/Core/Portable/EditAndContinue/ActiveStatementsMap.cs @@ -142,6 +142,9 @@ private static LinePositionSpan GetUpToDateSpan(ManagedActiveStatementDebugInfo return activeSpan; } + public bool IsEmpty + => InstructionMap.IsEmpty(); + internal async ValueTask> GetOldActiveStatementsAsync(IEditAndContinueAnalyzer analyzer, Document oldDocument, CancellationToken cancellationToken) { var oldTree = await oldDocument.DocumentState.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index edf44e821eef1..f6a6b4792dffe 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -139,20 +139,18 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di AddRudeEdit(RudeEditKind.MemberBodyTooBig, nameof(FeaturesResources.Modifying_body_of_member_will_prevent_the_debug_session_from_continuing_because_the_body_has_too_many_statements)); AddRudeEdit(RudeEditKind.SourceFileTooBig, nameof(FeaturesResources.Modifying_source_file_will_prevent_the_debug_session_from_continuing_because_the_file_is_too_big)); AddRudeEdit(RudeEditKind.InsertIntoGenericType, nameof(FeaturesResources.Adding_0_into_a_generic_type_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.ImplementRecordParameterAsReadOnly, nameof(FeaturesResources.Implementing_a_record_positional_parameter_0_as_read_only_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ImplementRecordParameterWithSet, nameof(FeaturesResources.Implementing_a_record_positional_parameter_0_with_a_set_accessor_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ExplicitRecordMethodParameterNamesMustMatch, nameof(FeaturesResources.Explicitly_implemented_methods_of_records_must_have_parameter_names_that_match_the_compiler_generated_equivalent_0)); - AddRudeEdit(RudeEditKind.NotSupportedByRuntime, nameof(FeaturesResources.Edit_and_continue_is_not_supported_by_the_runtime)); AddRudeEdit(RudeEditKind.MakeMethodAsync, nameof(FeaturesResources.Making_a_method_async_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.MakeMethodIterator, nameof(FeaturesResources.Making_a_method_an_iterator_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.InsertNotSupportedByRuntime, nameof(FeaturesResources.Adding_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ChangingAttributesNotSupportedByRuntime, nameof(FeaturesResources.Updating_the_attributes_of_0_is_not_supported_by_the_runtime)); + AddRudeEdit(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, nameof(FeaturesResources.Updating_reloadable_type_marked_by_0_attribute_is_not_supported_by_the_runtime)); AddRudeEdit(RudeEditKind.ChangingParameterTypes, nameof(FeaturesResources.Changing_parameter_types_of_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ChangingTypeParameters, nameof(FeaturesResources.Changing_type_parameters_of_0_will_prevent_the_debug_session_from_continuing)); AddRudeEdit(RudeEditKind.ChangingConstraints, nameof(FeaturesResources.Changing_constraints_of_0_will_prevent_the_debug_session_from_continuing)); - AddRudeEdit(RudeEditKind.ChangeImplicitMainReturnType, FeaturesResources.An_update_that_causes_the_return_type_of_implicit_main_to_change_will_prevent_the_debug_session_from_continuing); // VB specific diff --git a/src/Features/Core/Portable/EditAndContinue/EditSession.cs b/src/Features/Core/Portable/EditAndContinue/EditSession.cs index 2511afe8fbf26..04406ec9f2961 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditSession.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditSession.cs @@ -497,12 +497,12 @@ internal static void MergePartialEdits( { using var _0 = ArrayBuilder.GetInstance(edits.Count, out var mergedEditsBuilder); using var _1 = PooledHashSet.GetInstance(out var addedSymbolsBuilder); - using var _2 = ArrayBuilder<(ISymbol? oldSymbol, ISymbol newSymbol)>.GetInstance(edits.Count, out var resolvedSymbols); + using var _2 = ArrayBuilder<(ISymbol? oldSymbol, ISymbol? newSymbol)>.GetInstance(edits.Count, out var resolvedSymbols); foreach (var edit in edits) { SymbolKeyResolution oldResolution; - if (edit.Kind == SemanticEditKind.Update) + if (edit.Kind is SemanticEditKind.Update or SemanticEditKind.Delete) { oldResolution = edit.Symbol.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken); Contract.ThrowIfNull(oldResolution.Symbol); @@ -512,8 +512,16 @@ internal static void MergePartialEdits( oldResolution = default; } - var newResolution = edit.Symbol.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken); - Contract.ThrowIfNull(newResolution.Symbol); + SymbolKeyResolution newResolution; + if (edit.Kind is SemanticEditKind.Update or SemanticEditKind.Insert or SemanticEditKind.Replace) + { + newResolution = edit.Symbol.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken); + Contract.ThrowIfNull(newResolution.Symbol); + } + else + { + newResolution = default; + } resolvedSymbols.Add((oldResolution.Symbol, newResolution.Symbol)); } @@ -528,7 +536,7 @@ internal static void MergePartialEdits( if (edit.Kind == SemanticEditKind.Insert) { - // Inserts do not need partial type merging. + Contract.ThrowIfNull(newSymbol); addedSymbolsBuilder.Add(newSymbol); } @@ -552,7 +560,7 @@ internal static void MergePartialEdits( // Calculate merged syntax map for each partial type symbol: var symbolKeyComparer = SymbolKey.GetComparer(ignoreCase: false, ignoreAssemblyKeys: true); - var mergedSyntaxMaps = new Dictionary>(symbolKeyComparer); + var mergedSyntaxMaps = new Dictionary?>(symbolKeyComparer); var editsByPartialType = edits .Where(edit => edit.PartialType != null) @@ -560,15 +568,27 @@ internal static void MergePartialEdits( foreach (var partialTypeEdits in editsByPartialType) { - Debug.Assert(partialTypeEdits.All(edit => edit.SyntaxMapTree != null && edit.SyntaxMap != null)); + // Either all edits have syntax map or none has. + Debug.Assert( + partialTypeEdits.All(edit => edit.SyntaxMapTree != null && edit.SyntaxMap != null) || + partialTypeEdits.All(edit => edit.SyntaxMapTree == null && edit.SyntaxMap == null)); - var newTrees = partialTypeEdits.SelectAsArray(edit => edit.SyntaxMapTree!); - var syntaxMaps = partialTypeEdits.SelectAsArray(edit => edit.SyntaxMap!); + Func? mergedSyntaxMap; + if (partialTypeEdits.First().SyntaxMap != null) + { + var newTrees = partialTypeEdits.SelectAsArray(edit => edit.SyntaxMapTree!); + var syntaxMaps = partialTypeEdits.SelectAsArray(edit => edit.SyntaxMap!); + mergedSyntaxMap = node => syntaxMaps[newTrees.IndexOf(node.SyntaxTree)](node); + } + else + { + mergedSyntaxMap = null; + } - mergedSyntaxMaps.Add(partialTypeEdits.Key, node => syntaxMaps[newTrees.IndexOf(node.SyntaxTree)](node)); + mergedSyntaxMaps.Add(partialTypeEdits.Key, mergedSyntaxMap); } - // Deduplicate updates based on new symbol and use merged syntax map calculated above for a given partial type. + // Deduplicate edits based on their target symbol and use merged syntax map calculated above for a given partial type. using var _3 = PooledHashSet.GetInstance(out var visitedSymbols); @@ -578,12 +598,11 @@ internal static void MergePartialEdits( if (edit.PartialType != null) { - Contract.ThrowIfFalse(edit.Kind == SemanticEditKind.Update); - var (oldSymbol, newSymbol) = resolvedSymbols[i]; - if (visitedSymbols.Add(newSymbol)) + if (visitedSymbols.Add(newSymbol ?? oldSymbol!)) { - mergedEditsBuilder.Add(new SemanticEdit(SemanticEditKind.Update, oldSymbol, newSymbol, mergedSyntaxMaps[edit.PartialType.Value], preserveLocalVariables: true)); + var syntaxMap = mergedSyntaxMaps[edit.PartialType.Value]; + mergedEditsBuilder.Add(new SemanticEdit(edit.Kind, oldSymbol, newSymbol, syntaxMap, preserveLocalVariables: syntaxMap != null)); } } } diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index a976f5eb8e78a..616d33c6aec30 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -123,16 +123,15 @@ internal enum RudeEditKind : ushort //AddRecordPositionalParameter = 94, //DeleteRecordPositionalParameter = 95, ExplicitRecordMethodParameterNamesMustMatch = 96, - NotSupportedByRuntime = 97, MakeMethodAsync = 98, MakeMethodIterator = 99, InsertNotSupportedByRuntime = 100, ChangingAttributesNotSupportedByRuntime = 101, - ChangeImplicitMainReturnType = 102, ChangingParameterTypes = 103, ChangingTypeParameters = 104, ChangingConstraints = 105, + ChangingReloadableTypeNotSupportedByRuntime = 106, } } diff --git a/src/Features/Core/Portable/EditAndContinue/SemanticEditInfo.cs b/src/Features/Core/Portable/EditAndContinue/SemanticEditInfo.cs index 9f7a60a523640..53e343d4c60ef 100644 --- a/src/Features/Core/Portable/EditAndContinue/SemanticEditInfo.cs +++ b/src/Features/Core/Portable/EditAndContinue/SemanticEditInfo.cs @@ -38,9 +38,8 @@ internal readonly struct SemanticEditInfo /// /// Specified if the edit needs to be merged with other edits of the same . - /// for edits of non-partial types or their members and of a member of a partial type that do not require merging. /// - /// If specified, the is incomplete: it only provides mapping of the changed members of a single partial type declaration. + /// If specified, the is either null or incomplete: it only provides mapping of the changed members of a single partial type declaration. /// public SymbolKey? PartialType { get; } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 73ee20ce8e880..ece80338ca25f 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2867,6 +2867,9 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Edit and continue is not supported by the runtime. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Making a method an iterator will prevent the debug session from continuing. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index cc356be6ece44..2f8c199da9783 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -2105,6 +2105,11 @@ Pozitivní kontrolní výrazy zpětného vyhledávání s nulovou délkou se obv Pokud se aktualizuje {0} v okolí aktivního příkazu, relace ladění nebude moct pokračovat. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index be3144797f9e3..844f165e47ef1 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -2105,6 +2105,11 @@ Positive Lookbehindassertionen mit Nullbreite werden normalerweise am Anfang reg Durch Aktualisieren von "{0}" im Kontext einer aktiven Anweisung wird verhindert, dass die Debugsitzung fortgesetzt wird. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index eaa1749cb69f4..1fe3e814d676f 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -2105,6 +2105,11 @@ Las aserciones de búsqueda retrasada (lookbehind) positivas de ancho cero se us Si se actualiza un "{0}" en torno a una instrucción activa, la sesión de depuración no podrá continuar. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 3129470c0141e..1e3cad73f8696 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -2105,6 +2105,11 @@ Les assertions arrière positives de largeur nulle sont généralement utilisée La mise à jour de '{0}' autour d'une instruction active va empêcher la session de débogage de se poursuivre. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 819457a5f11b1..bc0e510b9b869 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -2105,6 +2105,11 @@ Le asserzioni lookbehind positive di larghezza zero vengono usate in genere all' Se si aggiorna '{0}' in prossimità di un'istruzione attiva, la sessione di debug non potrà continuare. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 16381ccc5f2c9..470c1af5f2a81 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -2105,6 +2105,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of アクティブ ステートメントの前後の '{0}' を更新すると、デバッグ セッションは続行されません。 + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 1a5b762cd534d..a0eb8736d8af6 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -2105,6 +2105,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 활성 문 주위에서 '{0}'을(를) 업데이트하면 디버그 세션을 계속할 수 없습니다. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 9a1aebfd64925..61094ffab22a3 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -2105,6 +2105,11 @@ Pozytywne asercje wsteczne o zerowej szerokości są zwykle używane na początk Aktualizacja elementu „{0}” w pobliżu aktywnej instrukcji uniemożliwi kontynuowanie sesji debugowania. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index 055c7b78a3e82..19af60eab0f78 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -2105,6 +2105,11 @@ As declarações de lookbehind positivas de largura zero normalmente são usadas Atualizar um '{0}' ao redor de uma instrução ativa impedirá a continuação da sessão de depuração. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index cdb668ea6c403..259bfbc0757db 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -2105,6 +2105,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Обновление "{0}" рядом с активной инструкцией сделает продолжение сеанса отладки невозможным. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index dfc553f36d4db..14f7077b25c6c 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -2105,6 +2105,11 @@ Sıfır genişlikli pozitif geri yönlü onaylamalar genellikle normal ifadeleri Etkin bir deyim etrafındaki '{0}' öğesini güncelleştirmek, hata ayıklama oturumunun devam etmesini engeller. + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index 8d5a4962c373a..68a4710e87683 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -2105,6 +2105,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新活动语句周围的“{0}”将阻止调试会话继续执行。 + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 2e434493caf66..bb07fe4552d8a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -2105,6 +2105,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新使用中陳述式前後的 '{0}',會造成偵錯工作階段無法繼續。 + + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + Updating a reloadable type (marked by {0}) or its member is not supported by the runtime. + + Updating the attributes of '{0}' is not supported by the runtime. Updating the attributes of '{0}' is not supported by the runtime. From 58bf3c8fdff71bb8dafbc30b36cbed5071c07d15 Mon Sep 17 00:00:00 2001 From: tmat Date: Tue, 20 Jul 2021 23:28:00 -0700 Subject: [PATCH 2/3] Fix --- .../VisualBasicTest/EditAndContinue/ActiveStatementTests.vb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb index d579ee52514a1..2e47009d2ed29 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb @@ -183,19 +183,19 @@ End Class Public Sub Update_Leaf_Reloadable() Dim src1 = ReloadableAttributeSrc + " -[CreateNewOnMetadataUpdate] + Class C Shared Sub Main() Goo(1) End Sub Shared Sub Goo(a As Integer) - Console.WriteLine(a) + System.Console.WriteLine(a) End Sub End Class " Dim src2 = ReloadableAttributeSrc + " -[CreateNewOnMetadataUpdate] + Class C Shared Sub Main() While True From 5c6e5996dc658626adf574937bf33043e873bee0 Mon Sep 17 00:00:00 2001 From: tmat Date: Tue, 20 Jul 2021 23:28:37 -0700 Subject: [PATCH 3/3] Fix --- .../Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 1380886d85ccc..dfeac6717fa33 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -1,5 +1,4 @@ // Licensed to the .NET Foundation under one or more agreements. -// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information.