diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs index 36ccd6cf6a05e..062a86601bdd1 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/CSharpEditAndContinueAnalyzerTests.cs @@ -32,7 +32,7 @@ public class CSharpEditAndContinueAnalyzerTests #region Helpers - private static void TestSpans(string source, Func hasLabel) + private static void TestSpans(string source, Func hasLabel) { var tree = SyntaxFactory.ParseSyntaxTree(source); @@ -41,7 +41,7 @@ private static void TestSpans(string source, Func hasLabel) var expectedText = source.Substring(expected.Start, expected.Length); var token = tree.GetRoot().FindToken(expected.Start); var node = token.Parent; - while (!hasLabel(node.Kind())) + while (!hasLabel(node)) { node = node.Parent; } @@ -146,8 +146,7 @@ public void ErrorSpans_TopLevel() /**/delegate C D2()/**/; -[/**/Attrib/**/] -/**/[Attrib]/**/ +[Attrib] /**/public class Z/**/ { /**/int f/**/; @@ -181,7 +180,7 @@ public void ErrorSpans_TopLevel() } "; - TestSpans(source, kind => SyntaxComparer.TopLevel.HasLabel(kind)); + TestSpans(source, node => SyntaxComparer.TopLevel.HasLabel(node)); } [Fact] diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs index 5658d7becc583..710c9bb7e5085 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/StatementEditingTests.cs @@ -7201,12 +7201,11 @@ public void LocalFunction_AddAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@2", - "Insert [A]@3"); + "Update [void L() { }]@2 -> [[A]void L() { }]@2"); // Get top edits so we can validate rude edits GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function)); } [Fact] @@ -7218,12 +7217,11 @@ public void LocalFunction_RemoveAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Delete [[A]]@2", - "Delete [A]@3"); + "Update [[A]void L() { }]@2 -> [void L() { }]@2"); // Get top edits so we can validate rude edits GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function)); } [Fact] @@ -7234,7 +7232,7 @@ public void LocalFunction_ReorderAttribute() var edits = GetMethodEdits(src1, src2); - edits.VerifyEdits("Reorder [B]@6 -> @3"); + edits.VerifyEdits("Update [[A, B]void L() { }]@2 -> [[B, A]void L() { }]@2"); // Get top edits so we can validate rude edits GetTopEdits(edits).VerifyRudeDiagnostics(); @@ -7249,15 +7247,9 @@ public void LocalFunction_CombineAttributeLists() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Update [[A]]@2 -> [[A, B]]@2", - "Insert [B]@6", - "Delete [[B]]@5", - "Delete [B]@6"); + "Update [[A][B]void L() { }]@2 -> [[A, B]void L() { }]@2"); - // Get top edits so we can validate rude edits - GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.attribute), - Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics(); } [Fact] @@ -7269,13 +7261,9 @@ public void LocalFunction_SplitAttributeLists() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Update [[A, B]]@2 -> [[A]]@2", - "Insert [[B]]@5", - "Insert [B]@6", - "Delete [B]@6"); + "Update [[A, B]void L() { }]@2 -> [[A][B]void L() { }]@2"); - GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[B]", FeaturesResources.attribute)); + GetTopEdits(edits).VerifyRudeDiagnostics(); } [Fact] @@ -7286,10 +7274,11 @@ public void LocalFunction_ChangeAttributeListTarget1() var edits = GetMethodEdits(src1, src2); - edits.VerifyEdits("Update [[return: A]]@2 -> [[A]]@2"); + edits.VerifyEdits("Update [[return: A]void L() { }]@2 -> [[A]void L() { }]@2"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function), + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function)); } [Fact] @@ -7300,10 +7289,11 @@ public void LocalFunction_ChangeAttributeListTarget2() var edits = GetMethodEdits(src1, src2); - edits.VerifyEdits("Update [[A]]@2 -> [[return: A]]@2"); + edits.VerifyEdits("Update [[A]void L() { }]@2 -> [[return: A]void L() { }]@2"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "return:", CSharpFeaturesResources.attribute_target)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function), + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function)); } [Fact] @@ -7315,11 +7305,10 @@ public void LocalFunction_ReturnType_AddAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Insert [[return: A]]@2", - "Insert [A]@11"); + "Update [int L() { return 1; }]@2 -> [[return: A]int L() { return 1; }]@2"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[return: A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function)); } [Fact] @@ -7331,11 +7320,10 @@ public void LocalFunction_ReturnType_RemoveAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Delete [[return: A]]@2", - "Delete [A]@11"); + "Update [[return: A]int L() { return 1; }]@2 -> [int L() { return 1; }]@2"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "L", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "L", FeaturesResources.local_function)); } [Fact] @@ -7346,7 +7334,7 @@ public void LocalFunction_ReturnType_ReorderAttribute() var edits = GetMethodEdits(src1, src2); - edits.VerifyEdits("Reorder [B]@14 -> @11"); + edits.VerifyEdits("Update [[return: A, B]int L() { return 1; }]@2 -> [[return: B, A]int L() { return 1; }]@2"); GetTopEdits(edits).VerifyRudeDiagnostics(); } @@ -7360,11 +7348,10 @@ public void LocalFunction_Parameter_AddAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@9", - "Insert [A]@10"); + "Update [int i]@9 -> [[A]int i]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int i", FeaturesResources.parameter)); } [Fact] @@ -7376,11 +7363,10 @@ public void LocalFunction_Parameter_RemoveAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Delete [[A]]@9", - "Delete [A]@10"); + "Update [[A]int i]@9 -> [int i]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "int i", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int i", FeaturesResources.parameter)); } [Fact] @@ -7391,7 +7377,7 @@ public void LocalFunction_Parameter_ReorderAttribute() var edits = GetMethodEdits(src1, src2); - edits.VerifyEdits("Reorder [B]@13 -> @10"); + edits.VerifyEdits("Update [[A, B]int i]@9 -> [[B, A]int i]@9"); GetTopEdits(edits).VerifyRudeDiagnostics(); } @@ -7405,11 +7391,10 @@ public void LocalFunction_TypeParameter_AddAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@9", - "Insert [A]@10"); + "Update [T]@9 -> [[A] T]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] @@ -7421,11 +7406,10 @@ public void LocalFunction_TypeParameter_RemoveAttribute() var edits = GetMethodEdits(src1, src2); edits.VerifyEdits( - "Delete [[A]]@9", - "Delete [A]@10"); + "Update [[A] T]@9 -> [T]@9"); GetTopEdits(edits).VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "T", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] @@ -7436,7 +7420,7 @@ public void LocalFunction_TypeParameter_ReorderAttribute() var edits = GetMethodEdits(src1, src2); - edits.VerifyEdits("Reorder [B]@13 -> @10"); + edits.VerifyEdits("Update [[A, B] T]@9 -> [[B, A] T]@9"); GetTopEdits(edits).VerifyRudeDiagnostics(); } diff --git a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index dd495ff5fd626..095a59d3feecc 100644 --- a/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/EditorFeatures/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -430,82 +430,190 @@ public void ExternAliasDelete() #region Attributes + [Fact] + public void Insert_TopLevelAttribute() + { + var src1 = ""; + var src2 = "[assembly: System.Obsolete(\"2\")]"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [[assembly: System.Obsolete(\"2\")]]@0", + "Insert [System.Obsolete(\"2\")]@11"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Insert, "[assembly: System.Obsolete(\"2\")]", FeaturesResources.attribute)); + } + + [Fact] + public void Delete_TopLevelAttribute() + { + var src1 = "[assembly: System.Obsolete(\"2\")]"; + var src2 = ""; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Delete [[assembly: System.Obsolete(\"2\")]]@0", + "Delete [System.Obsolete(\"2\")]@11"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Delete, null, FeaturesResources.attribute)); + } + + [Fact] + public void Update_TopLevelAttribute() + { + var src1 = "[assembly: System.Obsolete(\"1\")]"; + var src2 = "[assembly: System.Obsolete(\"2\")]"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[assembly: System.Obsolete(\"1\")]]@0 -> [[assembly: System.Obsolete(\"2\")]]@0", + "Update [System.Obsolete(\"1\")]@11 -> [System.Obsolete(\"2\")]@11"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.Update, "System.Obsolete(\"2\")", FeaturesResources.attribute)); + } + + [Fact] + public void Reorder_TopLevelAttribute() + { + var src1 = "[assembly: System.Obsolete(\"1\")][assembly: System.Obsolete(\"2\")]"; + var src2 = "[assembly: System.Obsolete(\"2\")][assembly: System.Obsolete(\"1\")]"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Reorder [[assembly: System.Obsolete(\"2\")]]@32 -> @0"); + + edits.VerifyRudeDiagnostics(); + } + [Fact] public void UpdateAttributes1() { - var src1 = "[A1]class C { }"; - var src2 = "[A2]class C { }"; + var attribute = "public class A1Attribute : System.Attribute { }\n\n" + + "public class A2Attribute : System.Attribute { }\n\n"; + + var src1 = attribute + "[A1]class C { }"; + var src2 = attribute + "[A2]class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [A1]@1 -> [A2]@1"); + "Update [[A1]class C { }]@98 -> [[A2]class C { }]@98"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A2", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); } [Fact] public void UpdateAttributes2() { - var src1 = "[A(1)]class C { }"; - var src2 = "[A(2)]class C { }"; + var src1 = "[System.Obsolete(\"1\")]class C { }"; + var src2 = "[System.Obsolete(\"2\")]class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [A(1)]@1 -> [A(2)]@1"); + "Update [[System.Obsolete(\"1\")]class C { }]@0 -> [[System.Obsolete(\"2\")]class C { }]@0"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A(2)", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); } [Fact] public void DeleteAttributes() { - var src1 = "[A, B]class C { }"; - var src2 = "[A]class C { }"; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "[A, B]class C { }"; + var src2 = attribute + "[A]class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[A, B]class C { }]@96 -> [[A]class C { }]@96"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); + } + + [Fact] + public void DeleteAttributes2() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "[B, A]class C { }"; + var src2 = attribute + "[A]class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [[A, B]]@0 -> [[A]]@0", - "Delete [B]@4"); + "Update [[B, A]class C { }]@96 -> [[A]class C { }]@96"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); + } + + [Fact] + public void InsertAttributes_SupportedByRuntime() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "[A]class C { }"; + var src2 = attribute + "[A, B]class C { }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [[A]class C { }]@96 -> [[A, B]class C { }]@96"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); } [Fact] public void InsertAttributes1() { - var src1 = "[A]class C { }"; - var src2 = "[A, B]class C { }"; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "[A]class C { }"; + var src2 = attribute + "[A, B]class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [[A]]@0 -> [[A, B]]@0", - "Insert [B]@4"); + "Update [[A]class C { }]@96 -> [[A, B]class C { }]@96"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); } [Fact] public void InsertAttributes2() { - var src1 = "class C { }"; - var src2 = "[A]class C { }"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "class C { }"; + var src2 = attribute + "[A]class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@0", - "Insert [A]@1"); + "Update [class C { }]@48 -> [[A]class C { }]@48"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); } [Fact] @@ -517,7 +625,7 @@ public void ReorderAttributes1() var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Reorder [C(3)]@13 -> @1"); + "Update [[A(1), B(2), C(3)]class C { }]@0 -> [[C(3), A(1), B(2)]class C { }]@0"); edits.VerifyRudeDiagnostics(); } @@ -531,7 +639,7 @@ public void ReorderAttributes2() var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Reorder [A]@1 -> @7"); + "Update [[A, B, C]class C { }]@0 -> [[B, C, A]class C { }]@0"); edits.VerifyRudeDiagnostics(); } @@ -539,17 +647,19 @@ public void ReorderAttributes2() [Fact] public void ReorderAndUpdateAttributes() { - var src1 = "[A(1), B, C]class C { }"; - var src2 = "[B, C, A(2)]class C { }"; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "[System.Obsolete(\"1\"), A, B]class C { }"; + var src2 = attribute + "[A, B, System.Obsolete(\"2\")]class C { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Reorder [A(1)]@1 -> @7", - "Update [A(1)]@1 -> [A(2)]@7"); + "Update [[System.Obsolete(\"1\"), A, B]class C { }]@96 -> [[A, B, System.Obsolete(\"2\")]class C { }]@96"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A(2)", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)); } #endregion @@ -2742,64 +2852,70 @@ public void Enum_NoModifiers_IntoType_Insert() [Fact] public void EnumAttributeInsert() { - var src1 = "enum E { }"; - var src2 = "[A]enum E { }"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "enum E { }"; + var src2 = attribute + "[A]enum E { }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@0", - "Insert [A]@1"); + "Update [enum E { }]@48 -> [[A]enum E { }]@48"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "enum E", FeaturesResources.enum_)); } [Fact] public void EnumMemberAttributeDelete() { - var src1 = "enum E { [A]X }"; - var src2 = "enum E { X }"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "enum E { [A]X }"; + var src2 = attribute + "enum E { X }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Delete [[A]]@9", - "Delete [A]@10"); + "Update [[A]X]@57 -> [X]@57"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "X", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "X", FeaturesResources.enum_value)); } [Fact] public void EnumMemberAttributeInsert() { - var src1 = "enum E { X }"; - var src2 = "enum E { [A]X }"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "enum E { X }"; + var src2 = attribute + "enum E { [A]X }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@9", - "Insert [A]@10"); + "Update [X]@57 -> [[A]X]@57"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "[A]X", FeaturesResources.enum_value)); } [Fact] public void EnumMemberAttributeUpdate() { - var src1 = "enum E { [A1]X }"; - var src2 = "enum E { [A2]X }"; + var attribute = "public class A1Attribute : System.Attribute { }\n\n" + + "public class A2Attribute : System.Attribute { }\n\n"; + + var src1 = attribute + "enum E { [A1]X }"; + var src2 = attribute + "enum E { [A2]X }"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [A1]@10 -> [A2]@10"); + "Update [[A1]X]@107 -> [[A2]X]@107"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A2", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "[A2]X", FeaturesResources.enum_value)); } [Fact] @@ -3324,17 +3440,37 @@ public void Delegates_Parameter_UpdateModifier() [Fact] public void Delegates_Parameter_AddAttribute() { - var src1 = "public delegate int D(int a);"; - var src2 = "public delegate int D([A]int a);"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D(int a);"; + var src2 = attribute + "public delegate int D([A]int a);"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@22", - "Insert [A]@23"); + "Update [int a]@70 -> [[A]int a]@70"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); + } + + [Fact] + public void Delegates_Parameter_AddAttribute_SupportedByRuntime() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D(int a);"; + var src2 = attribute + "public delegate int D([A]int a);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int a]@70 -> [[A]int a]@70"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); } [Fact] @@ -3432,33 +3568,90 @@ public void Delegates_TypeParameter_Variance3() [Fact] public void Delegates_TypeParameter_AddAttribute() { - var src1 = "public delegate int D();"; - var src2 = "public delegate int D<[A]T>();"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D();"; + var src2 = attribute + "public delegate int D<[A]T>();"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@22", - "Insert [A]@23"); + "Update [T]@70 -> [[A]T]@70"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] public void Delegates_AddAttribute() { - var src1 = "public delegate int D(int a);"; - var src2 = "[return:A]public delegate int D(int a);"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D(int a);"; + var src2 = attribute + "[A]public delegate int D(int a);"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [[return:A]]@0", - "Insert [A]@8"); + "Update [public delegate int D(int a);]@48 -> [[A]public delegate int D(int a);]@48"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public delegate int D(int a)", FeaturesResources.delegate_)); + } + + [Fact] + public void Delegates_AddAttribute_SupportedByRuntime() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D(int a);"; + var src2 = attribute + "[A]public delegate int D(int a);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [public delegate int D(int a);]@48 -> [[A]public delegate int D(int a);]@48"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + + [Fact] + public void Delegates_AddReturnAttribute() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D(int a);"; + var src2 = attribute + "[return:A]public delegate int D(int a);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [public delegate int D(int a);]@48 -> [[return:A]public delegate int D(int a);]@48"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[return:A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public delegate int D(int a)", FeaturesResources.delegate_)); + } + + [Fact] + public void Delegates_AddReturnAttribute_SupportedByRuntime() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + "public delegate int D(int a);"; + var src2 = attribute + "[return:A]public delegate int D(int a);"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [public delegate int D(int a);]@48 -> [[return:A]public delegate int D(int a);]@48"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); } [Fact] @@ -5165,8 +5358,6 @@ class C edits.VerifyEdits( @"Delete [[Obsolete] void goo(int a) { }]@18", - "Delete [[Obsolete]]@18", - "Delete [Obsolete]@19", "Delete [(int a)]@42", "Delete [int a]@43"); @@ -5201,8 +5392,6 @@ class C edits.VerifyEdits( @"Delete [[DllImport(""msvcrt.dll"")] public static extern int puts(string c);]@74", - @"Delete [[DllImport(""msvcrt.dll"")]]@74", - @"Delete [DllImport(""msvcrt.dll"")]@75", "Delete [(string c)]@134", "Delete [string c]@135"); @@ -5308,7 +5497,7 @@ static void Main(string[] args) var src2 = @" class C { - [Obsolete] + [System.Obsolete] void goo(int a) { } static void Main(string[] args) @@ -5320,12 +5509,10 @@ static void Main(string[] args) var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - @"Insert [[Obsolete] + @"Insert [[System.Obsolete] void goo(int a) { }]@18", - "Insert [[Obsolete]]@18", - "Insert [(int a)]@42", - "Insert [Obsolete]@19", - "Insert [int a]@43"); + "Insert [(int a)]@49", + "Insert [int a]@50"); edits.VerifyRudeDiagnostics(); } @@ -5413,10 +5600,8 @@ class C edits.VerifyEdits( @"Insert [[DllImport(""msvcrt.dll"")] private static extern int puts(string c);]@74", - @"Insert [[DllImport(""msvcrt.dll"")]]@74", "Insert [(string c)]@135", - @"Insert [DllImport(""msvcrt.dll"")]@75", - "Insert [string c]@136"); + "Insert [string c]@136"); // CLR doesn't support methods without a body edits.VerifySemanticDiagnostics( @@ -5935,10 +6120,51 @@ void Method2(int b) Diagnostic(RudeEditKind.ModifiersUpdate, "int b", FeaturesResources.parameter)); } + [Fact] + public void MethodUpdate_AddReturnTypeAttribute() + { + var src1 = @" +using System; + +class Test +{ + static void Main(string[] args) + { + System.Console.Write(5); + } +}"; + var src2 = @" +using System; + +class Test +{ + [return: Obsolete] + static void Main(string[] args) + { + System.Console.Write(5); + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits(@"Update [static void Main(string[] args) + { + System.Console.Write(5); + }]@38 -> [[return: Obsolete] + static void Main(string[] args) + { + System.Console.Write(5); + }]@38"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); + } + [Fact] public void MethodUpdate_AddAttribute() { var src1 = @" +using System; + class Test { static void Main(string[] args) @@ -5947,6 +6173,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { [Obsolete] @@ -5957,16 +6185,143 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Insert [[Obsolete]]@21", "Insert [Obsolete]@22"); + edits.VerifyEdits(@"Update [static void Main(string[] args) + { + System.Console.Write(5); + }]@38 -> [[Obsolete] + static void Main(string[] args) + { + System.Console.Write(5); + }]@38"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[Obsolete]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); + } + + [Fact] + public void MethodUpdate_AddAttribute_SupportedByRuntime() + { + var src1 = @" +using System; + +class Test +{ + static void Main(string[] args) + { + System.Console.Write(5); + } +}"; + var src2 = @" +using System; + +class Test +{ + [Obsolete] + static void Main(string[] args) + { + System.Console.Write(5); + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits(@"Update [static void Main(string[] args) + { + System.Console.Write(5); + }]@38 -> [[Obsolete] + static void Main(string[] args) + { + System.Console.Write(5); + }]@38"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Test.Main")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + + [Fact] + public void MethodUpdate_Attribute_ArrayParameter() + { + var src1 = @" +class AAttribute : System.Attribute +{ + public AAttribute(int[] nums) { } +} + +class C +{ + [A(new int[] { 1, 2, 3})] + void M() + { + } +}"; + var src2 = @" +class AAttribute : System.Attribute +{ + public AAttribute(int[] nums) { } +} + +class C +{ + [A(new int[] { 4, 5, 6})] + void M() + { + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + + [Fact] + public void MethodUpdate_Attribute_ArrayParameter_NoChange() + { + var src1 = @" +class AAttribute : System.Attribute +{ + public AAttribute(int[] nums) { } +} + +class C +{ + [A(new int[] { 1, 2, 3})] + void M() + { + var x = 1; + } +}"; + var src2 = @" +class AAttribute : System.Attribute +{ + public AAttribute(int[] nums) { } +} + +class C +{ + [A(new int[] { 1, 2, 3})] + void M() + { + var x = 2; + } +}"; + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) }); + + edits.VerifyRudeDiagnostics(); } [Fact] public void MethodUpdate_AddAttribute2() { var src1 = @" +using System; + class Test { [Obsolete] @@ -5976,6 +6331,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { [Obsolete, Serializable] @@ -5986,17 +6343,16 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Update [[Obsolete]]@21 -> [[Obsolete, Serializable]]@21", - "Insert [Serializable]@32"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "Serializable", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [Fact] public void MethodUpdate_AddAttribute3() { var src1 = @" +using System; + class Test { [Obsolete] @@ -6006,6 +6362,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { [Obsolete] @@ -6017,17 +6375,16 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Insert [[Serializable]]@37", - "Insert [Serializable]@38"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[Serializable]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [Fact] public void MethodUpdate_AddAttribute4() { var src1 = @" +using System; + class Test { static void Main(string[] args) @@ -6036,6 +6393,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { [Obsolete, Serializable] @@ -6046,19 +6405,16 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Insert [[Obsolete, Serializable]]@21", - "Insert [Obsolete]@22", - "Insert [Serializable]@32"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[Obsolete, Serializable]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [Fact] public void MethodUpdate_UpdateAttribute() { var src1 = @" +using System; + class Test { [Obsolete] @@ -6068,6 +6424,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { [Obsolete("""")] @@ -6078,10 +6436,8 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits(@"Update [Obsolete]@22 -> [Obsolete("""")]@22"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, @"Obsolete("""")", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [WorkItem(754853, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754853")] @@ -6089,6 +6445,8 @@ static void Main(string[] args) public void MethodUpdate_DeleteAttribute() { var src1 = @" +using System; + class Test { [Obsolete] @@ -6098,6 +6456,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { static void Main(string[] args) @@ -6107,18 +6467,16 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits( - "Delete [[Obsolete]]@21", - "Delete [Obsolete]@22"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "static void Main(string[] args)", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [Fact] public void MethodUpdate_DeleteAttribute2() { var src1 = @" +using System; + class Test { [Obsolete, Serializable] @@ -6128,6 +6486,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { [Obsolete] @@ -6138,17 +6498,16 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Update [[Obsolete, Serializable]]@21 -> [[Obsolete]]@21", - "Delete [Serializable]@32"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "[Obsolete]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [Fact] public void MethodUpdate_DeleteAttribute3() { var src1 = @" +using System; + class Test { [Obsolete] @@ -6159,6 +6518,8 @@ static void Main(string[] args) } }"; var src2 = @" +using System; + class Test { [Obsolete] @@ -6169,11 +6530,8 @@ static void Main(string[] args) }"; var edits = GetTopEdits(src1, src2); - edits.VerifyEdits("Delete [[Serializable]]@37", - "Delete [Serializable]@38"); - edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "static void Main(string[] args)", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void Main(string[] args)", FeaturesResources.method)); } [Fact] @@ -10597,6 +10955,53 @@ public void FieldInsert_Static_NotSupportedByRuntime() Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "a = 1", FeaturesResources.field)); } + [Fact] + public void FieldUpdate_AddAttribute_Multiple() + { + var src1 = @" +class C +{ + public int a = 1, x = 1; +}"; + var src2 = @" +class C +{ + [System.Obsolete]public int a = 1, x = 1; +}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [public int a = 1, x = 1;]@18 -> [[System.Obsolete]public int a = 1, x = 1;]@18"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "a = 1", FeaturesResources.field)); + } + + [Fact] + public void FieldUpdate_AddAttribute_SupportedByRuntime() + { + var src1 = @" +class C +{ + public int a; +}"; + var src2 = @" +class C +{ + [System.Obsolete]public int a; +}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.a")) + }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + [Fact] public void FieldDelete1() { @@ -11062,6 +11467,86 @@ public void PropertyTypeUpdate() Diagnostic(RudeEditKind.TypeUpdate, "char P", FeaturesResources.auto_property)); } + [Fact] + public void PropertyUpdate_AddAttribute() + { + var src1 = "class C { int P { get; set; } }"; + var src2 = "class C { [System.Obsolete]int P { get; set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int P", FeaturesResources.auto_property)); + } + + [Fact] + public void PropertyUpdate_AddAttribute_SupportedByRuntime() + { + var src1 = "class C { int P { get; set; } }"; + var src2 = "class C { [System.Obsolete]int P { get; set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")) + }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + + [Fact] + public void PropertyAccessorUpdate_AddAttribute() + { + var src1 = "class C { int P { get; set; } }"; + var src2 = "class C { int P { [System.Obsolete]get; set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "get", CSharpFeaturesResources.property_getter)); + } + + [Fact] + public void PropertyAccessorUpdate_AddAttribute2() + { + var src1 = "class C { int P { get; set; } }"; + var src2 = "class C { int P { get; [System.Obsolete]set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "set", CSharpFeaturesResources.property_setter)); + } + + [Fact] + public void PropertyAccessorUpdate_AddAttribute_SupportedByRuntime() + { + var src1 = "class C { int P { get; set; } }"; + var src2 = "class C { int P { [System.Obsolete]get; set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").GetMethod) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + + [Fact] + public void PropertyAccessorUpdate_AddAttribute_SupportedByRuntime2() + { + var src1 = "class C { int P { get; set; } }"; + var src2 = "class C { int P { get; [System.Obsolete]set; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").SetMethod) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + [Fact] public void PropertyInsert() { @@ -12740,51 +13225,142 @@ public void ParameterReorderAndUpdate() } [Fact] - public void ParameterAttributeInsert1() + public void ParameterAttributeInsert_SupportedByRuntime() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M(int a) {} }"; + var src2 = attribute + @"class C { public void M([A]int a) {} } "; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int a]@72 -> [[A]int a]@72"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); + } + + [Fact] + public void ParameterAttributeInsert_SupportedByRuntime_NonCustomAttribute() { var src1 = @"class C { public void M(int a) {} }"; - var src2 = @"class C { public void M([A]int a) {} } "; + var src2 = @"class C { public void M([System.Runtime.InteropServices.InAttribute]int a) {} } "; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int a]@24 -> [[System.Runtime.InteropServices.InAttribute]int a]@24"); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes, + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); + } + + [Fact] + public void ParameterAttributeInsert_SupportedByRuntime_SecurityAttribute1() + { + var attribute = "public class AAttribute : System.Security.Permissions.SecurityAttribute { }\n\n"; + + var src1 = attribute + @"class C { public void M(int a) {} }"; + var src2 = attribute + @"class C { public void M([A]int a) {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Insert [[A]]@24", - "Insert [A]@25"); + "Update [int a]@101 -> [[A]int a]@101"); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes, + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); + } + + [Fact] + public void ParameterAttributeInsert_SupportedByRuntime_SecurityAttribute2() + { + var attribute = "public class BAttribute : System.Security.Permissions.SecurityAttribute { }\n\n" + + "public class AAttribute : BAttribute { }\n\n"; + + var src1 = attribute + @"class C { public void M(int a) {} }"; + var src2 = attribute + @"class C { public void M([A]int a) {} } "; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int a]@143 -> [[A]int a]@143"); + + edits.VerifyRudeDiagnostics( + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes, + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); + } + + [Fact] + public void ParameterAttributeInsert1() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M(int a) {} }"; + var src2 = attribute + @"class C { public void M([A]int a) {} } "; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int a]@72 -> [[A]int a]@72"); + + edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } [Fact] public void ParameterAttributeInsert2() { - var src1 = @"class C { public void M([A]int a) {} }"; - var src2 = @"class C { public void M([A, B]int a) {} } "; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M([A]int a) {} }"; + var src2 = attribute + @"class C { public void M([A, B]int a) {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Update [[A]]@24 -> [[A, B]]@24", - "Insert [B]@28"); + "Update [[A]int a]@120 -> [[A, B]int a]@120"); + + edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } [Fact] public void ParameterAttributeDelete() { - var src1 = @"class C { public void M([A]int a) {} }"; - var src2 = @"class C { public void M(int a) {} } "; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M([A]int a) {} }"; + var src2 = attribute + @"class C { public void M(int a) {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Delete [[A]]@24", - "Delete [A]@25"); + "Update [[A]int a]@72 -> [int a]@72"); + + edits.VerifyRudeDiagnostics(Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } [Fact] public void ParameterAttributeUpdate() { - var src1 = @"class C { public void M([A(1), C]int a) {} }"; - var src2 = @"class C { public void M([A(2), B]int a) {} } "; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M([System.Obsolete(""1""), B]int a) {} }"; + var src2 = attribute + @"class C { public void M([System.Obsolete(""2""), A]int a) {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Update [A(1)]@25 -> [A(2)]@25", - "Update [C]@31 -> [B]@31"); + "Update [[System.Obsolete(\"1\"), B]int a]@120 -> [[System.Obsolete(\"2\"), A]int a]@120"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)); } #endregion @@ -12876,49 +13452,74 @@ public void MethodTypeParameterReorderAndUpdate() [Fact] public void MethodTypeParameterAttributeInsert1() { - var src1 = @"class C { public void M() {} }"; - var src2 = @"class C { public void M<[A]T>() {} } "; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M() {} }"; + var src2 = attribute + @"class C { public void M<[A]T>() {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Insert [[A]]@24", - "Insert [A]@25"); + "Update [T]@72 -> [[A]T]@72"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, "", FeaturesResources.method), + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] public void MethodTypeParameterAttributeInsert2() { - var src1 = @"class C { public void M<[A]T>() {} }"; - var src2 = @"class C { public void M<[A, B]T>() {} } "; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M<[A]T>() {} }"; + var src2 = attribute + @"class C { public void M<[A, B]T>() {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Update [[A]]@24 -> [[A, B]]@24", - "Insert [B]@28"); + "Update [[A]T]@120 -> [[A, B]T]@120"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, "", FeaturesResources.method), + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] public void MethodTypeParameterAttributeDelete() { - var src1 = @"class C { public void M<[A]T>() {} }"; - var src2 = @"class C { public void M() {} } "; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M<[A]T>() {} }"; + var src2 = attribute + @"class C { public void M() {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Delete [[A]]@24", - "Delete [A]@25"); + "Update [[A]T]@72 -> [T]@72"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.GenericMethodTriviaUpdate, "", FeaturesResources.method), + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] public void MethodTypeParameterAttributeUpdate() { - var src1 = @"class C { public void M<[A(1), C]T>() {} }"; - var src2 = @"class C { public void M<[A(2), B]T>() {} } "; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C { public void M<[System.Obsolete(""1""), B]T>() {} }"; + var src2 = attribute + @"class C { public void M<[System.Obsolete(""2""), A]T>() {} } "; var edits = GetTopEdits(src1, src2); + edits.VerifyEdits( - "Update [A(1)]@25 -> [A(2)]@25", - "Update [C]@31 -> [B]@31"); + "Update [[System.Obsolete(\"1\"), B]T]@120 -> [[System.Obsolete(\"2\"), A]T]@120"); + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } #endregion @@ -13035,66 +13636,90 @@ public void TypeTypeParameterReorderAndUpdate() [Fact] public void TypeTypeParameterAttributeInsert1() { - var src1 = @"class C {}"; - var src2 = @"class C<[A]T> {}"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C {}"; + var src2 = attribute + @"class C<[A]T> {}"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Insert [[A]]@8", - "Insert [A]@9"); + "Update [T]@56 -> [[A]T]@56"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "[A]", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] public void TypeTypeParameterAttributeInsert2() { - var src1 = @"class C<[A]T> {}"; - var src2 = @"class C<[A, B]T> {}"; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C<[A]T> {}"; + var src2 = attribute + @"class C<[A, B]T> {}"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [[A]]@8 -> [[A, B]]@8", - "Insert [B]@12"); + "Update [[A]T]@104 -> [[A, B]T]@104"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); + } + + [Fact] + public void TypeTypeParameterAttributeInsert_SupportedByRuntime() + { + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C {}"; + var src2 = attribute + @"class C<[A]T> {}"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [T]@56 -> [[A]T]@56"); + + edits.VerifySemantics( + ActiveStatementsDescription.Empty, + new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")) }, + capabilities: EditAndContinueTestHelpers.Net5RuntimeCapabilities | EditAndContinueCapabilities.ChangeCustomAttributes); } [Fact] public void TypeTypeParameterAttributeDelete() { - var src1 = @"class C<[A]T> {}"; - var src2 = @"class C {}"; + var attribute = "public class AAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C<[A]T> {}"; + var src2 = attribute + @"class C {}"; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Delete [[A]]@8", - "Delete [A]@9"); + "Update [[A]T]@56 -> [T]@56"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "T", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } [Fact] public void TypeTypeParameterAttributeUpdate() { - var src1 = @"class C<[A(1), C]T> {}"; - var src2 = @"class C<[A(2), B]T> {} "; + var attribute = "public class AAttribute : System.Attribute { }\n\n" + + "public class BAttribute : System.Attribute { }\n\n"; + + var src1 = attribute + @"class C<[System.Obsolete(""1""), B]T> {}"; + var src2 = attribute + @"class C<[System.Obsolete(""2""), A]T> {} "; var edits = GetTopEdits(src1, src2); edits.VerifyEdits( - "Update [A(1)]@9 -> [A(2)]@9", - "Update [C]@15 -> [B]@15"); + "Update [[System.Obsolete(\"1\"), B]T]@104 -> [[System.Obsolete(\"2\"), A]T]@104"); edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A(2)", FeaturesResources.attribute), - Diagnostic(RudeEditKind.Update, "B", FeaturesResources.attribute)); + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)); } #endregion diff --git a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs index ecd7e89fb48cd..e553b1563cf3e 100644 --- a/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs +++ b/src/EditorFeatures/Test/EditAndContinue/EditAndContinueWorkspaceServiceTests.cs @@ -3645,5 +3645,19 @@ public void ParseCapabilities_IgnoreInvalidNumeric() Assert.True(service.HasFlag(EditAndContinueCapabilities.Baseline)); Assert.True(service.HasFlag(EditAndContinueCapabilities.NewTypeDefinition)); } + + [Fact] + public void ParseCapabilities_AllCapabilitiesParsed() + { + foreach (var name in Enum.GetNames(typeof(EditAndContinueCapabilities))) + { + var capabilities = ImmutableArray.Create(name); + + var service = EditAndContinueWorkspaceService.ParseCapabilities(capabilities); + + var flag = (EditAndContinueCapabilities)Enum.Parse(typeof(EditAndContinueCapabilities), name); + Assert.True(service.HasFlag(flag), $"Capability '{name}' was not parsed correctly, so it's impossible for a runtime to enable it!"); + } + } } } diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb index d110065e51de3..aecb17e8655b2 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/ActiveStatementTests.vb @@ -1937,7 +1937,7 @@ End Class Dim src2 = " Class C - Private Const a As Integer = 1 + Private Const a As Integer = 1 End Class " @@ -1982,7 +1982,7 @@ End Class Dim src2 = " Class C - Private Const a As Integer = 1, b As Integer = 2 + Private Const a As Integer = 1, b As Integer = 2 End Class " diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb index 5f10e00e950e8..519ab947a0a8d 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/TopLevelEditingTests.vb @@ -283,28 +283,47 @@ Option Strict Off #Region "Attributes" Public Sub UpdateAttributes1() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : End Class" + Dim attribute = "Public Class A1Attribute : Inherits System.Attribute : End Class" & vbCrLf & + "Public Class A2Attribute : Inherits System.Attribute : End Class" & vbCrLf + + Dim src1 = attribute & "Class C : End Class" + Dim src2 = attribute & "Class C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [A1]@1 -> [A2]@1") + "Update [Class C]@132 -> [Class C]@132") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A2", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Class C", FeaturesResources.class_)) End Sub Public Sub UpdateAttributes2() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : End Class" + Dim src1 = "Class C : End Class" + Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [A(1)]@1 -> [A(2)]@1") + "Update [Class C]@0 -> [Class C]@0") + + edits.VerifyRudeDiagnostics( + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Class C", FeaturesResources.class_)) + End Sub + + + Public Sub UpdateAttributes3() + Dim attribute = "Public Class A1Attribute : Inherits System.Attribute : End Class" & vbCrLf & + "Public Class A2Attribute : Inherits System.Attribute : End Class" & vbCrLf + + Dim src1 = attribute & "Module C : End Module" + Dim src2 = attribute & "Module C : End Module" + + Dim edits = GetTopEdits(src1, src2) + edits.VerifyEdits( + "Update [Module C]@132 -> [Module C]@132") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A(2)", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Module C", VBFeaturesResources.module_)) End Sub @@ -314,6 +333,7 @@ Option Strict Off Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( + "Update []@0 -> []@0", "Update [Assembly: A(1)]@1 -> [Assembly: A(2)]@1") edits.VerifyRudeDiagnostics( @@ -322,29 +342,32 @@ Option Strict Off Public Sub UpdateAttributes_TopLevel2() - Dim src1 = "" - Dim src2 = "" + Dim src1 = "" + Dim src2 = "" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [Assembly: A(1)]@1 -> [Module: A(1)]@1") + "Update []@0 -> []@0", + "Update [Assembly: System.Obsolete(""1"")]@1 -> [Module: System.Obsolete(""1"")]@1") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "Module: A(1)", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.Update, "Module: System.Obsolete(""1"")", FeaturesResources.attribute)) End Sub Public Sub DeleteAttributes() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : End Class" + Dim attribute = "Public Class AAttribute : Inherits System.Attribute : End Class" & vbCrLf & + "Public Class BAttribute : Inherits System.Attribute : End Class" & vbCrLf + + Dim src1 = attribute & "Class C : End Class" + Dim src2 = attribute & "Class C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update []@0 -> []@0", - "Delete [B]@4") + "Update [Class C]@130 -> [Class C]@130") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Class C", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Class C", FeaturesResources.class_)) End Sub @@ -365,29 +388,27 @@ Option Strict Off Public Sub InsertAttributes1() Dim src1 = "Class C : End Class" - Dim src2 = "Class C : End Class" + Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update []@0 -> []@0", - "Insert [B]@4") + "Update [Class C]@0 -> [Class C]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Class C", FeaturesResources.class_)) End Sub Public Sub InsertAttributes2() Dim src1 = "Class C : End Class" - Dim src2 = "Class C : End Class" + Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@0", - "Insert [A]@1") + "Update [Class C]@0 -> [Class C]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Class C", FeaturesResources.class_)) End Sub @@ -411,7 +432,7 @@ Option Strict Off Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Reorder [C(3)]@13 -> @1") + edits.VerifyEdits("Update [Class C]@0 -> [Class C]@0") edits.VerifyRudeDiagnostics() End Sub @@ -421,7 +442,7 @@ Option Strict Off Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) - edits.VerifyEdits("Reorder [A]@1 -> @7") + edits.VerifyEdits("Update [Class C]@0 -> [Class C]@0") edits.VerifyRudeDiagnostics() End Sub @@ -438,16 +459,15 @@ Option Strict Off Public Sub ReorderAndUpdateAttributes() - Dim src1 = "Class C : End Class" - Dim src2 = "Class C : End Class" + Dim src1 = "Class C : End Class" + Dim src2 = "Class C : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Reorder [A(1)]@1 -> @7", - "Update [A(1)]@1 -> [A(2)]@7") + "Update [Class C]@0 -> [Class C]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A(2)", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Class C", FeaturesResources.class_)) End Sub #End Region @@ -1220,7 +1240,7 @@ End Class SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember(Of NamedTypeSymbol)("C").InstanceConstructors.Single(), partialType:="C", preserveLocalVariables:=True) }) }) - End sub + End Sub Public Sub Type_DeleteInsert_DataMembers_PartialMerge() @@ -1257,7 +1277,7 @@ End Class }), DocumentResults() }) - End sub + End Sub #End Region #Region "Enums" @@ -1347,53 +1367,56 @@ End Class Public Sub Enum_Attribute_Insert() Dim src1 = "Enum E : End Enum" - Dim src2 = "Enum E : End Enum" + Dim src2 = "Enum E : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@0", "Insert [A]@1") + "Update [Enum E]@0 -> [Enum E]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Enum E", FeaturesResources.enum_)) End Sub Public Sub Enum_MemberAttribute_Delete() - Dim src1 = "Enum E : X : End Enum" + Dim src1 = "Enum E : X : End Enum" Dim src2 = "Enum E : X : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete []@9", "Delete [A]@10") + "Update [X]@9 -> [X]@9") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "X", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "X", FeaturesResources.enum_value)) End Sub Public Sub Enum_MemberAttribute_Insert() Dim src1 = "Enum E : X : End Enum" - Dim src2 = "Enum E : X : End Enum" + Dim src2 = "Enum E : X : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@9", "Insert [A]@10") + "Update [X]@9 -> [X]@9") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "X", FeaturesResources.enum_value)) End Sub Public Sub Enum_MemberAttribute_Update() - Dim src1 = "Enum E : X : End Enum" - Dim src2 = "Enum E : X : End Enum" + Dim attribute = "Public Class A1Attribute : Inherits System.Attribute : End Class" & vbCrLf & + "Public Class A2Attribute : Inherits System.Attribute : End Class" & vbCrLf + + Dim src1 = attribute & "Enum E : X : End Enum" + Dim src2 = attribute & "Enum E : X : End Enum" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [A1]@10 -> [A2]@10") + "Update [X]@141 -> [X]@141") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A2", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "X", FeaturesResources.enum_value)) End Sub @@ -1696,15 +1719,14 @@ End Class Public Sub Delegates_Parameter_AddAttribute() Dim src1 = "Public Delegate Function D(a As Integer) As Integer" - Dim src2 = "Public Delegate Function D( a As Integer) As Integer" + Dim src2 = "Public Delegate Function D( a As Integer) As Integer" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@27", - "Insert [A]@28") + "Update [a As Integer]@27 -> [ a As Integer]@27") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Delegate Function D( a As Integer)", FeaturesResources.delegate_)) End Sub @@ -1790,15 +1812,14 @@ End Class Public Sub Delegates_AddAttribute() Dim src1 = "Public Delegate Function D(a As Integer) As Integer" - Dim src2 = "Public Delegate Function D(a As Integer) As Integer" + Dim src2 = "Public Delegate Function D(a As Integer) As Integer" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@0", - "Insert [A]@1") + "Update [Public Delegate Function D(a As Integer) As Integer]@0 -> [Public Delegate Function D(a As Integer) As Integer]@0") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Delegate Function D(a As Integer)", FeaturesResources.delegate_)) End Sub #End Region @@ -2133,7 +2154,7 @@ End Class ''' A new generic type can be added whether it's nested and inherits generic parameters from the containing type, or top-level. ''' - public Sub NestedClassGeneric_Insert() + Public Sub NestedClassGeneric_Insert() Dim src1 = " Imports System Class C(Of T) @@ -2245,7 +2266,7 @@ End Structure End Sub - public sub NestedDelegateInPartialType_InsertDelete() + Public Sub NestedDelegateInPartialType_InsertDelete() Dim srcA1 = "Partial Structure S : End Structure" Dim srcB1 = "Partial Structure S : Delegate Sub D()" + vbCrLf + "End Structure" Dim srcA2 = "Partial Structure S : Delegate Sub D()" + vbCrLf + "End Structure" @@ -3076,8 +3097,6 @@ End Class" edits.VerifyEdits( "Delete [ Sub goo(a As Integer) : End Sub]@11", "Delete [ Sub goo(a As Integer)]@11", - "Delete []@11", - "Delete [Obsolete]@12", "Delete [(a As Integer)]@29", "Delete [a As Integer]@30", "Delete [a]@30", @@ -3145,9 +3164,7 @@ End Class" edits.VerifyEdits( "Insert [Private Sub F : End Sub]@11", - "Insert [Private Sub F]@11", - "Insert []@11", - "Insert [System.Obsolete]@12") + "Insert [Private Sub F]@11") edits.VerifySemantics(ActiveStatementsDescription.Empty, {SemanticEdit(SemanticEditKind.Insert, Function(c) c.GetMember(Of NamedTypeSymbol)("C").GetMember("F"))}) @@ -3476,113 +3493,105 @@ End Class Public Sub MethodUpdate_AddAttribute() Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@11", - "Insert [A]@12") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub Public Sub MethodUpdate_AddAttribute2() Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update []@11 -> []@11", - "Insert [B]@15") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub Public Sub MethodUpdate_AddAttribute3() Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@14", - "Insert [B]@15") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub Public Sub MethodUpdate_AddAttribute4() Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@11", - "Insert [A]@12", - "Insert [B]@15") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub Public Sub MethodUpdate_UpdateAttribute() - Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [A]@12 -> [A(1)]@12") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A(1)", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub Public Sub MethodUpdate_DeleteAttribute() - Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete []@11", - "Delete [A]@12") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Sub F()", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub Public Sub MethodUpdate_DeleteAttribute2() - Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update []@11 -> []@11", - "Delete [B]@15") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Sub F()", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub Public Sub MethodUpdate_DeleteAttribute3() - Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim src2 = "Class C : " & vbLf & "Sub F() : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete []@14", - "Delete [B]@15") + "Update [Sub F()]@11 -> [Sub F()]@11") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Sub F()", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Sub F()", FeaturesResources.method)) End Sub @@ -4936,7 +4945,7 @@ End Class End Sub - public sub PartialTypes_ConstructorWithInitializerUpdates() + Public Sub PartialTypes_ConstructorWithInitializerUpdates() Dim srcA1 = " Imports System @@ -5099,7 +5108,7 @@ End Class End Sub - public sub PartialDeclaration_Delete() + Public Sub PartialDeclaration_Delete() Dim srcA1 = " Partial Class C Sub New() @@ -8784,81 +8793,82 @@ End Class Public Sub ParameterAttributeInsert1() Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@24", - "Insert [A]@25") + "Update [a As Integer]@24 -> [a As Integer]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "a As Integer", FeaturesResources.parameter)) End Sub Public Sub ParameterAttributeInsert2() Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update []@24 -> []@24", - "Insert [B]@28") + "Update [a As Integer]@24 -> [a As Integer]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "a As Integer", FeaturesResources.parameter)) End Sub Public Sub ParameterAttributeDelete() - Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete []@24", - "Delete [A]@25") + "Update [a As Integer]@24 -> [a As Integer]@24") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "a As Integer", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "a As Integer", FeaturesResources.parameter)) End Sub Public Sub ParameterAttributeUpdate() - Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" + Dim attribute = "Public Class AAttribute : Inherits System.Attribute : End Class" & vbCrLf & + "Public Class BAttribute : Inherits System.Attribute : End Class" & vbCrLf + + Dim src1 = attribute & "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" + Dim src2 = attribute & "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [A(1)]@25 -> [A(2)]@25", - "Update [C]@31 -> [B]@31") + "Update [a As Integer]@154 -> [a As Integer]@154") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "A(2)", FeaturesResources.attribute), - Diagnostic(RudeEditKind.Update, "B", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "a As Integer", FeaturesResources.parameter)) End Sub Public Sub ReturnValueAttributeUpdate() - Dim src1 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" - Dim src2 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" + Dim attribute = "Public Class AAttribute : Inherits System.Attribute : End Class" & vbCrLf & + "Public Class BAttribute : Inherits System.Attribute : End Class" & vbCrLf + + Dim src1 = attribute + "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" + Dim src2 = attribute + "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Update [A]@35 -> [B]@35") + "Update [As Integer]@161 -> [As Integer]@161") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Update, "B", FeaturesResources.attribute)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method)) End Sub Public Sub ParameterAttributeReorder() - Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" - Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" + Dim src1 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" + Dim src2 = "Class C : " & vbLf & "Public Sub M(a As Integer) : End Sub : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Reorder [A(2)]@31 -> @25") + "Update [a As Integer]@24 -> [a As Integer]@24") edits.VerifyRudeDiagnostics() End Sub @@ -8866,29 +8876,27 @@ End Class Public Sub FunctionAsClauseAttributeInsert() Dim src1 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" - Dim src2 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" + Dim src2 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert []@34", - "Insert [A]@35") + "Update [As Integer]@31 -> [As Integer]@31") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Insert, "", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method)) End Sub Public Sub FunctionAsClauseAttributeDelete() - Dim src1 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" + Dim src1 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" Dim src2 = "Class C : " & vbLf & "Public Function M() As Integer" & vbLf & "Return 0 : End Function : End Class" Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete []@34", - "Delete [A]@35") + "Update [As Integer]@31 -> [As Integer]@31") edits.VerifyRudeDiagnostics( - Diagnostic(RudeEditKind.Delete, "Public Function M()", VBFeaturesResources.attributes)) + Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "Public Function M()", FeaturesResources.method)) End Sub @@ -8898,9 +8906,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Delete [As Integer]@31", - "Delete []@34", - "Delete [A]@35") + "Delete [As Integer]@31") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Delete, "Public Function M()", VBFeaturesResources.as_clause)) @@ -8913,9 +8919,7 @@ End Class Dim edits = GetTopEdits(src1, src2) edits.VerifyEdits( - "Insert [As Integer]@31", - "Insert []@34", - "Insert [A]@35") + "Insert [As Integer]@31") edits.VerifyRudeDiagnostics( Diagnostic(RudeEditKind.Insert, "As Integer", VBFeaturesResources.as_clause)) diff --git a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb index 54061eb80965c..81b7738b5a72d 100644 --- a/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb +++ b/src/EditorFeatures/VisualBasicTest/EditAndContinue/VisualBasicEditAndContinueAnalyzerTests.vb @@ -161,8 +161,7 @@ End Enum Delegate Function D2 As C(Of T) Delegate Sub D2 As C(Of T) -<Attrib> - + Public MustInherit Class Z Dim f0 As Integer Dim WithEvents EClass As New EventClass diff --git a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs index dd102f7e42bfa..536701ea63cd6 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/CSharpEditAndContinueAnalyzer.cs @@ -1238,13 +1238,33 @@ protected override SyntaxNode GetSymbolDeclarationSyntax(SyntaxReference referen SemanticModel model, CancellationToken cancellationToken) { - if (node.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter, SyntaxKind.UsingDirective, SyntaxKind.NamespaceDeclaration)) + if (editKind == EditKind.Update) + { + if (node.IsKind(SyntaxKind.Parameter)) + { + // If this is a parameter of a delegate, the symbol will be the Invoke method, but we need to go back up to the delegate itself + // so the analysis can see the attributes + var parameterSymbol = model.GetRequiredDeclaredSymbol(node, cancellationToken); + if (parameterSymbol.ContainingSymbol is IMethodSymbol { MethodKind: MethodKind.DelegateInvoke } invokeMethodSymbol) + { + parameterSymbol = invokeMethodSymbol.ContainingSymbol; + } + return parameterSymbol; + } + + if (node is FieldDeclarationSyntax field) + { + // If attributes on a field change then we get the field declaration here, but GetDeclaredSymbol needs an actual variable declaration + // Fortunately attributes are shared across all of them, so we don't need to be too fancy + return model.GetDeclaredSymbol(field.Declaration.Variables.First(), cancellationToken); + } + } + else if (node.IsKind(SyntaxKind.Parameter, SyntaxKind.TypeParameter)) { return null; } - // Enum declaration update that removes/adds a trailing comma. - if (editKind == EditKind.Update && node.IsKind(SyntaxKind.EnumDeclaration)) + if (node.IsKind(SyntaxKind.UsingDirective, SyntaxKind.NamespaceDeclaration)) { return null; } @@ -1337,11 +1357,12 @@ protected override void ReportLambdaSignatureRudeEdits( SyntaxNode oldLambdaBody, SemanticModel newModel, SyntaxNode newLambdaBody, + EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics, out bool hasErrors, CancellationToken cancellationToken) { - base.ReportLambdaSignatureRudeEdits(oldModel, oldLambdaBody, newModel, newLambdaBody, diagnostics, out hasErrors, cancellationToken); + base.ReportLambdaSignatureRudeEdits(oldModel, oldLambdaBody, newModel, newLambdaBody, capabilities, diagnostics, out hasErrors, cancellationToken); if (IsLocalFunctionBody(oldLambdaBody) != IsLocalFunctionBody(newLambdaBody)) { @@ -2283,9 +2304,20 @@ private void ClassifyInsert(SyntaxNode node) case SyntaxKind.TypeParameter: case SyntaxKind.TypeParameterConstraintClause: case SyntaxKind.TypeParameterList: + ReportError(RudeEditKind.Insert); + return; + case SyntaxKind.Attribute: case SyntaxKind.AttributeList: - ReportError(RudeEditKind.Insert); + // To allow inserting of attributes we need to check if the inserted attribute + // is a pseudo-custom attribute that CLR allows us to change, or if it is a compiler well-know attribute + // that affects the generated IL, so we defer those checks until semantic analysis. + + // Unless the attribute is a module/assembly attribute + if (node.IsParentKind(SyntaxKind.CompilationUnit) || node.Parent.IsParentKind(SyntaxKind.CompilationUnit)) + { + ReportError(RudeEditKind.Insert); + } return; } @@ -2368,10 +2400,15 @@ private void ClassifyDelete(SyntaxNode oldNode) case SyntaxKind.AttributeList: case SyntaxKind.Attribute: - // To allow removal of attributes we would need to check if the removed attribute - // is a pseudo-custom attribute that CLR allows us to change, or if it is a compiler well-know attribute - // that affects the generated IL. - ReportError(RudeEditKind.Delete); + // To allow removal of attributes we need to check if the removed attribute + // is a pseudo-custom attribute that CLR does not allow us to change, or if it is a compiler well-know attribute + // that affects the generated IL, so we defer those checks until semantic analysis. + + // Unless the attribute is a module/assembly attribute + if (oldNode.IsParentKind(SyntaxKind.CompilationUnit) || oldNode.Parent.IsParentKind(SyntaxKind.CompilationUnit)) + { + ReportError(RudeEditKind.Delete); + } return; case SyntaxKind.TypeParameter: @@ -2518,9 +2555,15 @@ private void ClassifyUpdate(SyntaxNode oldNode, SyntaxNode newNode) return; case SyntaxKind.Attribute: - // Dev12 reports "Rename" if the attribute type name is changed. - // But such update is actually not renaming the attribute, it's changing what attribute is applied. - ReportError(RudeEditKind.Update); + // To allow update of attributes we need to check if the updated attribute + // is a pseudo-custom attribute that CLR allows us to change, or if it is a compiler well-know attribute + // that affects the generated IL, so we defer those checks until semantic analysis. + + // Unless the attribute is a module/assembly attribute + if (newNode.IsParentKind(SyntaxKind.CompilationUnit) || newNode.Parent.IsParentKind(SyntaxKind.CompilationUnit)) + { + ReportError(RudeEditKind.Update); + } return; case SyntaxKind.TypeParameterList: @@ -2798,8 +2841,13 @@ private void ClassifyUpdate(EnumMemberDeclarationSyntax oldNode, EnumMemberDecla return; } - Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.EqualsValue, newNode.EqualsValue)); - ReportError(RudeEditKind.InitializerUpdate); + if (!SyntaxFactory.AreEquivalent(oldNode.EqualsValue, newNode.EqualsValue)) + { + ReportError(RudeEditKind.InitializerUpdate); + return; + } + + // Attributes are processed during semantic analysis } private void ClassifyUpdate(ConstructorDeclarationSyntax oldNode, ConstructorDeclarationSyntax newNode) @@ -2926,8 +2974,13 @@ private void ClassifyUpdate(TypeParameterSyntax oldNode, TypeParameterSyntax new return; } - Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.VarianceKeyword, newNode.VarianceKeyword)); - ReportError(RudeEditKind.VarianceUpdate); + if (!SyntaxFactory.AreEquivalent(oldNode.VarianceKeyword, newNode.VarianceKeyword)) + { + ReportError(RudeEditKind.VarianceUpdate); + return; + } + + // attribute changes are handled by semantics } private void ClassifyUpdate(TypeParameterConstraintClauseSyntax oldNode, TypeParameterConstraintClauseSyntax newNode) @@ -2962,8 +3015,13 @@ private void ClassifyUpdate(ParameterSyntax oldNode, ParameterSyntax newNode) return; } - Debug.Assert(!SyntaxFactory.AreEquivalent(oldNode.Default, newNode.Default)); - ReportError(RudeEditKind.InitializerUpdate); + if (!SyntaxFactory.AreEquivalent(oldNode.Default, newNode.Default)) + { + ReportError(RudeEditKind.InitializerUpdate); + return; + } + + // Attribute changes handled in semantics } private void ClassifyUpdate(AttributeListSyntax oldNode, AttributeListSyntax newNode) diff --git a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs index 11a4bfd82d71f..8a4aa880d6630 100644 --- a/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs +++ b/src/Features/CSharp/Portable/EditAndContinue/SyntaxComparer.cs @@ -269,13 +269,6 @@ internal Label Classify(SyntaxKind kind, SyntaxNode? node, out bool isLeaf) case SyntaxKind.Parameter: return Label.Parameter; - case SyntaxKind.AttributeList: - return Label.AttributeList; - - case SyntaxKind.Attribute: - isLeaf = true; - return Label.Attribute; - case SyntaxKind.ConstructorDeclaration: // Root when matching constructor bodies. return Label.ConstructorDeclaration; @@ -286,7 +279,7 @@ internal Label Classify(SyntaxKind kind, SyntaxNode? node, out bool isLeaf) return ClassifyStatementSyntax(kind, node, out isLeaf); } - return ClassifyTopSyntax(kind, out isLeaf); + return ClassifyTopSyntax(kind, node, out isLeaf); } private static Label ClassifyStatementSyntax(SyntaxKind kind, SyntaxNode? node, out bool isLeaf) @@ -535,7 +528,7 @@ private static Label ClassifyStatementSyntax(SyntaxKind kind, SyntaxNode? node, return Label.Ignored; } - private static Label ClassifyTopSyntax(SyntaxKind kind, out bool isLeaf) + private static Label ClassifyTopSyntax(SyntaxKind kind, SyntaxNode? node, out bool isLeaf) { isLeaf = false; @@ -626,6 +619,23 @@ private static Label ClassifyTopSyntax(SyntaxKind kind, out bool isLeaf) // For top syntax, a variable declarator is a leaf node isLeaf = true; return Label.FieldVariableDeclarator; + + case SyntaxKind.AttributeList: + // Only module/assembly attributes are labelled + if (node is not null && node.IsParentKind(SyntaxKind.CompilationUnit)) + { + return Label.AttributeList; + } + break; + + case SyntaxKind.Attribute: + // Only module/assembly attributes are labelled + if (node is { Parent: { } parent } && parent.IsParentKind(SyntaxKind.CompilationUnit)) + { + isLeaf = true; + return Label.Attribute; + } + break; } // If we got this far, its an unlabelled node. For top diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 0f1cdff914830..81e68f05df5c7 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -2898,6 +2898,9 @@ private async Task> AnalyzeSemanticsAsync( processedSymbols.Remove(newSymbol); } + // Need to check for attribute rude edits for fields and properties + AnalyzeCustomAttributes(oldSymbol, newSymbol, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + DeferConstructorEdit(oldSymbol.ContainingType, newSymbol.ContainingType, newDeclaration, syntaxMap, newSymbol.IsStatic, ref instanceConstructorEdits, ref staticConstructorEdits); // Don't add a separate semantic edit. @@ -2916,6 +2919,8 @@ private async Task> AnalyzeSemanticsAsync( if (editKind == SemanticEditKind.Update) { + AnalyzeCustomAttributes(oldSymbol, newSymbol, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + // The only update to the type itself that's supported is an addition or removal of the partial modifier, // which does not have impact on the emitted type metadata. if (newSymbol is INamedTypeSymbol) @@ -2923,23 +2928,19 @@ private async Task> AnalyzeSemanticsAsync( continue; } - // The field/property itself is being updated. Currently we do not allow any modifiers or attributes to be updated. + // The field/property itself is being updated. Currently we do not allow any modifiers to be updated. Attribute + // updates will have been handled already if (newSymbol is IFieldSymbol or IPropertySymbol) { continue; } -#if TODO_ACCESSORS - // The property itself is being updated. Currently we do not allow any modifiers or attributes to be updated, - // so the only case when this happens is in C# for a property/indexer that has an expression body. - // The symbol that's actually being updated is the getter. - // TODO: This will need to be revisited in https://github.com/dotnet/roslyn/issues/52300 - if (newSymbol is IPropertySymbol { GetMethod: var propertyGetter and not null }) + // The only updates allowed for a parameter or type parameter is an attribute change, but we only need the edit + // for the containing symbol which will be handled elsewhere. + if (newSymbol is IParameterSymbol or ITypeParameterSymbol) { - newSymbol = propertyGetter; - lazySymbolKey = null; + continue; } -#endif } lazySymbolKey ??= SymbolKey.Create(newSymbol, cancellationToken); @@ -3061,6 +3062,197 @@ private async Task> AnalyzeSemanticsAsync( } } + private void AnalyzeCustomAttributes(ISymbol? oldSymbol, ISymbol newSymbol, EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics, ArrayBuilder? semanticEdits, Func? syntaxMap, CancellationToken cancellationToken) + { + var needsEdit = false; + + if (newSymbol is IMethodSymbol newMethod) + { + if (oldSymbol is not IMethodSymbol oldMethod) + { + return; + } + + needsEdit |= HasCustomAttributeChanges(oldMethod.GetReturnTypeAttributes(), newMethod.GetReturnTypeAttributes(), newMethod, capabilities, diagnostics); + } + else if (newSymbol is INamedTypeSymbol { DelegateInvokeMethod: not null } newType) + { + var oldType = oldSymbol as INamedTypeSymbol; + // If this is a delegate with attributes on its return type for example, they are found on the DelegateInvokeMethod + AnalyzeCustomAttributes(oldType?.DelegateInvokeMethod, newType.DelegateInvokeMethod, capabilities, diagnostics, semanticEdits, syntaxMap, cancellationToken); + } + + foreach (var parameter in newSymbol.GetParameters()) + { + var oldParameter = oldSymbol?.GetParameters().FirstOrDefault(p => p.Name.Equals(parameter.Name)); + needsEdit |= HasCustomAttributeChanges(oldParameter?.GetAttributes(), parameter.GetAttributes(), parameter, capabilities, diagnostics); + } + + foreach (var typeParam in newSymbol.GetTypeParameters()) + { + var oldParameter = oldSymbol?.GetTypeParameters().FirstOrDefault(p => p.Name.Equals(typeParam.Name)); + needsEdit |= HasCustomAttributeChanges(oldParameter?.GetAttributes(), typeParam.GetAttributes(), typeParam, capabilities, diagnostics); + } + + // This is the only case we care about whether to issue an edit or not, because this is the only case where types have their attributes checked + // and types are the only things that would otherwise not have edits reported. + needsEdit |= HasCustomAttributeChanges(oldSymbol?.GetAttributes(), newSymbol.GetAttributes(), newSymbol, capabilities, diagnostics); + + // If we don't need to add an edit, then we're done + if (!needsEdit || semanticEdits is null) + { + return; + } + + // Most symbol types will automatically have an edit added, so we just need to handle a few + if (newSymbol is INamedTypeSymbol or IFieldSymbol or IPropertySymbol) + { + var symbolKey = SymbolKey.Create(newSymbol, cancellationToken); + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, syntaxMapTree: null, partialType: null)); + } + else if (newSymbol is ITypeParameterSymbol or IMethodSymbol { MethodKind: MethodKind.DelegateInvoke }) + { + var symbolKey = SymbolKey.Create(newSymbol.ContainingSymbol, cancellationToken); + semanticEdits.Add(new SemanticEditInfo(SemanticEditKind.Update, symbolKey, syntaxMap, syntaxMapTree: null, partialType: null)); + } + } + + private bool HasCustomAttributeChanges(ImmutableArray? oldAttributes, ImmutableArray newAttributes, ISymbol newSymbol, EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics) + { + using var _ = ArrayBuilder.GetInstance(out var changedAttributes); + + FindChangedAttributes(oldAttributes, newAttributes, changedAttributes); + if (oldAttributes.HasValue) + { + FindChangedAttributes(newAttributes, oldAttributes.Value, changedAttributes); + } + + if (changedAttributes.Count == 0) + { + return false; + } + + // We need diagnostics reported if the runtime doesn't support changing attributes, + // but even if it does, only attributes stored in the CustomAttributes table are editable + if (!capabilities.HasFlag(EditAndContinueCapabilities.ChangeCustomAttributes) || + changedAttributes.Any(IsNonCustomAttribute)) + { + var newNode = FindSyntaxNode(newSymbol); + diagnostics.Add(new RudeEditDiagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, GetDiagnosticSpan(newNode, EditKind.Update), newNode, new[] { GetDisplayName(newNode, EditKind.Update) })); + + // If the runtime doesn't support edits then pretend there weren't changes, so no edits are produced + return false; + } + + return true; + + static void FindChangedAttributes(ImmutableArray? oldAttributes, ImmutableArray newAttributes, ArrayBuilder changedAttributes) + { + for (var i = 0; i < newAttributes.Length; i++) + { + var newAttribute = newAttributes[i]; + var oldAttribute = FindMatch(newAttribute, oldAttributes); + + if (oldAttribute is null) + { + changedAttributes.Add(newAttribute); + } + } + } + + static AttributeData? FindMatch(AttributeData attribute, ImmutableArray? oldAttributes) + { + if (!oldAttributes.HasValue) + { + return null; + } + + foreach (var match in oldAttributes.Value) + { + if (SymbolEquivalenceComparer.Instance.Equals(match.AttributeClass, attribute.AttributeClass)) + { + if (SymbolEquivalenceComparer.Instance.Equals(match.AttributeConstructor, attribute.AttributeConstructor) && + match.ConstructorArguments.SequenceEqual(attribute.ConstructorArguments, TypedConstantComparer.Instance) && + match.NamedArguments.SequenceEqual(attribute.NamedArguments, NamedArgumentComparer.Instance)) + { + return match; + } + } + } + return null; + } + + static SyntaxNode FindSyntaxNode(ISymbol symbol) + { + // In VB parameters of delegates don't have declaring syntax references so we have to go all the way up to the delegate + // See: https://github.com/dotnet/roslyn/issues/53337 + if (symbol.DeclaringSyntaxReferences.Length == 0) + { + return FindSyntaxNode(symbol.ContainingSymbol); + } + + return symbol.DeclaringSyntaxReferences.First().GetSyntax(); + } + + static bool IsNonCustomAttribute(AttributeData attribute) + { + // TODO: Use a compiler API to get this information rather than hard coding a list: https://github.com/dotnet/roslyn/issues/53410 + + // This list comes from ShouldEmitAttribute in src\Compilers\CSharp\Portable\Symbols\Attributes\AttributeData.cs + // and src\Compilers\VisualBasic\Portable\Symbols\Attributes\AttributeData.vb + return attribute.AttributeClass?.ToNameDisplayString() switch + { + "System.CLSCompliantAttribute" => true, + "System.Diagnostics.CodeAnalysis.AllowNullAttribute" => true, + "System.Diagnostics.CodeAnalysis.DisallowNullAttribute" => true, + "System.Diagnostics.CodeAnalysis.MaybeNullAttribute" => true, + "System.Diagnostics.CodeAnalysis.NotNullAttribute" => true, + "System.NonSerializedAttribute" => true, + "System.Reflection.AssemblyAlgorithmIdAttribute" => true, + "System.Reflection.AssemblyCultureAttribute" => true, + "System.Reflection.AssemblyFlagsAttribute" => true, + "System.Reflection.AssemblyVersionAttribute" => true, + "System.Runtime.CompilerServices.DllImportAttribute" => true, // Already covered by other rude edits, but included for completeness + "System.Runtime.CompilerServices.IndexerNameAttribute" => true, + "System.Runtime.CompilerServices.MethodImplAttribute" => true, + "System.Runtime.CompilerServices.SpecialNameAttribute" => true, + "System.Runtime.CompilerServices.TypeForwardedToAttribute" => true, + "System.Runtime.InteropServices.ComImportAttribute" => true, + "System.Runtime.InteropServices.DefaultParameterValueAttribute" => true, + "System.Runtime.InteropServices.FieldOffsetAttribute" => true, + "System.Runtime.InteropServices.InAttribute" => true, + "System.Runtime.InteropServices.MarshalAsAttribute" => true, + "System.Runtime.InteropServices.OptionalAttribute" => true, + "System.Runtime.InteropServices.OutAttribute" => true, + "System.Runtime.InteropServices.PreserveSigAttribute" => true, + "System.Runtime.InteropServices.StructLayoutAttribute" => true, + "System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeImportAttribute" => true, + "System.Security.DynamicSecurityMethodAttribute" => true, + "System.SerializableAttribute" => true, + not null => IsSecurityAttribute(attribute.AttributeClass), + _ => false + }; + } + + static bool IsSecurityAttribute(INamedTypeSymbol namedTypeSymbol) + { + // Security attributes are any attribute derived from System.Security.Permissions.SecurityAttribute, directly or indirectly + + var symbol = namedTypeSymbol; + while (symbol is not null) + { + if (symbol.ToNameDisplayString() == "System.Security.Permissions.SecurityAttribute") + { + return true; + } + + symbol = symbol.BaseType; + } + + return false; + } + } + private static bool CanAddNewMember(ISymbol newSymbol, EditAndContinueCapabilities capabilities) { if (newSymbol is IMethodSymbol or IPropertySymbol) // Properties are just get_ and set_ methods @@ -3622,7 +3814,7 @@ private void ReportLambdaAndClosureRudeEdits( return; } - ReportLambdaSignatureRudeEdits(oldModel, oldLambdaBody, newModel, newLambdaInfo.NewBody, diagnostics, out var hasErrors, cancellationToken); + ReportLambdaSignatureRudeEdits(oldModel, oldLambdaBody, newModel, newLambdaInfo.NewBody, capabilities, diagnostics, out var hasErrors, cancellationToken); anySignatureErrors |= hasErrors; } @@ -4291,6 +4483,7 @@ protected virtual void ReportLambdaSignatureRudeEdits( SyntaxNode oldLambdaBody, SemanticModel newModel, SyntaxNode newLambdaBody, + EditAndContinueCapabilities capabilities, ArrayBuilder diagnostics, out bool hasErrors, CancellationToken cancellationToken) @@ -4310,6 +4503,8 @@ protected virtual void ReportLambdaSignatureRudeEdits( var oldLambdaSymbol = GetLambdaExpressionSymbol(oldModel, oldLambda, cancellationToken); var newLambdaSymbol = GetLambdaExpressionSymbol(newModel, newLambda, cancellationToken); + AnalyzeCustomAttributes(oldLambdaSymbol, newLambdaSymbol, capabilities, diagnostics, semanticEdits: null, syntaxMap: null, cancellationToken); + RudeEditKind rudeEdit; if (!oldLambdaSymbol.Parameters.SequenceEqual(newLambdaSymbol.Parameters, s_assemblyEqualityComparer.ParameterEquivalenceComparer)) @@ -4456,6 +4651,36 @@ internal static void AddNodes(ArrayBuilder nodes, SeparatedSyntax } } + private sealed class TypedConstantComparer : IEqualityComparer + { + public static TypedConstantComparer Instance = new TypedConstantComparer(); + + public bool Equals(TypedConstant x, TypedConstant y) + => x.Kind.Equals(y.Kind) && + x.IsNull.Equals(y.IsNull) && + SymbolEquivalenceComparer.Instance.Equals(x.Type, y.Type) && + x.Kind switch + { + TypedConstantKind.Array => x.Values.SequenceEqual(y.Values, TypedConstantComparer.Instance), + _ => object.Equals(x.Value, y.Value) + }; + + public int GetHashCode(TypedConstant obj) + => obj.GetHashCode(); + } + + private sealed class NamedArgumentComparer : IEqualityComparer> + { + public static NamedArgumentComparer Instance = new NamedArgumentComparer(); + + public bool Equals(KeyValuePair x, KeyValuePair y) + => x.Key.Equals(y.Key) && + TypedConstantComparer.Instance.Equals(x.Value, y.Value); + + public int GetHashCode(KeyValuePair obj) + => obj.GetHashCode(); + } + #endregion #region Testing diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs index 0258fdfe359dd..2d6a527566aac 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueCapabilities.cs @@ -37,6 +37,11 @@ internal enum EditAndContinueCapabilities /// /// Creating a new type definition. /// - NewTypeDefinition = 1 << 4 + NewTypeDefinition = 1 << 4, + + /// + /// Adding, updating and deleting of custom attributes (as distinct from pseudo-custom attributes) + /// + ChangeCustomAttributes = 1 << 5, } } diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs index 8b97ca9ab8711..32c79d77a897f 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueDiagnosticDescriptors.cs @@ -153,6 +153,7 @@ void AddGeneralDiagnostic(EditAndContinueErrorCode code, string resourceName, Di 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)); // VB specific AddRudeEdit(RudeEditKind.HandlesClauseUpdate, nameof(FeaturesResources.Updating_the_Handles_clause_of_0_will_prevent_the_debug_session_from_continuing)); diff --git a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs index 3632cdd79bb40..4dbc936bb9529 100644 --- a/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs +++ b/src/Features/Core/Portable/EditAndContinue/EditAndContinueWorkspaceService.cs @@ -114,6 +114,7 @@ internal static EditAndContinueCapabilities ParseCapabilities(ImmutableArray EditAndContinueCapabilities.AddStaticFieldToExistingType, "AddInstanceFieldToExistingType" => EditAndContinueCapabilities.AddInstanceFieldToExistingType, "NewTypeDefinition" => EditAndContinueCapabilities.NewTypeDefinition, + "ChangeCustomAttributes" => EditAndContinueCapabilities.ChangeCustomAttributes, // To make it eaiser for runtimes to specify more broad capabilities "AddDefinitionToExistingType" => EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddStaticFieldToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType, diff --git a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs index 9696305cf75cf..98c571f9c6802 100644 --- a/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs +++ b/src/Features/Core/Portable/EditAndContinue/RudeEditKind.cs @@ -127,6 +127,7 @@ internal enum RudeEditKind : ushort NotSupportedByRuntime = 97, MakeMethodAsync = 98, MakeMethodIterator = 99, - InsertNotSupportedByRuntime = 100 + InsertNotSupportedByRuntime = 100, + ChangingAttributesNotSupportedByRuntime = 101 } } diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 0bd2f5cc41022..a1e7d9653e735 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -2885,4 +2885,7 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Making a method 'async' will prevent the debug session from continuing. + + Updating the attributes of '{0}' is not supported by the runtime. + \ No newline at end of file diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf index 8d5ac4a753f43..1df883b850441 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.cs.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Pro lambda výrazy používat text bloku diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf index 5c89e609d8198..fb78e23e5bce2 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.de.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Blocktextkörper für Lambdaausdrücke verwenden diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf index e6d5265fd14b4..50a5fd50630e5 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.es.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Usar cuerpo del bloque para las expresiones lambda diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf index 058798e286f84..f88bebc705fb0 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.fr.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Utiliser le corps de bloc pour les expressions lambda diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf index 95ddae6c1a32f..b3debbd02411a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.it.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Usa il corpo del blocco per le espressioni lambda diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf index 42af4b180c2ae..670e4fa05740a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ja.xlf @@ -2095,6 +2095,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of アクティブ ステートメントの前後の '{0}' を更新すると、デバッグ セッションは続行されません。 + + Updating the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions ラムダ式にブロック本体を使用する diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf index 3d5f84fbefbaa..66b63e7857464 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ko.xlf @@ -2095,6 +2095,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 활성 문 주위에서 '{0}'을(를) 업데이트하면 디버그 세션을 계속할 수 없습니다. + + Updating the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions 람다 식에 블록 본문 사용 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf index 909043114c76e..53e1ce23a705a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pl.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Użyj treści bloku dla wyrażeń lambda diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf index d237ee5aa35ff..379852007521a 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.pt-BR.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Usar o corpo do bloco para expressões lambda diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf index f29021df5743f..7692686a359ca 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.ru.xlf @@ -2095,6 +2095,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of Обновление "{0}" рядом с активной инструкцией сделает продолжение сеанса отладки невозможным. + + Updating the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Использовать тело блока для лямбда-выражений diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf index 35b12bd806b28..77600d5869695 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.tr.xlf @@ -2095,6 +2095,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 the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions Lambda ifadeleri için blok vücut kullanımı diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf index da1729d1caf2b..cfefe8521ca2e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hans.xlf @@ -2095,6 +2095,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新活动语句周围的“{0}”将阻止调试会话继续执行。 + + Updating the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions 对 lambda 表达式使用块主体 diff --git a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf index 9fbc11b0add62..01fba5962147e 100644 --- a/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf +++ b/src/Features/Core/Portable/xlf/FeaturesResources.zh-Hant.xlf @@ -2095,6 +2095,11 @@ Zero-width positive lookbehind assertions are typically used at the beginning of 更新使用中陳述式前後的 '{0}',會造成偵錯工作階段無法繼續。 + + Updating the attributes of '{0}' is not supported by the runtime. + Updating the attributes of '{0}' is not supported by the runtime. + + Use block body for lambda expressions 使用 Lambda 運算式的區塊主體 diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb index 0edff2c23df9b..81081ccd2928b 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/SyntaxComparer.vb @@ -323,7 +323,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return ClassifyStatementSyntax(kind, nodeOpt, isLeaf) End If - Return ClassifyTopSytnax(kind, isLeaf, ignoreVariableDeclarations) + Return ClassifyTopSytnax(kind, nodeOpt, isLeaf, ignoreVariableDeclarations) End Function Friend Shared Function ClassifyStatementSyntax(kind As SyntaxKind, nodeOpt As SyntaxNode, ByRef isLeaf As Boolean) As Label @@ -723,7 +723,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Select End Function - Private Shared Function ClassifyTopSytnax(kind As SyntaxKind, ByRef isLeaf As Boolean, ignoreVariableDeclarations As Boolean) As Label + Private Shared Function ClassifyTopSytnax(kind As SyntaxKind, nodeOpt As SyntaxNode, ByRef isLeaf As Boolean, ignoreVariableDeclarations As Boolean) As Label Select Case kind Case SyntaxKind.CompilationUnit isLeaf = False @@ -867,12 +867,19 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return Label.Parameter Case SyntaxKind.AttributeList - isLeaf = False - Return Label.AttributeList + If nodeOpt IsNot Nothing AndAlso nodeOpt.IsParentKind(SyntaxKind.AttributesStatement) Then + isLeaf = False + Return Label.AttributeList + End If + isLeaf = True + Return Label.Ignored Case SyntaxKind.Attribute isLeaf = True - Return Label.Attribute + If nodeOpt IsNot Nothing AndAlso nodeOpt.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then + Return Label.Attribute + End If + Return Label.Ignored Case Else isLeaf = True @@ -933,6 +940,11 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ' But when comparing field definitions we should ignore VariableDeclaration children. ignoreChildFunction = Function(childKind) HasLabel(childKind, ignoreVariableDeclarations:=True) + Case SyntaxKind.AttributesStatement + ' Normally attributes and attribute lists are ignored, but for attribute statements + ' we need to include them, so just assume they're labelled + ignoreChildFunction = Function(childKind) True + Case Else If HasChildren(left) Then ignoreChildFunction = Function(childKind) HasLabel(childKind, ignoreVariableDeclarations:=False) diff --git a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb index 85075aaf5a143..0dcaaea8c095b 100644 --- a/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb +++ b/src/Features/VisualBasic/Portable/EditAndContinue/VisualBasicEditAndContinueAnalyzer.vb @@ -1214,7 +1214,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Dim newSymbols As OneOrMany(Of ISymbol) = Nothing If editKind = EditKind.Delete Then - If Not TryGetSyntaxNodesForEdit(oldNode, oldModel, oldSymbols, cancellationToken) Then + If Not TryGetSyntaxNodesForEdit(editKind, oldNode, oldModel, oldSymbols, cancellationToken) Then Return OneOrMany(Of (ISymbol, ISymbol)).Empty End If @@ -1222,7 +1222,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If If editKind = EditKind.Insert Then - If Not TryGetSyntaxNodesForEdit(newNode, newModel, newSymbols, cancellationToken) Then + If Not TryGetSyntaxNodesForEdit(editKind, newNode, newModel, newSymbols, cancellationToken) Then Return OneOrMany(Of (ISymbol, ISymbol)).Empty End If @@ -1230,8 +1230,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End If If editKind = EditKind.Update Then - If Not TryGetSyntaxNodesForEdit(oldNode, oldModel, oldSymbols, cancellationToken) OrElse - Not TryGetSyntaxNodesForEdit(newNode, newModel, newSymbols, cancellationToken) Then + If Not TryGetSyntaxNodesForEdit(editKind, oldNode, oldModel, oldSymbols, cancellationToken) OrElse + Not TryGetSyntaxNodesForEdit(editKind, newNode, newModel, newSymbols, cancellationToken) Then Return OneOrMany(Of (ISymbol, ISymbol)).Empty End If @@ -1257,6 +1257,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Function Private Shared Function TryGetSyntaxNodesForEdit( + editKind As EditKind, node As SyntaxNode, model As SemanticModel, ByRef symbols As OneOrMany(Of ISymbol), @@ -1271,14 +1272,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.AddHandlerAccessorStatement, SyntaxKind.RemoveHandlerAccessorStatement, SyntaxKind.RaiseEventAccessorStatement, - SyntaxKind.ClassStatement, - SyntaxKind.StructureStatement, - SyntaxKind.InterfaceStatement, - SyntaxKind.ModuleStatement, - SyntaxKind.EnumStatement, SyntaxKind.NamespaceStatement Return False + Case SyntaxKind.SimpleAsClause + If editKind = EditKind.Update AndAlso node.Parent.IsKind(SyntaxKind.FunctionStatement) Then + node = node.Parent ' for attributes on return types of functions + End If + Case SyntaxKind.EventStatement If node.Parent.IsKind(SyntaxKind.EventBlock) Then Return False @@ -1289,19 +1290,26 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return False End If - Case SyntaxKind.SubStatement ' interface method - If node.Parent.IsKind(SyntaxKind.SubBlock) Then - Return False + Case SyntaxKind.Parameter + If editKind = EditKind.Update Then + ' for delegate invoke methods we need to go one step higher, to the delegate itself + Dim parameterSymbol = model.GetDeclaredSymbol(node, cancellationToken) + Dim containingDelegate = TryCast(parameterSymbol.ContainingSymbol, IMethodSymbol) + If containingDelegate IsNot Nothing AndAlso containingDelegate.MethodKind = MethodKind.DelegateInvoke Then + parameterSymbol = containingDelegate.ContainingSymbol + End If + + symbols = OneOrMany.Create(parameterSymbol) + Return True End If + Return False - Case SyntaxKind.FunctionStatement ' interface method - If node.Parent.IsKind(SyntaxKind.FunctionBlock) Then + Case SyntaxKind.TypeParameter + If editKind <> EditKind.Update Then Return False End If - Case SyntaxKind.Parameter, - SyntaxKind.TypeParameter, - SyntaxKind.ImportsStatement, + Case SyntaxKind.ImportsStatement, SyntaxKind.NamespaceBlock Return False @@ -1321,6 +1329,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return True End If node = variableDeclarator.Names(0) + + Case SyntaxKind.FieldDeclaration + If editKind = EditKind.Update Then + ' If attributes on a field change then we get the field declaration here, but GetDeclaredSymbol needs an actual variable name + ' Fortunately attributes are shared across all of them, so we don't need to be too fancy + Dim field = CType(node, FieldDeclarationSyntax) + node = field.Declarators.First().Names.First() + End If + End Select Dim symbol = model.GetDeclaredSymbol(node, cancellationToken) @@ -1870,7 +1887,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return FeaturesResources.operator_ Case SyntaxKind.ConstructorBlock - Return If(CType(node, ConstructorBlockSyntax).SubNewStatement.Modifiers.Any(SyntaxKind.SharedKeyword), VBFeaturesResources.shared_constructor, FeaturesResources.constructor) + Return If(CType(node, ConstructorBlockSyntax).SubNewStatement.Modifiers.Any(SyntaxKind.SharedKeyword), VBFeaturesResources.Shared_constructor, FeaturesResources.constructor) Case SyntaxKind.SubNewStatement Return If(CType(node, SubNewStatementSyntax).Modifiers.Any(SyntaxKind.SharedKeyword), VBFeaturesResources.Shared_constructor, FeaturesResources.constructor) @@ -2257,13 +2274,25 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue SyntaxKind.TypeConstraint, SyntaxKind.TypeParameterList, SyntaxKind.Parameter, - SyntaxKind.Attribute, - SyntaxKind.AttributeList, SyntaxKind.AttributesStatement, SyntaxKind.SimpleAsClause ReportError(RudeEditKind.Insert) Return + Case SyntaxKind.Attribute + ' Only module/assembly attributes are rude + If node.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then + ReportError(RudeEditKind.Insert) + End If + Return + + Case SyntaxKind.AttributeList + ' Only module/assembly attributes are rude + If node.IsParentKind(SyntaxKind.AttributesStatement) Then + ReportError(RudeEditKind.Insert) + End If + Return + Case SyntaxKind.ParameterList ClassifyParameterInsert(DirectCast(node, ParameterListSyntax)) Return @@ -2332,12 +2361,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ' We do not report error here since it will be reported in semantic analysis. Return - Case SyntaxKind.AttributeList, - SyntaxKind.Attribute - ' To allow removal of attributes we would need to check if the removed attribute - ' is a pseudo-custom attribute that CLR allows us to change, or if it is a compiler well-know attribute - ' that affects the generated IL. - ReportError(RudeEditKind.Delete) + Case SyntaxKind.AttributeList + ' Only module/assembly attributes are rude edits + If oldNode.IsParentKind(SyntaxKind.AttributesStatement) Then + ReportError(RudeEditKind.Insert) + End If + Return + + Case SyntaxKind.Attribute + ' Only module/assembly attributes are rude edits + If oldNode.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then + ReportError(RudeEditKind.Insert) + End If Return Case SyntaxKind.TypeParameter, @@ -2524,13 +2559,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue ClassifyUpdate(DirectCast(oldNode, ParameterSyntax), DirectCast(newNode, ParameterSyntax)) Return - Case SyntaxKind.Attribute + Case SyntaxKind.AttributesStatement ReportError(RudeEditKind.Update) Return - Case SyntaxKind.TypeParameterList, - SyntaxKind.ParameterList, - SyntaxKind.AttributeList + Case SyntaxKind.Attribute + ' Only module/assembly attributes are rude edits + If newNode.Parent.IsParentKind(SyntaxKind.AttributesStatement) Then + ReportError(RudeEditKind.Update) + End If + Return + + Case SyntaxKind.AttributeList, + SyntaxKind.TypeParameterList, + SyntaxKind.ParameterList Return Case Else @@ -2579,8 +2621,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return End If - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.UnderlyingType, newNode.UnderlyingType)) - ReportError(RudeEditKind.EnumUnderlyingTypeUpdate) + If Not SyntaxFactory.AreEquivalent(oldNode.UnderlyingType, newNode.UnderlyingType) Then + ReportError(RudeEditKind.EnumUnderlyingTypeUpdate) + Return + End If End Sub Private Sub ClassifyUpdate(oldNode As DelegateStatementSyntax, newNode As DelegateStatementSyntax) @@ -2600,8 +2644,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return End If - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier)) - ReportError(RudeEditKind.Renamed) + If Not SyntaxFactory.AreEquivalent(oldNode.Identifier, newNode.Identifier) Then + ReportError(RudeEditKind.Renamed) + Return + End If End Sub Private Sub ClassifyUpdate(oldNode As FieldDeclarationSyntax, newNode As FieldDeclarationSyntax) @@ -2628,13 +2674,13 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return End If - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.ArrayBounds, newNode.ArrayBounds)) - - If oldNode.ArrayBounds Is Nothing OrElse - newNode.ArrayBounds Is Nothing OrElse - oldNode.ArrayBounds.Arguments.Count <> newNode.ArrayBounds.Arguments.Count Then - ReportError(RudeEditKind.TypeUpdate) - Return + If Not SyntaxFactory.AreEquivalent(oldNode.ArrayBounds, newNode.ArrayBounds) Then + If oldNode.ArrayBounds Is Nothing OrElse + newNode.ArrayBounds Is Nothing OrElse + oldNode.ArrayBounds.Arguments.Count <> newNode.ArrayBounds.Arguments.Count Then + ReportError(RudeEditKind.TypeUpdate) + Return + End If End If ' Otherwise only the size of the array changed, which is a legal initializer update @@ -2870,8 +2916,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return End If - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.Initializer, newNode.Initializer)) - ReportError(RudeEditKind.InitializerUpdate) + If Not SyntaxFactory.AreEquivalent(oldNode.Initializer, newNode.Initializer) Then + ReportError(RudeEditKind.InitializerUpdate) + Return + End If End Sub Private Sub ClassifyUpdate(oldNode As ConstructorBlockSyntax, newNode As ConstructorBlockSyntax) @@ -2889,8 +2937,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue End Sub Private Sub ClassifyUpdate(oldNode As SimpleAsClauseSyntax, newNode As SimpleAsClauseSyntax) - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.Type, newNode.Type)) - ReportError(RudeEditKind.TypeUpdate, newNode.Parent, newNode.Parent) + If Not SyntaxFactory.AreEquivalent(oldNode.Type, newNode.Type) Then + ReportError(RudeEditKind.TypeUpdate, newNode.Parent, newNode.Parent) + End If End Sub Private Sub ClassifyUpdate(oldNode As TypeParameterSyntax, newNode As TypeParameterSyntax) @@ -2899,8 +2948,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue Return End If - Debug.Assert(Not SyntaxFactory.AreEquivalent(oldNode.VarianceKeyword, newNode.VarianceKeyword)) - ReportError(RudeEditKind.VarianceUpdate) + If Not SyntaxFactory.AreEquivalent(oldNode.VarianceKeyword, newNode.VarianceKeyword) Then + ReportError(RudeEditKind.VarianceUpdate) + Return + End If End Sub Private Sub ClassifyUpdate(oldNode As ParameterSyntax, newNode As ParameterSyntax)