diff --git a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs index c96a7e29d4da4..cadd2d351b7b7 100644 --- a/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs +++ b/src/Compilers/CSharp/Test/Emit2/Emit/EditAndContinue/EditAndContinueTests.cs @@ -4128,6 +4128,7 @@ class C validator: g => { g.VerifyTypeDefNames("", "C"); + g.VerifyFieldDefNames(); g.VerifyMethodDefNames("get_P", "set_P", ".ctor"); }) @@ -4137,15 +4138,16 @@ class C { } """, - edits: new[] - { + edits: + [ Edit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), newSymbolProvider: c => c.GetMember("C")), Edit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), newSymbolProvider: c => c.GetMember("C")), Edit(SemanticEditKind.Delete, c => c.GetMember("C.P"), newSymbolProvider: c => c.GetMember("C")), - }, + ], validator: g => { g.VerifyTypeDefNames("HotReloadException"); + g.VerifyFieldDefNames("Code"); g.VerifyMethodDefNames("get_P", "set_P", ".ctor"); // Set the property name to "_deleted" @@ -4202,16 +4204,17 @@ class C public string P { get; set; } } """, - edits: new[] { + edits: [ Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMember("C.P")), - }, + ], validator: g => { g.VerifyTypeDefNames(); + g.VerifyFieldDefNames("

k__BackingField"); g.VerifyMethodDefNames("get_P", "set_P"); g.VerifyMemberRefNames(".ctor", ".ctor"); - g.VerifyEncLogDefinitions(new[] - { + g.VerifyEncLogDefinitions( + [ Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddField), Row(2, TableIndex.Field, EditAndContinueOperation.Default), Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), @@ -4224,9 +4227,9 @@ class C Row(8, TableIndex.CustomAttribute, EditAndContinueOperation.Default), Row(3, TableIndex.MethodSemantics, EditAndContinueOperation.Default), Row(4, TableIndex.MethodSemantics, EditAndContinueOperation.Default) - }); - g.VerifyEncMapDefinitions(new[] - { + ]); + g.VerifyEncMapDefinitions( + [ Handle(2, TableIndex.Field), Handle(1, TableIndex.MethodDef), Handle(2, TableIndex.MethodDef), @@ -4238,7 +4241,7 @@ class C Handle(1, TableIndex.Property), Handle(3, TableIndex.MethodSemantics), Handle(4, TableIndex.MethodSemantics) - }); + ]); var expectedIL = """ { @@ -4539,6 +4542,258 @@ .maxstack 8 .Verify(); } + [Fact] + public void Property_ChangeToAutoProp() + { + using var _ = new EditAndContinueTest() + .AddBaseline( + source: $$""" + class C + { + public string P { get { return "1"; } set { } } + } + """, + validator: g => + { + g.VerifyTypeDefNames("", "C"); + g.VerifyFieldDefNames(); + g.VerifyMethodDefNames("get_P", "set_P", ".ctor"); + }) + + .AddGeneration( + source: """ + class C + { + public string P { get; set; } + } + """, + edits: [ + Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMember("C.P")), + ], + validator: g => + { + g.VerifyTypeDefNames(); + g.VerifyFieldDefNames("

k__BackingField"); + g.VerifyMethodDefNames("get_P", "set_P"); + g.VerifyMemberRefNames(".ctor", ".ctor"); + g.VerifyEncLogDefinitions( + [ + Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddField), + Row(1, TableIndex.Field, EditAndContinueOperation.Default), + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(1, TableIndex.Property, EditAndContinueOperation.Default), + Row(1, TableIndex.Param, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodSemantics, EditAndContinueOperation.Default), + Row(4, TableIndex.MethodSemantics, EditAndContinueOperation.Default) + ]); + g.VerifyEncMapDefinitions( + [ + Handle(1, TableIndex.Field), + Handle(1, TableIndex.MethodDef), + Handle(2, TableIndex.MethodDef), + Handle(1, TableIndex.Param), + Handle(4, TableIndex.CustomAttribute), + Handle(5, TableIndex.CustomAttribute), + Handle(6, TableIndex.CustomAttribute), + Handle(7, TableIndex.CustomAttribute), + Handle(1, TableIndex.Property), + Handle(3, TableIndex.MethodSemantics), + Handle(4, TableIndex.MethodSemantics) + ]); + + var expectedIL = """ + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld 0x04000001 + IL_0006: ret + } + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld 0x04000001 + IL_0007: ret + } + """; + + g.VerifyIL(expectedIL); + }) + .Verify(); + } + + [Fact] + public void Property_ChangeToAutoProp_FieldAccess() + { + using var _ = new EditAndContinueTest(parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()) + .AddBaseline( + source: $$""" + class C + { + public string P { get { return "1"; } set { } } + } + """, + validator: g => + { + g.VerifyTypeDefNames("", "C"); + g.VerifyFieldDefNames(); + g.VerifyMethodDefNames("get_P", "set_P", ".ctor"); + }) + + .AddGeneration( + source: """ + class C + { + public string P { get; set => field = value; } + } + """, + edits: [ + Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMember("C.P")), + ], + validator: g => + { + g.VerifyTypeDefNames(); + g.VerifyFieldDefNames("

k__BackingField"); + g.VerifyMethodDefNames("get_P", "set_P"); + g.VerifyMemberRefNames(".ctor", ".ctor"); + g.VerifyEncLogDefinitions( + [ + Row(2, TableIndex.TypeDef, EditAndContinueOperation.AddField), + Row(1, TableIndex.Field, EditAndContinueOperation.Default), + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(1, TableIndex.Property, EditAndContinueOperation.Default), + Row(1, TableIndex.Param, EditAndContinueOperation.Default), + Row(4, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(5, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(6, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodSemantics, EditAndContinueOperation.Default), + Row(4, TableIndex.MethodSemantics, EditAndContinueOperation.Default) + ]); + g.VerifyEncMapDefinitions( + [ + Handle(1, TableIndex.Field), + Handle(1, TableIndex.MethodDef), + Handle(2, TableIndex.MethodDef), + Handle(1, TableIndex.Param), + Handle(4, TableIndex.CustomAttribute), + Handle(5, TableIndex.CustomAttribute), + Handle(6, TableIndex.CustomAttribute), + Handle(1, TableIndex.Property), + Handle(3, TableIndex.MethodSemantics), + Handle(4, TableIndex.MethodSemantics) + ]); + + var expectedIL = """ + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld 0x04000001 + IL_0006: ret + } + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld 0x04000001 + IL_0007: ret + } + """; + + g.VerifyIL(expectedIL); + }) + .Verify(); + } + + [Fact] + public void Property_AutoProp_AddFieldAccess() + { + using var _ = new EditAndContinueTest(parseOptions: TestOptions.RegularPreview.WithNoRefSafetyRulesAttribute()) + .AddBaseline( + source: $$""" + class C + { + public string P { get; set; } + } + """, + validator: g => + { + g.VerifyTypeDefNames("", "C"); + g.VerifyFieldDefNames("

k__BackingField"); + g.VerifyMethodDefNames("get_P", "set_P", ".ctor"); + }) + + .AddGeneration( + source: """ + class C + { + public string P { get; set => field = value; } + } + """, + edits: [ + Edit(SemanticEditKind.Insert, symbolProvider: c => c.GetMember("C.P")), + ], + validator: g => + { + g.VerifyTypeDefNames(); + g.VerifyFieldDefNames(); + g.VerifyMethodDefNames("get_P", "set_P"); + g.VerifyMemberRefNames(".ctor", ".ctor"); + g.VerifyEncLogDefinitions( + [ + Row(1, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(2, TableIndex.MethodDef, EditAndContinueOperation.Default), + Row(1, TableIndex.Property, EditAndContinueOperation.Default), + Row(1, TableIndex.Param, EditAndContinueOperation.Default), + Row(1, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(7, TableIndex.CustomAttribute, EditAndContinueOperation.Default), + Row(3, TableIndex.MethodSemantics, EditAndContinueOperation.Default), + Row(4, TableIndex.MethodSemantics, EditAndContinueOperation.Default) + ]); + g.VerifyEncMapDefinitions( + [ + Handle(1, TableIndex.MethodDef), + Handle(2, TableIndex.MethodDef), + Handle(1, TableIndex.Param), + Handle(1, TableIndex.CustomAttribute), + Handle(7, TableIndex.CustomAttribute), + Handle(1, TableIndex.Property), + Handle(3, TableIndex.MethodSemantics), + Handle(4, TableIndex.MethodSemantics) + ]); + + var expectedIL = """ + { + // Code size 7 (0x7) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldfld 0x04000001 + IL_0006: ret + } + { + // Code size 8 (0x8) + .maxstack 8 + IL_0000: ldarg.0 + IL_0001: ldarg.1 + IL_0002: stfld 0x04000001 + IL_0007: ret + } + """; + + g.VerifyIL(expectedIL); + }) + .Verify(); + } + [Fact] public void Property_ChangeReturnType() { diff --git a/src/Features/CSharpTest/EditAndContinue/ActiveStatementTests.cs b/src/Features/CSharpTest/EditAndContinue/ActiveStatementTests.cs index ecb6f55caa0d1..ac41ce3687d1a 100644 --- a/src/Features/CSharpTest/EditAndContinue/ActiveStatementTests.cs +++ b/src/Features/CSharpTest/EditAndContinue/ActiveStatementTests.cs @@ -2199,6 +2199,52 @@ class C capabilities: EditAndContinueCapabilities.AddMethodToExistingType); } + [Fact] + public void Property_Update_ExpressionBodyToAutoProp_FieldAccess() + { + var src1 = @" +class C +{ + public int P => 1; +} +"; + var src2 = @" +class C +{ + public int P => field; +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifySemanticDiagnostics( + active, + capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType); + } + + [Fact] + public void Property_Update_Accessor_ExpressionBodyToAutoProp_FieldAccess() + { + var src1 = @" +class C +{ + public int P { get => 1; } +} +"; + var src2 = @" +class C +{ + public int P { get => field; } +} +"; + var edits = GetTopEdits(src1, src2); + var active = GetActiveStatements(src1, src2); + + edits.VerifySemanticDiagnostics( + active, + capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType); + } + [Fact] public void Property_Auto_Record_ReplacingNonPrimaryWithPrimary_Getter() { diff --git a/src/Features/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestVerifier.cs b/src/Features/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestVerifier.cs index 8dcdd79f7b572..0bc9a243bce81 100644 --- a/src/Features/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestVerifier.cs +++ b/src/Features/CSharpTest/EditAndContinue/Helpers/CSharpEditAndContinueTestVerifier.cs @@ -17,6 +17,7 @@ internal sealed class CSharpEditAndContinueTestVerifier(Action? faul public override string ProjectFileExtension => ".csproj"; public override TreeComparer TopSyntaxComparer => SyntaxComparer.TopLevel; public override string? TryGetResource(string keyword) => EditingTestBase.TryGetResource(keyword); + public override ParseOptions ParseOptions => CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview); public override ImmutableArray GetDeclarators(ISymbol method) { diff --git a/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs b/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs index 5175dd4ff97e0..4f4a3690aa25e 100644 --- a/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs +++ b/src/Features/CSharpTest/EditAndContinue/Helpers/EditingTestBase.cs @@ -134,7 +134,7 @@ internal static string GetDocumentFilePath(int documentIndex) private static SyntaxTree ParseSource(string markedSource, int documentIndex = 0) => SyntaxFactory.ParseSyntaxTree( SourceMarkers.Clear(markedSource), - CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp12), + CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.Preview), path: GetDocumentFilePath(documentIndex)); internal static EditScriptDescription GetTopEdits(string methodBody1, string methodBody2, MethodKind kind) diff --git a/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs b/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs index cc004f7d840dd..03bc4fdf81a70 100644 --- a/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs +++ b/src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs @@ -17719,6 +17719,27 @@ public void Property_ExpressionBody_Update() ]); } + [Fact] + public void Property_ExpressionBody_Update_Auto_FieldAccess() + { + var src1 = "class C { int P => 1; }"; + var src2 = "class C { int P => field; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int P => 1;]@10 -> [int P => field;]@10", + "Update [=> 1]@16 -> [=> field]@16"); + + edits.VerifySemantics( + [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P").GetMethod)], + capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType); + + edits.VerifySemanticDiagnostics( + [Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", GetResource("property getter"))], + capabilities: EditAndContinueCapabilities.Baseline); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48628")] public void Property_ExpressionBody_ModifierUpdate() { @@ -17818,6 +17839,124 @@ public void Property_BlockBodyToExpressionBody2() capabilities: EditAndContinueCapabilities.Baseline); } + [Fact] + public void Property_BlockBodyToExpressionBody_Auto_FieldAccess() + { + var src1 = "class C { int P { get { return field; } set; } }"; + var src2 = "class C { int P => field; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int P { get { return field; } set; }]@10 -> [int P => field;]@10", + "Insert [=> field]@16", + "Delete [{ get { return field; } set; }]@16", + "Delete [get { return field; }]@18", + "Delete [set;]@40"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")), + SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C"))); + } + + [Fact] + public void Property_BlockBodyToGetterExpressionBody_Auto_FieldAccess() + { + var src1 = "class C { int P { get { return field; } set; } }"; + var src2 = "class C { int P { get => field; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [get { return field; }]@18 -> [get => field;]@18", + "Delete [set;]@40"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")), + SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C"))); + } + + [Fact] + public void Property_BlockBody_Update_SetAccessor_Auto_FieldAccess() + { + var src1 = "class C { int P { get { return field; } set; } }"; + var src2 = "class C { int P { get { return field; } set { field = value; } } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [set;]@40 -> [set { field = value; }]@40"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P"))); + } + + [Fact] + public void Property_Auto_GetterBlockBodyToGetterExpressionBody_FieldAccess() + { + var src1 = "class C { int P { get { return field; } } }"; + var src2 = "class C { int P { get => field; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [get { return field; }]@18 -> [get => field;]@18"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"))); + } + + [Fact] + public void Property_Auto_SetterBlockBodyToSetterExpressionBody_FieldAccess() + { + var src1 = "class C { int P { set { field = value; } } }"; + var src2 = "class C { int P { set => field = value; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [set { field = value; }]@18 -> [set => field = value;]@18"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C").GetMember("P").SetMethod)); + } + + [Fact] + public void Property_Auto_ExpressionBodyToGetterExpressionBody_FieldAccess() + { + var src1 = "class C { int P => field; }"; + var src2 = "class C { int P { get => field; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int P => field;]@10 -> [int P { get => field; }]@10", + "Insert [{ get => field; }]@16", + "Insert [get => field;]@18", + "Delete [=> field]@16"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"))); + } + + [Fact] + public void Property_Auto_GetterExpressionBodyToExpressionBody_FieldAccess() + { + var src1 = "class C { int P { get => field; } }"; + var src2 = "class C { int P => field; }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [int P { get => field; }]@10 -> [int P => field;]@10", + "Insert [=> field]@16", + "Delete [{ get => field; }]@16", + "Delete [get => field;]@18"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"))); + } + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")] public void Property_ExpressionBodyToGetterExpressionBody() { @@ -18655,6 +18794,7 @@ struct S { public int a; static int c { get; set; } + static int d { get => field; set; } static int e { get { return 0; } set { } } static int g { get; } = 1; static int i { get; set; } = 1; @@ -18669,6 +18809,7 @@ struct S edits.VerifySemanticDiagnostics( Diagnostic(RudeEditKind.InsertOrMoveStructMember, "static int c", GetResource("auto-property"), GetResource("struct")), + Diagnostic(RudeEditKind.InsertOrMoveStructMember, "static int d", GetResource("auto-property"), GetResource("struct")), Diagnostic(RudeEditKind.InsertOrMoveStructMember, "static int g", GetResource("auto-property"), GetResource("struct")), Diagnostic(RudeEditKind.InsertOrMoveStructMember, "static int i", GetResource("auto-property"), GetResource("struct"))); } @@ -18686,6 +18827,31 @@ public void Property_Insert_Auto() capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType); } + [Fact] + public void Property_Insert_Auto_FieldAccess() + { + var src1 = "class C { }"; + var src2 = "class C { int P { get; set => field = value; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [int P { get; set => field = value; }]@10", + "Insert [{ get; set => field = value; }]@16", + "Insert [get;]@18", + "Insert [set => field = value;]@23"); + + edits.VerifySemantics( + [SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("P"))], + capabilities: + EditAndContinueCapabilities.AddMethodToExistingType | + EditAndContinueCapabilities.AddInstanceFieldToExistingType); + + edits.VerifySemanticDiagnostics( + [Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", GetResource("auto-property"))], + capabilities: EditAndContinueCapabilities.Baseline); + } + [Fact] public void Property_Insert_Auto_Static() { @@ -18699,6 +18865,31 @@ public void Property_Insert_Auto_Static() capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddStaticFieldToExistingType); } + [Fact] + public void Property_Insert_Auto_Static_FieldAccess() + { + var src1 = "class C { }"; + var src2 = "class C { static int P { get; set => field = value; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [static int P { get; set => field = value; }]@10", + "Insert [{ get; set => field = value; }]@23", + "Insert [get;]@25", + "Insert [set => field = value;]@30"); + + edits.VerifySemantics( + [SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("P"))], + capabilities: + EditAndContinueCapabilities.AddMethodToExistingType | + EditAndContinueCapabilities.AddStaticFieldToExistingType); + + edits.VerifySemanticDiagnostics( + [Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "static int P", GetResource("auto-property"))], + capabilities: EditAndContinueCapabilities.Baseline); + } + [Fact] public void Property_Insert_Auto_GenericType() { @@ -18720,6 +18911,35 @@ public void Property_Insert_Auto_GenericType() capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType); } + [Fact] + public void Property_Insert_Auto_GenericType_FieldAccess() + { + var src1 = "class C { }"; + var src2 = "class C { int P { get; set => field = value; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [int P { get; set => field = value; }]@13", + "Insert [{ get; set => field = value; }]@19", + "Insert [get;]@21", + "Insert [set => field = value;]@26"); + + edits.VerifySemantics( + [SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("P"))], + capabilities: + EditAndContinueCapabilities.AddMethodToExistingType | + EditAndContinueCapabilities.AddInstanceFieldToExistingType | + EditAndContinueCapabilities.GenericAddMethodToExistingType | + EditAndContinueCapabilities.GenericAddFieldToExistingType); + + edits.VerifySemanticDiagnostics( + [Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", GetResource("auto-property"))], + capabilities: + EditAndContinueCapabilities.AddMethodToExistingType | + EditAndContinueCapabilities.AddInstanceFieldToExistingType); + } + [Fact] public void Property_Insert_Auto_GenericType_Static() { @@ -18741,6 +18961,35 @@ public void Property_Insert_Auto_GenericType_Static() capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddStaticFieldToExistingType); } + [Fact] + public void Property_Insert_Auto_GenericType_Static_FieldAccess() + { + var src1 = "class C { }"; + var src2 = "class C { static int P { get; set => field = value; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Insert [static int P { get; set => field = value; }]@13", + "Insert [{ get; set => field = value; }]@26", + "Insert [get;]@28", + "Insert [set => field = value;]@33"); + + edits.VerifySemantics( + [SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C").GetMember("P"))], + capabilities: + EditAndContinueCapabilities.AddMethodToExistingType | + EditAndContinueCapabilities.AddStaticFieldToExistingType | + EditAndContinueCapabilities.GenericAddMethodToExistingType | + EditAndContinueCapabilities.GenericAddFieldToExistingType); + + edits.VerifySemanticDiagnostics( + [Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "static int P", GetResource("auto-property"))], + capabilities: + EditAndContinueCapabilities.AddMethodToExistingType | + EditAndContinueCapabilities.AddStaticFieldToExistingType); + } + // Design: Adding private accessors should also be allowed since we now allow adding private methods // and adding public properties and/or public accessors are not allowed. [Fact] @@ -19076,6 +19325,36 @@ public void Property_Auto_Accessor_Update_ReplacingImplicitWithExpressionBodiedP capabilities: EditAndContinueCapabilities.Baseline); } + [Fact] + public void Property_Auto_Accessor_Update_ReplaceImplicitGetWithExpressionBody_FieldAccess() + { + var src1 = "class C { int P { get; } }"; + var src2 = "class C { int P { get => field; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [get;]@18 -> [get => field;]@18"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P").GetMethod)); + } + + [Fact] + public void Property_Auto_Accessor_Update_ReplaceImplicitSetWithExpressionBody_FieldAccess() + { + var src1 = "class C { int P { get; set; } }"; + var src2 = "class C { int P { get; set => field = value; } }"; + + var edits = GetTopEdits(src1, src2); + + edits.VerifyEdits( + "Update [set;]@23 -> [set => field = value;]@23"); + + edits.VerifySemantics( + SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P").SetMethod)); + } + [Fact] public void Property_ReadOnlyRef_Insert() { diff --git a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs index 10e67de9ba545..faddbf8513466 100644 --- a/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs +++ b/src/Features/Core/Portable/EditAndContinue/AbstractEditAndContinueAnalyzer.cs @@ -4217,8 +4217,8 @@ private void ReportUpdatedSymbolDeclarationRudeEdits( else if (oldSymbol is IMethodSymbol oldMethod && newSymbol is IMethodSymbol newMethod) { // Changing property accessor to auto-property accessor adds a field: - if (oldMethod is { MethodKind: MethodKind.PropertyGet, AssociatedSymbol: IPropertySymbol oldProperty } && !oldProperty.IsAutoProperty() && - newMethod is { MethodKind: MethodKind.PropertyGet, AssociatedSymbol: IPropertySymbol newProperty } && newProperty.IsAutoProperty() && + if (oldMethod is { MethodKind: MethodKind.PropertyGet or MethodKind.PropertySet, AssociatedSymbol: IPropertySymbol oldProperty } && !oldProperty.IsAutoProperty() && + newMethod is { MethodKind: MethodKind.PropertyGet or MethodKind.PropertySet, AssociatedSymbol: IPropertySymbol newProperty } && newProperty.IsAutoProperty() && !capabilities.Grant(GetRequiredAddFieldCapabilities(newMethod))) { rudeEdit = RudeEditKind.InsertNotSupportedByRuntime; diff --git a/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs b/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs index 0b10743066686..835d7210d8b64 100644 --- a/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs +++ b/src/Features/TestUtilities/EditAndContinue/EditAndContinueTestVerifier.cs @@ -9,17 +9,17 @@ using System.IO; using System.Linq; using System.Threading; -using Microsoft.CodeAnalysis.Differencing; using Microsoft.CodeAnalysis.Contracts.EditAndContinue; +using Microsoft.CodeAnalysis.Differencing; using Microsoft.CodeAnalysis.Emit; +using Microsoft.CodeAnalysis.Host; +using Microsoft.CodeAnalysis.Host.Mef; using Microsoft.CodeAnalysis.Test.Utilities; using Microsoft.CodeAnalysis.Text; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; using static Microsoft.CodeAnalysis.EditAndContinue.AbstractEditAndContinueAnalyzer; -using Microsoft.CodeAnalysis.Host.Mef; -using Microsoft.CodeAnalysis.Host; namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests; @@ -58,6 +58,7 @@ protected EditAndContinueTestVerifier(Action? faultInjector) public abstract string ProjectFileExtension { get; } public abstract TreeComparer TopSyntaxComparer { get; } public abstract string? TryGetResource(string keyword); + public abstract ParseOptions ParseOptions { get; } internal static AbstractEditAndContinueAnalyzer CreateAnalyzer(Action? faultInjector, string languageName) { @@ -463,7 +464,8 @@ private void CreateProjects(EditScriptDescription[] editScripts, AdhocWorkspace language: LanguageName, compilationOutputInfo: default, filePath: Path.Combine(TempRoot.Root, "project" + ProjectFileExtension), - checksumAlgorithm: SourceHashAlgorithms.Default)); + checksumAlgorithm: SourceHashAlgorithms.Default), + parseOptions: ParseOptions); oldProject = workspace.AddProject(projectInfo).WithMetadataReferences(TargetFrameworkUtil.GetReferences(targetFramework)); foreach (var editScript in editScripts) diff --git a/src/Features/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestVerifier.vb b/src/Features/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestVerifier.vb index a414ba2176483..650e755431ae9 100644 --- a/src/Features/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestVerifier.vb +++ b/src/Features/VisualBasicTest/EditAndContinue/Helpers/VisualBasicEditAndContinueTestVerifier.vb @@ -44,6 +44,12 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.EditAndContinue Public Overrides Function TryGetResource(keyword As String) As String Return EditingTestBase.TryGetResource(keyword) End Function + + Public Overrides ReadOnly Property ParseOptions As ParseOptions + Get + Return VisualBasicParseOptions.Default.WithLanguageVersion(LanguageVersion.Latest) + End Get + End Property End Class End Namespace