|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable disable
#pragma warning disable IDE0055 // Collection expression formatting
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.UnitTests;
using Microsoft.CodeAnalysis.Differencing;
using Microsoft.CodeAnalysis.EditAndContinue;
using Microsoft.CodeAnalysis.EditAndContinue.UnitTests;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Test.Utilities;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.CSharp.EditAndContinue.UnitTests;
[UseExportProvider]
public class TopLevelEditingTests : EditingTestBase
{
private static readonly string s_attributeSource = @"
[System.AttributeUsage(System.AttributeTargets.All)]class A : System.Attribute { public A() {} public A(int x) { } }
";
#region Usings
[Fact]
public void Using_Global_Insert1()
{
var src1 = @"
using System.Collections.Generic;
";
var src2 = @"
global using D = System.Diagnostics;
global using System.Collections;
using System.Collections.Generic;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [global using D = System.Diagnostics;]@2",
"Insert [global using System.Collections;]@40");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Global_Insert2()
{
var src1 = @"
using unsafe D3 = int*;
";
var src2 = @"
global using D1 = int;
using D2 = (int, int);
using unsafe D3 = int*;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [global using D1 = int;]@2",
"Insert [using D2 = (int, int);]@26");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Delete1()
{
var src1 = @"
using System.Diagnostics;
";
var src2 = @"";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [using System.Diagnostics;]@2");
Assert.IsType<UsingDirectiveSyntax>(edits.Edits.First().OldNode);
Assert.Null(edits.Edits.First().NewNode);
}
[Fact]
public void Using_Delete2()
{
var src1 = @"
using D = System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
";
var src2 = @"
using System.Collections.Generic;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [using D = System.Diagnostics;]@2",
"Delete [using System.Collections;]@33");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Delete3()
{
var src1 = @"
global using D1 = int;
using D2 = (int, int);
using unsafe D3 = int*;
";
var src2 = @"
using D2 = (int, int);
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [global using D1 = int;]@2",
"Delete [using unsafe D3 = int*;]@50");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Insert1()
{
var src1 = @"
using System.Collections.Generic;
";
var src2 = @"
using D = System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [using D = System.Diagnostics;]@2",
"Insert [using System.Collections;]@33");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Insert2()
{
var src1 = @"
using System.Collections.Generic;
";
var src2 = @"
global using D1 = int;
using D2 = (int, int);
using unsafe D3 = int*;
using System.Collections.Generic;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [global using D1 = int;]@2",
"Insert [using D2 = (int, int);]@26",
"Insert [using unsafe D3 = int*;]@50");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Update1()
{
var src1 = @"
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
";
var src2 = @"
using System.Diagnostics;
using X = System.Collections;
using System.Collections.Generic;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [using System.Collections;]@29 -> [using X = System.Collections;]@29");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Update2()
{
var src1 = @"
using System.Diagnostics;
using X1 = System.Collections;
using System.Collections.Generic;
";
var src2 = @"
using System.Diagnostics;
using X2 = System.Collections;
using System.Collections.Generic;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [using X1 = System.Collections;]@29 -> [using X2 = System.Collections;]@29");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Update3()
{
var src1 = @"
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
";
var src2 = @"
using System;
using System.Collections;
using System.Collections.Generic;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [using System.Diagnostics;]@2 -> [using System;]@2");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Update4()
{
var src1 = @"
using X = int;
using Y = int;
using Z = int;
";
var src2 = @"
using X = string;
using unsafe Y = int*;
global using Z = int;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [using X = int;]@2 -> [using X = string;]@2",
"Update [using Y = int;]@18 -> [using unsafe Y = int*;]@21",
"Update [using Z = int;]@34 -> [global using Z = int;]@45");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Using_Reorder1()
{
var src1 = @"
using System.Diagnostics;
using System.Collections;
using System.Collections.Generic;
";
var src2 = @"
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [using System.Diagnostics;]@2 -> @64");
}
[Fact]
public void Using_Reorder2()
{
var src1 = @"
using X = int;
using Y = string;
";
var src2 = @"
using Y = string;
using X = int;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [using Y = string;]@18 -> @2");
}
[Fact]
public void Using_InsertDelete1()
{
var src1 = @"
namespace N
{
using System.Collections;
}
namespace M
{
}
";
var src2 = @"
namespace N
{
}
namespace M
{
using System.Collections;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [using System.Collections;]@43",
"Delete [using System.Collections;]@22");
}
[Fact]
public void Using_InsertDelete2()
{
var src1 = @"
namespace N
{
using System.Collections;
}
";
var src2 = @"
using System.Collections;
namespace N
{
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [using System.Collections;]@2",
"Delete [using System.Collections;]@22");
}
[Fact]
public void Using_Delete_ChangesCodeMeaning()
{
// This test specifically validates the scenario we _don't_ support, namely when inserting or deleting
// a using directive, if existing code changes in meaning as a result, we don't issue edits for that code.
// If this ever regresses then please buy a lottery ticket because the feature has magically fixed itself.
var src1 = @"
using System.IO;
using DirectoryInfo = N.C;
namespace N
{
public class C
{
public C(string a) { }
public FileAttributes Attributes { get; set; }
}
public class D
{
public void M()
{
var d = new DirectoryInfo(""aa"");
var x = directoryInfo.Attributes;
}
}
}";
var src2 = @"
using System.IO;
namespace N
{
public class C
{
public C(string a) { }
public FileAttributes Attributes { get; set; }
}
public class D
{
public void M()
{
var d = new DirectoryInfo(""aa"");
var x = directoryInfo.Attributes;
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [using DirectoryInfo = N.C;]@20");
edits.VerifySemantics();
}
[Fact]
public void Using_Insert_ForNewCode()
{
// As distinct from the above, this test validates a real world scenario of inserting a using directive
// and changing code that utilizes the new directive to some effect.
var src1 = @"
namespace N
{
class Program
{
static void F()
{
}
}
}";
var src2 = @"
using System;
namespace N
{
class Program
{
static void F()
{
Console.WriteLine(""Hello World!"");
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("N.Program.F")));
}
[Fact]
public void Using_Delete_ForOldCode()
{
var src1 = @"
using System;
namespace N
{
class Program
{
static void F()
{
Console.WriteLine(""Hello World!"");
}
}
}";
var src2 = @"
namespace N
{
class Program
{
static void F()
{
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("N.Program.F")));
}
[Fact]
public void Using_Insert_CreatesAmbiguousCode()
{
// This test validates that we still issue edits for changed valid code, even when unchanged
// code has ambiguities after adding a using.
var src1 = @"
using System.Threading;
namespace N
{
class C
{
void M()
{
// Timer exists in System.Threading and System.Timers
var t = new Timer(s => System.Console.WriteLine(s));
}
}
}";
var src2 = @"
using System.Threading;
using System.Timers;
namespace N
{
class C
{
void M()
{
// Timer exists in System.Threading and System.Timers
var t = new Timer(s => System.Console.WriteLine(s));
}
void M2()
{
// TimersDescriptionAttribute only exists in System.Timers
System.Console.WriteLine(new TimersDescriptionAttribute(""""));
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("N.C.M2"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
#endregion
#region Extern Alias
[Fact]
public void ExternAliasUpdate()
{
var src1 = "extern alias X;";
var src2 = "extern alias Y;";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [extern alias X;]@0 -> [extern alias Y;]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Update, "extern alias Y;", CSharpFeaturesResources.extern_alias));
}
[Fact]
public void ExternAliasInsert()
{
var src1 = "";
var src2 = "extern alias Y;";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [extern alias Y;]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "extern alias Y;", CSharpFeaturesResources.extern_alias));
}
[Fact]
public void ExternAliasDelete()
{
var src1 = "extern alias Y;";
var src2 = "";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [extern alias Y;]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, null, CSharpFeaturesResources.extern_alias));
}
#endregion
#region Assembly/Module 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.VerifySemanticDiagnostics(
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.VerifySemanticDiagnostics(
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.VerifySemanticDiagnostics(
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.VerifySemanticDiagnostics();
}
#endregion
#region Types
[Theory]
[InlineData("class", "struct")]
[InlineData("class", "record")] // TODO: Allow this conversion: https://github.com/dotnet/roslyn/issues/51874
[InlineData("class", "record struct")]
[InlineData("class", "interface")]
[InlineData("struct", "record struct")] // TODO: Allow this conversion: https://github.com/dotnet/roslyn/issues/51874
public void Type_Kind_Update(string oldKeyword, string newKeyword)
{
var src1 = oldKeyword + " C { }";
var src2 = newKeyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [" + oldKeyword + " C { }]@0 -> [" + newKeyword + " C { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeKindUpdate, newKeyword + " C"));
}
[Theory]
[InlineData("class", "struct")]
[InlineData("class", "record")]
[InlineData("class", "record struct")]
[InlineData("class", "interface")]
[InlineData("struct", "record struct")]
public void Type_Kind_Update_Reloadable(string oldKeyword, string newKeyword)
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]" + oldKeyword + " C { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]" + newKeyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[CreateNewOnMetadataUpdate]" + oldKeyword + " C { }]@145 -> [[CreateNewOnMetadataUpdate]" + newKeyword + " C { }]@145");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Modifiers_Static_Remove()
{
var src1 = "public static class C { }";
var src2 = "public class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public static class C { }]@0 -> [public class C { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public class C", FeaturesResources.class_));
}
[Theory]
[InlineData("public")]
[InlineData("protected")]
[InlineData("private")]
[InlineData("private protected")]
[InlineData("internal protected")]
public void Type_Modifiers_Accessibility_Change(string accessibility)
{
var src1 = accessibility + " class C { }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [" + accessibility + " class C { }]@0 -> [class C { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingAccessibility, "class C", FeaturesResources.class_));
}
[Theory]
[InlineData("public", "public")]
[InlineData("internal", "internal")]
[InlineData("", "internal")]
[InlineData("internal", "")]
[InlineData("protected", "protected")]
[InlineData("private", "private")]
[InlineData("private protected", "private protected")]
[InlineData("internal protected", "internal protected")]
public void Type_Modifiers_Accessibility_Partial(string accessibilityA, string accessibilityB)
{
var srcA1 = accessibilityA + " partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = accessibilityB + " partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(),
]);
}
[Fact]
public void Type_Modifiers_Internal_Remove()
{
var src1 = "internal interface C { }";
var src2 = "interface C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Fact]
public void Type_Modifiers_Internal_Add()
{
var src1 = "struct C { }";
var src2 = "internal struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Fact]
public void Type_Modifiers_Accessibility_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public class C { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[CreateNewOnMetadataUpdate]public class C { }]@145 -> [[CreateNewOnMetadataUpdate]internal class C { }]@145");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("interface")]
[InlineData("record")]
[InlineData("record struct")]
public void Type_Modifiers_NestedPrivateInInterface_Remove(string keyword)
{
var src1 = "interface C { private " + keyword + " S { } }";
var src2 = "interface C { " + keyword + " S { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingAccessibility, keyword + " S", GetResource(keyword)));
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("interface")]
[InlineData("record")]
[InlineData("record struct")]
public void Type_Modifiers_NestedPrivateInClass_Add(string keyword)
{
var src1 = "class C { " + keyword + " S { } }";
var src2 = "class C { private " + keyword + " S { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("interface")]
[InlineData("record")]
[InlineData("record struct")]
public void Type_Modifiers_NestedPublicInInterface_Add(string keyword)
{
var src1 = "interface C { " + keyword + " S { } }";
var src2 = "interface C { public " + keyword + " S { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48628")]
public void Type_Modifiers_Unsafe_Add()
{
var src1 = "public class C { }";
var src2 = "public unsafe class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public class C { }]@0 -> [public unsafe class C { }]@0");
edits.VerifySemanticDiagnostics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48628")]
public void Type_Modifiers_Unsafe_Remove()
{
var src1 = @"
using System;
unsafe delegate void D();
class C
{
unsafe class N { }
public unsafe event Action<int> A { add { } remove { } }
unsafe int F() => 0;
unsafe int X;
unsafe int Y { get; }
unsafe C() {}
unsafe ~C() {}
}
";
var src2 = @"
using System;
delegate void D();
class C
{
class N { }
public event Action<int> A { add { } remove { } }
int F() => 0;
int X;
int Y { get; }
C() {}
~C() {}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [unsafe delegate void D();]@17 -> [delegate void D();]@17",
"Update [unsafe class N { }]@60 -> [class N { }]@53",
"Update [public unsafe event Action<int> A { add { } remove { } }]@84 -> [public event Action<int> A { add { } remove { } }]@70",
"Update [unsafe int F() => 0;]@146 -> [int F() => 0;]@125",
"Update [unsafe int X;]@172 -> [int X;]@144",
"Update [unsafe int Y { get; }]@191 -> [int Y { get; }]@156",
"Update [unsafe C() {}]@218 -> [C() {}]@176",
"Update [unsafe ~C() {}]@237 -> [~C() {}]@188");
edits.VerifySemanticDiagnostics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48628")]
public void Type_Modifiers_Unsafe_DeleteInsert()
{
var srcA1 = "partial class C { unsafe void F() { } }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("F"))
]),
]);
}
[Fact]
public void Type_Modifiers_Ref_Add()
{
var src1 = "public struct C { }";
var src2 = "public ref struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public struct C { }]@0 -> [public ref struct C { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public ref struct C", FeaturesResources.struct_));
}
[Fact]
public void Type_Modifiers_Ref_Remove()
{
var src1 = "public ref struct C { }";
var src2 = "public struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public ref struct C { }]@0 -> [public struct C { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public struct C", FeaturesResources.struct_));
}
[Fact]
public void Type_Modifiers_ReadOnly_Add()
{
var src1 = "public struct C { }";
var src2 = "public readonly struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public struct C { }]@0 -> [public readonly struct C { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public readonly struct C", FeaturesResources.struct_));
}
[Fact]
public void Type_Modifiers_ReadOnly_Remove()
{
var src1 = "public readonly struct C { }";
var src2 = "public struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public readonly struct C { }]@0 -> [public struct C { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public struct C", FeaturesResources.struct_));
}
[Theory]
[InlineData("[System.CLSCompliantAttribute]", "CLSCompliantAttribute")]
[InlineData("[System.Diagnostics.CodeAnalysis.AllowNullAttribute]", "AllowNullAttribute")]
[InlineData("[System.Diagnostics.CodeAnalysis.DisallowNullAttribute]", "DisallowNullAttribute")]
[InlineData("[System.Diagnostics.CodeAnalysis.MaybeNullAttribute]", "MaybeNullAttribute")]
[InlineData("[System.Diagnostics.CodeAnalysis.NotNullAttribute]", "NotNullAttribute")]
[InlineData("[System.NonSerializedAttribute]", "NonSerializedAttribute")]
[InlineData("[System.Reflection.AssemblyAlgorithmIdAttribute]", "AssemblyAlgorithmIdAttribute")]
[InlineData("[System.Reflection.AssemblyCultureAttribute]", "AssemblyCultureAttribute")]
[InlineData("[System.Reflection.AssemblyFlagsAttribute]", "AssemblyFlagsAttribute")]
[InlineData("[System.Reflection.AssemblyVersionAttribute]", "AssemblyVersionAttribute")]
[InlineData("[System.Runtime.CompilerServices.DllImportAttribute]", "DllImportAttribute")]
[InlineData("[System.Runtime.CompilerServices.IndexerNameAttribute]", "IndexerNameAttribute")]
[InlineData("[System.Runtime.CompilerServices.MethodImplAttribute]", "MethodImplAttribute")]
[InlineData("[System.Runtime.CompilerServices.SpecialNameAttribute]", "SpecialNameAttribute")]
[InlineData("[System.Runtime.CompilerServices.TypeForwardedToAttribute]", "TypeForwardedToAttribute")]
[InlineData("[System.Runtime.InteropServices.ComImportAttribute]", "ComImportAttribute")]
[InlineData("[System.Runtime.InteropServices.DefaultParameterValueAttribute]", "DefaultParameterValueAttribute")]
[InlineData("[System.Runtime.InteropServices.FieldOffsetAttribute]", "FieldOffsetAttribute")]
[InlineData("[System.Runtime.InteropServices.InAttribute]", "InAttribute")]
[InlineData("[System.Runtime.InteropServices.MarshalAsAttribute]", "MarshalAsAttribute")]
[InlineData("[System.Runtime.InteropServices.OptionalAttribute]", "OptionalAttribute")]
[InlineData("[System.Runtime.InteropServices.OutAttribute]", "OutAttribute")]
[InlineData("[System.Runtime.InteropServices.PreserveSigAttribute]", "PreserveSigAttribute")]
[InlineData("[System.Runtime.InteropServices.StructLayoutAttribute]", "StructLayoutAttribute")]
[InlineData("[System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeImportAttribute]", "WindowsRuntimeImportAttribute")]
[InlineData("[System.Security.DynamicSecurityMethodAttribute]", "DynamicSecurityMethodAttribute")]
[InlineData("[System.SerializableAttribute]", "SerializableAttribute")]
[InlineData("[System.Runtime.CompilerServices.AsyncMethodBuilderAttribute]", "AsyncMethodBuilderAttribute")]
public void Type_Attribute_Insert_SupportedByRuntime_NonCustomAttribute(string attributeType, string attributeName)
{
var src1 = @"class C { public void M(int a) {} }";
var src2 = attributeType + @"class C { public void M(int a) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [class C { public void M(int a) {} }]@0 -> [" + attributeType + "class C { public void M(int a) {} }]@0");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNonCustomAttribute, "class C", attributeName, FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Theory]
[InlineData("struct")]
[InlineData("record struct")]
public void Type_Attribute_Insert_InlineArray(string keyword)
{
var attribute = "namespace System.Runtime.CompilerServices { public class InlineArrayAttribute : Attribute { public InlineArrayAttribute(int n) { } } } ";
var src1 = attribute + keyword + " C { int a; }";
var src2 = attribute + "[System.Runtime.CompilerServices.InlineArray(1)]" + keyword + " C { int a; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttribute, keyword + " C", "InlineArrayAttribute")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Theory]
[InlineData("struct")]
[InlineData("record struct")]
public void Type_Attribute_Update_InlineArray(string keyword)
{
var attribute = "namespace System.Runtime.CompilerServices { public class InlineArrayAttribute : Attribute { public InlineArrayAttribute(int n) { } } } ";
var src1 = attribute + "[System.Runtime.CompilerServices.InlineArray(1)]" + keyword + " C { int a; }";
var src2 = attribute + "[System.Runtime.CompilerServices.InlineArray(2)]" + keyword + " C { int a; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttribute, keyword + " C", "InlineArrayAttribute")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Theory]
[InlineData("struct")]
[InlineData("record struct")]
public void Type_Attribute_Update_InlineArray_Reloadable(string keyword)
{
var attribute = ReloadableAttributeSrc + "namespace System.Runtime.CompilerServices { public class InlineArrayAttribute : Attribute { public InlineArrayAttribute(int n) { } } } ";
var src1 = attribute + "[CreateNewOnMetadataUpdate, InlineArray(1)]" + keyword + " C { int a; }";
var src2 = attribute + "[CreateNewOnMetadataUpdate, InlineArray(2)]" + keyword + " C { int a; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Attribute_Update_NotSupportedByRuntime1()
{
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]class C { }]@98 -> [[A2]class C { }]@98");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Attribute_Update_NotSupportedByRuntime2()
{
var src1 = "[System.Obsolete(\"1\")]class C { }";
var src2 = "[System.Obsolete(\"2\")]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[System.Obsolete(\"1\")]class C { }]@0 -> [[System.Obsolete(\"2\")]class C { }]@0");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Attribute_Delete_NotSupportedByRuntime1()
{
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.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Attribute_Delete_NotSupportedByRuntime2()
{
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 [[B, A]class C { }]@96 -> [[A]class C { }]@96");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory]
[InlineData("struct")]
[InlineData("record struct")]
public void Type_Attribute_Delete_InlineArray(string keyword)
{
var attribute = "namespace System.Runtime.CompilerServices { public class InlineArrayAttribute : Attribute { public InlineArrayAttribute(int n) { } } } ";
var src1 = attribute + "[System.Runtime.CompilerServices.InlineArray(1)]" + keyword + " C { int a; }";
var src2 = attribute + keyword + " C { int a; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttribute, keyword + " C", "InlineArrayAttribute")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact, WorkItem("https://devdiv.visualstudio.com/DevDiv/_workitems/edit/1831006")]
public void Type_Attribute_Update_Null()
{
var attribute = @"
using System;
public class A : Attribute { public A1(int[] array, Type type, Type[] types) {} }
";
var src1 = attribute + "[A(null, null, new Type[] { typeof(C) })]class C { }";
var src2 = attribute + "[A(null, null, null)]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Type_Attribute_Change_Reloadable()
{
var attributeSrc = @"
public class A1 : System.Attribute { }
public class A2 : System.Attribute { }
public class A3 : System.Attribute { }
";
var src1 = ReloadableAttributeSrc + attributeSrc + "[CreateNewOnMetadataUpdate, A1, A2]class C { }";
var src2 = ReloadableAttributeSrc + attributeSrc + "[CreateNewOnMetadataUpdate, A2, A3]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[CreateNewOnMetadataUpdate, A1, A2]class C { }]@267 -> [[CreateNewOnMetadataUpdate, A2, A3]class C { }]@267");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Attribute_ReloadableRemove()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }";
var src2 = ReloadableAttributeSrc + "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Attribute_ReloadableAdd()
{
var src1 = ReloadableAttributeSrc + "class C { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Type_Attribute_ReloadableBase()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class B { } class C : B { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class B { } class C : B { void F() {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Update_Attribute_Insert()
{
var attributes =
"""
class A : System.Attribute { }
class B : System.Attribute { }
""";
var src1 = attributes + "[A]class C { }";
var src2 = attributes + "[A, B]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[A]class C { }]@62 -> [[A, B]class C { }]@62");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Type_Update_Attribute_Insert_Reloadable()
{
var attributes = ReloadableAttributeSrc +
"""
class A : System.Attribute { }
class B : System.Attribute { }
""";
var srcA1 = attributes + "[CreateNewOnMetadataUpdate]partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = attributes + "[CreateNewOnMetadataUpdate][A]partial class C { }";
var srcB2 = "[B]partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Update_Attribute_Insert_NotSupportedByRuntime1()
{
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.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Update_Attribute_Insert_NotSupportedByRuntime2()
{
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(
"Update [class C { }]@48 -> [[A]class C { }]@48");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Update_Attribute_Reorder1()
{
var src1 = "[A(1), B(2), C(3)]class C { }";
var src2 = "[C(3), A(1), B(2)]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[A(1), B(2), C(3)]class C { }]@0 -> [[C(3), A(1), B(2)]class C { }]@0");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Type_Update_Attribute_Reorder2()
{
var src1 = "[A, B, C]class C { }";
var src2 = "[B, C, A]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[A, B, C]class C { }]@0 -> [[B, C, A]class C { }]@0");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Type_Attribute_ReorderAndUpdate_NotSupportedByRuntime()
{
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(
"Update [[System.Obsolete(\"1\"), A, B]class C { }]@96 -> [[A, B, System.Obsolete(\"2\")]class C { }]@96");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "class C", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("interface")]
[InlineData("record")]
[InlineData("record struct")]
public void Type_Rename(string keyword)
{
var src1 = keyword + " C { }";
var src2 = keyword + " D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [" + keyword + " C { }]@0 -> [" + keyword + " D { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, keyword + " D", GetResource(keyword, "C")));
}
[Fact]
public void Type_Rename_AddAndDeleteMember()
{
var src1 = "class C { int x = 1; }";
var src2 = "class D { void F() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [class C { int x = 1; }]@0 -> [class D { void F() { } }]@0",
"Insert [void F() { }]@10",
"Insert [()]@16",
"Delete [int x = 1;]@10",
"Delete [int x = 1]@10",
"Delete [x = 1]@14");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "class D", GetResource("class", "C")));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/54886")]
public void Type_Rename_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[CreateNewOnMetadataUpdate]class C { }]@145 -> [[CreateNewOnMetadataUpdate]class D { }]@145");
// TODO: expected: Replace edit of D
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "class D", GetResource("class", "C")));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/54886")]
public void Type_Rename_Reloadable_AddAndDeleteMember()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { int x = 1; }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class D { void F() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[CreateNewOnMetadataUpdate]class C { int x = 1; }]@145 -> [[CreateNewOnMetadataUpdate]class D { void F() { } }]@145",
"Insert [void F() { }]@182",
"Insert [()]@188",
"Delete [int x = 1;]@182",
"Delete [int x = 1]@182",
"Delete [x = 1]@186");
// TODO: expected: Replace edit of D
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "class D", GetResource("class", "C")));
}
[Fact]
public void Interface_NoModifiers_Insert()
{
var src1 = "";
var src2 = "interface C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Interface_NoModifiers_IntoNamespace_Insert()
{
var src1 = "namespace N { } ";
var src2 = "namespace N { interface C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Interface_NoModifiers_IntoType_Insert()
{
var src1 = "interface N { }";
var src2 = "interface N { interface C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Class_NoModifiers_Insert()
{
var src1 = "";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Class_NoModifiers_IntoNamespace_Insert()
{
var src1 = "namespace N { }";
var src2 = "namespace N { class C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Class_NoModifiers_IntoType_Insert()
{
var src1 = "struct N { }";
var src2 = "struct N { class C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Struct_NoModifiers_Insert()
{
var src1 = "";
var src2 = "struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Struct_NoModifiers_IntoNamespace_Insert()
{
var src1 = "namespace N { }";
var src2 = "namespace N { struct C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Struct_NoModifiers_IntoType_Insert()
{
var src1 = "struct N { }";
var src2 = "struct N { struct C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Type_BaseType_Insert_Unchanged()
{
var src1 = "class C { }";
var src2 = "class C : object { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [: object]@8");
edits.VerifySemantics();
}
[Fact]
public void Type_BaseType_Insert_Changed()
{
var src1 = "class C { }";
var src2 = "class C : D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [: D]@8");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "D", FeaturesResources.class_));
}
[Fact]
public void Type_BaseType_Insert_WithPrimaryInitializer()
{
var src1 = "class C() { }";
var src2 = "class C() : D() { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [: D()]@10",
"Insert [D()]@12");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "D()", FeaturesResources.class_));
}
[Fact]
public void Type_BaseType_Delete_WithPrimaryInitializer()
{
var src1 = "class C() : D() { }";
var src2 = "class C() { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [: D()]@10",
"Delete [D()]@12");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class C", FeaturesResources.class_));
}
[Theory]
[InlineData("string", "string?")]
[InlineData("string[]", "string[]?")]
[InlineData("object", "dynamic")]
[InlineData("dynamic?", "dynamic")]
[InlineData("(int a, int b)", "(int a, int c)")]
public void Type_BaseType_Update_RuntimeTypeUnchanged(string oldType, string newType)
{
var src1 = "class C : System.Collections.Generic.List<" + oldType + "> {}";
var src2 = "class C : System.Collections.Generic.List<" + newType + "> {}";
var edits = GetTopEdits(src1, src2);
// We don't require a runtime capability to update attributes.
// All runtimes support changing the attributes in metadata, some just don't reflect the changes in the Reflection model.
// Having compiler-generated attributes visible via Reflaction API is not that important.
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")));
}
[Theory]
[InlineData("int", "string")]
[InlineData("int", "int?")]
[InlineData("(int a, int b)", "(int a, double b)")]
public void Type_BaseType_Update_RuntimeTypeChanged(string oldType, string newType)
{
var src1 = "class C : System.Collections.Generic.List<" + oldType + "> {}";
var src2 = "class C : System.Collections.Generic.List<" + newType + "> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "System.Collections.Generic.List<" + newType + ">", FeaturesResources.class_));
}
[Fact]
public void Type_BaseType_Update_CompileTimeTypeUnchanged()
{
var src1 = "using A = System.Int32; using B = System.Int32; class C : System.Collections.Generic.List<A> {}";
var src2 = "using A = System.Int32; using B = System.Int32; class C : System.Collections.Generic.List<B> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Fact]
public void Type_BaseInterface_Add()
{
var src1 = "class C { }";
var src2 = "class C : IDisposable { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [: IDisposable]@8");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "IDisposable", FeaturesResources.class_));
}
[Fact]
public void Type_BaseInterface_Delete_Inherited()
{
var src1 = @"
interface B {}
interface A : B {}
class C : A, B {}
";
var src2 = @"
interface B {}
interface A : B {}
class C : A {}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Fact]
public void Type_BaseInterface_Reorder()
{
var src1 = "class C : IGoo, IBar { }";
var src2 = "class C : IBar, IGoo { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [: IGoo, IBar]@8 -> [: IBar, IGoo]@8");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "IBar, IGoo", FeaturesResources.class_));
}
[Theory]
[InlineData("string", "string?")]
[InlineData("object", "dynamic")]
[InlineData("(int a, int b)", "(int a, int c)")]
public void Type_BaseInterface_Update_RuntimeTypeUnchanged(string oldType, string newType)
{
var src1 = "class C : System.Collections.Generic.IEnumerable<" + oldType + "> {}";
var src2 = "class C : System.Collections.Generic.IEnumerable<" + newType + "> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")));
}
[Theory]
[InlineData("int", "string")]
[InlineData("int", "int?")]
[InlineData("(int a, int b)", "(int a, double b)")]
public void Type_BaseInterface_Update_RuntimeTypeChanged(string oldType, string newType)
{
var src1 = "class C : System.Collections.Generic.IEnumerable<" + oldType + "> {}";
var src2 = "class C : System.Collections.Generic.IEnumerable<" + newType + "> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "System.Collections.Generic.IEnumerable<" + newType + ">", FeaturesResources.class_));
}
[Fact]
public void Type_Base_Partial()
{
var srcA1 = "partial class C : B, I { }";
var srcB1 = "partial class C : J { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C : B, I, J { }";
var srcC = @"
class B {}
interface I {}
interface J {}";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC, srcC)],
[
DocumentResults(),
DocumentResults(),
DocumentResults()
]);
}
[Fact]
public void Type_Base_Partial_InsertDeleteAndUpdate()
{
var srcA1 = "partial class C { }";
var srcB1 = "";
var srcC1 = "partial class C { }";
var srcA2 = "";
var srcB2 = "partial class C : D { }";
var srcC2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "partial class C", FeaturesResources.class_)]),
DocumentResults(),
]);
}
[Fact]
public void Type_Base_InsertDelete()
{
var srcA1 = "";
var srcB1 = "class C : B, I { }";
var srcA2 = "class C : B, I { }";
var srcB2 = "";
var srcC = @"
class B {}
interface I {}
interface J {}";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC, srcC)],
[
DocumentResults(),
DocumentResults(),
DocumentResults()
]);
}
[Fact]
public void Type_Reloadable_NotSupportedByRuntime()
{
var src1 = ReloadableAttributeSrc + @"
[CreateNewOnMetadataUpdate]
public class C
{
void F() { System.Console.WriteLine(1); }
}";
var src2 = ReloadableAttributeSrc + @"
[CreateNewOnMetadataUpdate]
public class C
{
void F() { System.Console.WriteLine(2); }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, "void F()", "CreateNewOnMetadataUpdateAttribute")],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Insert_AbstractVirtualOverride()
{
var src1 = "";
var src2 = @"
public abstract class C<T>
{
public abstract void F();
public virtual void G() {}
public override string ToString() => null;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Insert_NotSupportedByRuntime()
{
var src1 = @"
public class C
{
void F()
{
}
}";
var src2 = @"
public class C
{
void F()
{
}
}
public class D
{
void M()
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "public class D", FeaturesResources.class_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Insert_Reloadable()
{
var src1 = ReloadableAttributeSrc + "";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { void F() {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void InterfaceInsert()
{
var src1 = "";
var src2 = @"
public interface I
{
void F();
static void G() {}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void RefStructInsert()
{
var src1 = "";
var src2 = "ref struct X { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [ref struct X { }]@0");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Struct_ReadOnly_Insert()
{
var src1 = "";
var src2 = "readonly struct X { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [readonly struct X { }]@0");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Struct_RefModifier_Add()
{
var src1 = "struct X { }";
var src2 = "ref struct X { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [struct X { }]@0 -> [ref struct X { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "ref struct X", FeaturesResources.struct_));
}
[Fact]
public void Struct_ReadonlyModifier_Add()
{
var src1 = "struct X { }";
var src2 = "readonly struct X { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [struct X { }]@0 -> [readonly struct X { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "readonly struct X", SyntaxFacts.GetText(SyntaxKind.StructKeyword)));
}
[Theory]
[InlineData("ref")]
[InlineData("readonly")]
public void Struct_Modifiers_Partial_InsertDelete(string modifier)
{
var srcA1 = modifier + " partial struct S { }";
var srcB1 = "partial struct S { }";
var srcA2 = "partial struct S { }";
var srcB2 = modifier + " partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults()
]);
}
[Fact]
public void Class_ImplementingInterface_Add_Implicit_NonVirtual()
{
var src1 = """
interface I
{
void F();
}
""";
var src2 = """
interface I
{
void F();
}
class C : I
{
public void F() {}
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Class_ImplementingInterface_Add_Implicit_Virtual()
{
var src1 = """
interface I
{
void F();
}
""";
var src2 = """
interface I
{
void F();
}
class C : I
{
public virtual void F() {}
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Class_ImplementingInterface_Add_Implicit_Override()
{
var src1 = """
interface I
{
void F();
}
class C : I
{
public virtual void F() {}
}
""";
var src2 = """
interface I
{
void F();
}
class C : I
{
public virtual void F() {}
}
class D : C
{
public override void F() {}
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("D"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition | EditAndContinueCapabilities.AddExplicitInterfaceImplementation);
}
[Theory]
[InlineData("void F();", "void I.F() {}")]
[InlineData("int F { get; }", "int I.F { get; }")]
[InlineData("event System.Action F;", "event System.Action I.F { add {} remove {} }")]
public void Class_ImplementingInterface_Add_Explicit_NonVirtual(string memberDef, string explicitImpl)
{
var src1 = $$"""
interface I
{
{{memberDef}}
}
""";
var src2 = $$"""
interface I
{
{{memberDef}}
}
class C<T> : I
{
{{explicitImpl}}
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition | EditAndContinueCapabilities.AddExplicitInterfaceImplementation);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "class C<T>", GetResource("class"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37128")]
public void Interface_InsertMembers()
{
var src1 = @"
using System;
interface I
{
}
";
var src2 = @"
using System;
interface I
{
static int StaticField = 10;
static void StaticMethod() { }
void VirtualMethod1() { }
virtual void VirtualMethod2() { }
abstract void AbstractMethod();
sealed void NonVirtualMethod() { }
public static int operator +(I a, I b) => 1;
static int StaticProperty1 { get => 1; set { } }
static int StaticProperty2 => 1;
virtual int VirtualProperty1 { get => 1; set { } }
virtual int VirtualProperty2 { get => 1; }
int VirtualProperty3 { get => 1; set { } }
int VirtualProperty4 { get => 1; }
abstract int AbstractProperty1 { get; set; }
abstract int AbstractProperty2 { get; }
sealed int NonVirtualProperty => 1;
int this[byte virtualIndexer] => 1;
int this[sbyte virtualIndexer] { get => 1; }
virtual int this[ushort virtualIndexer] { get => 1; set {} }
virtual int this[short virtualIndexer] { get => 1; set {} }
abstract int this[uint abstractIndexer] { get; set; }
abstract int this[int abstractIndexer] { get; }
sealed int this[ulong nonVirtualIndexer] { get => 1; set {} }
sealed int this[long nonVirtualIndexer] { get => 1; set {} }
static event Action StaticEvent;
static event Action StaticEvent2 { add { } remove { } }
event Action VirtualEvent { add { } remove { } }
abstract event Action AbstractEvent;
sealed event Action NonVirtualEvent { add { } remove { } }
abstract class C { }
interface J { }
enum E { }
delegate void D();
}
";
var edits = GetTopEdits(src1, src2);
// TODO: InsertIntoInterface errors are reported due to https://github.com/dotnet/roslyn/issues/37128.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertIntoInterface, "static void StaticMethod()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertVirtual, "void VirtualMethod1()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertVirtual, "virtual void VirtualMethod2()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertVirtual, "abstract void AbstractMethod()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertIntoInterface, "sealed void NonVirtualMethod()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertOperator, "public static int operator +(I a, I b)", FeaturesResources.operator_),
Diagnostic(RudeEditKind.InsertIntoInterface, "static int StaticProperty1", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertIntoInterface, "static int StaticProperty2", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertVirtual, "virtual int VirtualProperty1", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertVirtual, "virtual int VirtualProperty2", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertVirtual, "int VirtualProperty3", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertVirtual, "int VirtualProperty4", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertVirtual, "abstract int AbstractProperty1", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertVirtual, "abstract int AbstractProperty2", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertIntoInterface, "sealed int NonVirtualProperty", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertVirtual, "int this[byte virtualIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertVirtual, "int this[sbyte virtualIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertVirtual, "virtual int this[ushort virtualIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertVirtual, "virtual int this[short virtualIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertVirtual, "abstract int this[uint abstractIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertVirtual, "abstract int this[int abstractIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertIntoInterface, "sealed int this[ulong nonVirtualIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertIntoInterface, "sealed int this[long nonVirtualIndexer]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertIntoInterface, "static event Action StaticEvent2", FeaturesResources.event_),
Diagnostic(RudeEditKind.InsertVirtual, "event Action VirtualEvent", FeaturesResources.event_),
Diagnostic(RudeEditKind.InsertIntoInterface, "sealed event Action NonVirtualEvent", FeaturesResources.event_),
Diagnostic(RudeEditKind.InsertIntoInterface, "StaticField = 10", FeaturesResources.field),
Diagnostic(RudeEditKind.InsertIntoInterface, "StaticEvent", CSharpFeaturesResources.event_field),
Diagnostic(RudeEditKind.InsertVirtual, "AbstractEvent", CSharpFeaturesResources.event_field));
}
[Fact]
public void Interface_InsertDelete()
{
var srcA1 = @"
interface I
{
static void M() { }
}
";
var srcB1 = @"
";
var srcA2 = @"
";
var srcB2 = @"
interface I
{
static void M() { }
}
";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember("M"))
]),
]);
}
[Fact]
public void Type_Generic_Insert_StatelessMembers()
{
var src1 = @"
using System;
class C<T>
{
int P1 { get => 1; }
int this[string s] { set {} }
}
";
var src2 = @"
using System;
class C<T>
{
C(int x) {}
void M() {}
void G<S>() {}
int P1 { get => 1; set {} }
int P2 { get => 1; set {} }
int this[int i] { set {} get => 1; }
int this[string s] { set {} get => 1; }
event Action E { add {} remove {} }
enum E {}
interface I {}
interface I<S> {}
class D {}
class D<S> {}
delegate void Del();
delegate void Del<S>();
}
";
var edits = GetTopEdits(src1, src2);
var diagnostics = new[]
{
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "C(int x)", FeaturesResources.constructor),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "void M()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "void G<S>()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P2", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int this[int i]", FeaturesResources.indexer_),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "event Action E", FeaturesResources.event_),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "set", CSharpFeaturesResources.property_setter),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "get", CSharpFeaturesResources.indexer_getter),
};
edits.VerifySemanticDiagnostics(diagnostics, capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(diagnostics, capabilities: EditAndContinueCapabilities.GenericAddMethodToExistingType);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType);
}
[Fact]
public void Type_Generic_Insert_DataMembers()
{
var src1 = @"
using System;
class C<T>
{
}
";
var src2 = @"
using System;
class C<T>
{
int P { get; set; }
event Action EF;
int F1, F2;
static int SF;
}
";
var edits = GetTopEdits(src1, src2);
var nonGenericCapabilities =
EditAndContinueCapabilities.AddInstanceFieldToExistingType |
EditAndContinueCapabilities.AddStaticFieldToExistingType |
EditAndContinueCapabilities.AddMethodToExistingType;
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", FeaturesResources.auto_property),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "EF", CSharpFeaturesResources.event_field),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "F1", FeaturesResources.field),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "F2", FeaturesResources.field),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "SF", FeaturesResources.field),
], capabilities: nonGenericCapabilities);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", GetResource("auto-property")),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "F1", FeaturesResources.field),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "F2", FeaturesResources.field),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "SF", FeaturesResources.field),
], capabilities: nonGenericCapabilities | EditAndContinueCapabilities.GenericAddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", FeaturesResources.auto_property),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "EF", CSharpFeaturesResources.event_field)
], capabilities: nonGenericCapabilities | EditAndContinueCapabilities.GenericAddFieldToExistingType);
edits.VerifySemanticDiagnostics(
capabilities: nonGenericCapabilities | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericAddFieldToExistingType);
}
[Fact]
public void Type_Generic_Insert_IntoNestedType()
{
var src1 = @"
class C<T>
{
class D
{
}
}
";
var src2 = @"
class C<T>
{
class D
{
void F() {}
int X;
static int Y;
}
}
";
var edits = GetTopEdits(src1, src2);
var nonGenericCapabilities =
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.AddInstanceFieldToExistingType |
EditAndContinueCapabilities.AddStaticFieldToExistingType;
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "void F()", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "X", FeaturesResources.field),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "Y", FeaturesResources.field)
], capabilities: nonGenericCapabilities);
edits.VerifySemanticDiagnostics(capabilities:
nonGenericCapabilities |
EditAndContinueCapabilities.GenericAddMethodToExistingType |
EditAndContinueCapabilities.GenericAddFieldToExistingType);
}
[Fact]
public void Type_Generic_InsertMembers_Reloadable()
{
var src1 = ReloadableAttributeSrc + @"
interface IExplicit
{
void F() {}
}
[CreateNewOnMetadataUpdate]
class C<T> : IExplicit
{
void IExplicit.F() {}
}
";
var src2 = ReloadableAttributeSrc + @"
interface IExplicit
{
void F() {}
}
[CreateNewOnMetadataUpdate]
class C<T> : IExplicit
{
void IExplicit.F() {}
void M() {}
int P1 { get; set; }
int P2 { get => 1; set {} }
int this[int i] { get => 1; set {} }
event System.Action E { add {} remove {} }
event System.Action EF;
int F1, F2;
enum E {}
interface I {}
class D {}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition | EditAndContinueCapabilities.AddExplicitInterfaceImplementation);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingReloadableTypeNotSupportedByRuntime, "void M()", "CreateNewOnMetadataUpdateAttribute")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Generic_DeleteInsert()
{
var srcA1 = @"
class C<T> { void F() {} }
struct S<T> { void F() {} }
interface I<T> { void F() {} }
";
var srcB1 = "";
var srcA2 = srcB1;
var srcB2 = srcA1;
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I.F"))
])
],
capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "void F()", GetResource("method")),
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "void F()", GetResource("method")),
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "void F()", GetResource("method"))
])
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/54881")]
[WorkItem("https://github.com/dotnet/roslyn/issues/54881")]
public void Type_TypeParameter_Insert_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public class C<T> { void F() { } }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal class C<T, S> { int x = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")));
}
[Fact]
public void Type_Delete()
{
var src1 = @"
class C { void F() {} }
struct S { void F() {} }
interface I { void F() {} }
";
var src2 = "";
GetTopEdits(src1, src2).VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.class_, "C")),
Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.struct_, "S")),
Diagnostic(RudeEditKind.Delete, null, DeletedSymbolDisplay(FeaturesResources.interface_, "I")));
}
[Fact]
public void Type_Delete_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { void F() {} }";
var src2 = ReloadableAttributeSrc;
GetTopEdits(src1, src2).VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "using System.Runtime.CompilerServices;", GetResource("class", "C")));
}
[Fact]
public void Type_Partial_DeleteDeclaration()
{
var srcA1 = "partial class C { void F() {} void M() { } }";
var srcB1 = "partial class C { void G() {} }";
var srcA2 = "";
var srcB2 = "partial class C { void G() {} void M() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C"))
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("M")),
])
]);
}
[Fact]
public void Type_Partial_InsertFirstDeclaration()
{
var src1 = "";
var src2 = "partial class C { void F() {} }";
GetTopEdits(src1, src2).VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C"), preserveLocalVariables: false)],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Partial_InsertSecondDeclaration()
{
var srcA1 = "partial class C { void F() {} }";
var srcB1 = "";
var srcA2 = "partial class C { void F() {} }";
var srcB2 = "partial class C { void G() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").GetMember("G"), preserveLocalVariables: false)
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Type_Partial_Reloadable()
{
var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { void F() {} }";
var srcB1 = "";
var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { void F() {} }";
var srcB2 = "partial class C { void G() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_DeleteInsert()
{
var srcA1 = @"
class C { void F() {} }
struct S { void F() {} }
interface I { void F() {} }
";
var srcB1 = "";
var srcA2 = srcB1;
var srcB2 = srcA1;
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("S").GetMember("F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember("F")),
])
]);
}
[Fact]
public void Type_DeleteInsert_Reloadable()
{
var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { void F() {} }";
var srcB1 = "";
var srcA2 = ReloadableAttributeSrc;
var srcB2 = "[CreateNewOnMetadataUpdate]class C { void F() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")),
])
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_NonInsertableMembers_DeleteInsert()
{
var srcA1 = @"
abstract class C
{
public abstract void AbstractMethod();
public virtual void VirtualMethod() {}
public override string ToString() => null;
public void I.G() {}
}
interface I
{
void G();
void F() {}
}
";
var srcB1 = "";
var srcA2 = srcB1;
var srcB2 = srcA1;
// TODO: The methods without bodies do not need to be updated.
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("AbstractMethod")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("VirtualMethod")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("ToString")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("I.G")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember("G")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember("F")),
])
]);
}
[Fact]
public void Type_Attribute_NonInsertableMembers_DeleteInsert()
{
var srcA1 = @"
abstract class C
{
public abstract void AbstractMethod();
public virtual void VirtualMethod() {}
public override string ToString() => null;
public void I.G() {}
}
interface I
{
void G();
void F() {}
}
";
var srcB1 = "";
var srcA2 = "";
var srcB2 = @"
abstract class C
{
[System.Obsolete]public abstract void AbstractMethod();
public virtual void VirtualMethod() {}
public override string ToString() => null;
public void I.G() {}
}
interface I
{
[System.Obsolete]void G();
void F() {}
}";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("AbstractMethod")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("VirtualMethod")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("ToString")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("I.G")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember("G")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember("F")),
])
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Type_DeleteInsert_DataMembers()
{
var srcA1 = @"
class C
{
public int x = 1;
public int y = 2;
public int P { get; set; } = 3;
public event System.Action E = new System.Action(null);
}
";
var srcB1 = "";
var srcA2 = "";
var srcB2 = @"
class C
{
public int x = 1;
public int y = 2;
public int P { get; set; } = 3;
public event System.Action E = new System.Action(null);
}
";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true),
])
]);
}
[Fact]
public void Type_DeleteInsert_DataMembers_PartialSplit()
{
var srcA1 = @"
class C
{
public int x = 1;
public int y = 2;
public int P { get; set; } = 3;
}
";
var srcB1 = "";
var srcA2 = @"
partial class C
{
public int x = 1;
public int y = 2;
}
";
var srcB2 = @"
partial class C
{
public int P { get; set; } = 3;
}
";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true),
])
]);
}
[Fact]
public void Type_DeleteInsert_DataMembers_PartialMerge()
{
var srcA1 = @"
partial class C
{
public int x = 1;
public int y = 2;
}
";
var srcB1 = @"
partial class C
{
public int P { get; set; } = 3;
}";
var srcA2 = @"
class C
{
public int x = 1;
public int y = 2;
public int P { get; set; } = 3;
}
";
var srcB2 = @"
";
// note that accessors are not updated since they do not have bodies
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true),
]),
DocumentResults()
]);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("interface")]
[InlineData("record")]
[InlineData("record struct")]
public void Type_Move_NamespaceChange(string keyword)
{
var declaration = keyword + " C {}";
var src1 = $"namespace N {{{declaration,-20}}} namespace M {{ }}";
var src2 = $"namespace N {{ }} namespace M {{{declaration}}}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [" + declaration + "]@13 -> @45");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingNamespace, keyword + " C", GetResource(keyword), "N", "M"));
}
[Fact]
public void Type_Move_NamespaceChange_Delegate()
{
var src1 = @"namespace N { delegate void F(); } namespace M { }";
var src2 = @"namespace N { } namespace M { delegate void F(); }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [delegate void F();]@14 -> @49");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingNamespace, "delegate void F()", GetResource("delegate"), "N", "M"));
}
[Fact]
public void Type_Move_NamespaceChange_Subnamespace()
{
var src1 = @"namespace N { class C {} } namespace M { namespace O { } }";
var src2 = @"namespace N { } namespace M { namespace O { class C {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [class C {}]@14 -> @55");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N", "M.O"));
}
[Fact]
public void Type_Move_SameEffectiveNamespace()
{
var src1 = @"namespace N.M { class C {} } namespace N { namespace M { } }";
var src2 = @"namespace N.M { } namespace N { namespace M { class C {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [class C {}]@16 -> @57");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Type_Move_MultiFile()
{
var srcA1 = @"namespace N { class C {} } namespace M { }";
var srcB1 = @"namespace N { } namespace M { class C {} }";
var srcA2 = @"namespace N { } namespace M { class C {} }";
var srcB2 = @"namespace N { class C {} } namespace M { }";
var editsA = GetTopEdits(srcA1, srcA2);
editsA.VerifyEdits(
"Move [class C {}]@14 -> @41");
var editsB = GetTopEdits(srcB1, srcB2);
editsB.VerifyEdits(
"Move [class C {}]@41 -> @14");
EditAndContinueValidation.VerifySemantics(
[editsA, editsB],
[
DocumentResults(),
DocumentResults(),
]);
}
#endregion
#region Records
[Fact]
public void Record_Insert()
{
var src1 = "";
var src2 = "record C;";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [record C;]@0");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Record_Insert_WithParameters()
{
var src1 = "";
var src2 = "record C(int A);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Record_Name_Update()
{
var src1 = "record C { }";
var src2 = "record D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [record C { }]@0 -> [record D { }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "record D", GetResource("record", "C")));
}
[Fact]
public void RecordStruct_NoModifiers_Insert()
{
var src1 = "";
var src2 = "record struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void RecordStruct_AddField()
{
var src1 = @"
record struct C(int X)
{
}";
var src2 = @"
record struct C(int X)
{
private int _y = 0;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "_y = 0", FeaturesResources.field, CSharpFeaturesResources.record_struct));
}
[Fact]
public void RecordStruct_AddProperty()
{
var src1 = @"
record struct C(int X)
{
}";
var src2 = @"
record struct C(int X)
{
public int Y { get; set; } = 0;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "public int Y", GetResource("auto-property"), GetResource("record struct")));
}
[Fact]
public void Record_NoModifiers_Insert()
{
var src1 = "";
var src2 = "record C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Record_NoModifiers_IntoNamespace_Insert()
{
var src1 = "namespace N { }";
var src2 = "namespace N { record C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Record_NoModifiers_IntoType_Insert()
{
var src1 = "struct N { }";
var src2 = "struct N { record C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Record_BaseType_Update1()
{
var src1 = "record C { }";
var src2 = "record C : D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [: D]@9");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "D", CSharpFeaturesResources.record_));
}
[Fact]
public void Record_BaseType_Update2()
{
var src1 = "record C : D1 { }";
var src2 = "record C : D2 { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [: D1]@9 -> [: D2]@9");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "D2", CSharpFeaturesResources.record_));
}
[Fact]
public void Record_BaseInterface_Update1()
{
var src1 = "record C { }";
var src2 = "record C : IDisposable { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [: IDisposable]@9");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "IDisposable", CSharpFeaturesResources.record_));
}
[Fact]
public void Record_BaseInterface_Update2()
{
var src1 = "record C : IGoo, IBar { }";
var src2 = "record C : IGoo { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [: IGoo, IBar]@9 -> [: IGoo]@9");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "IGoo", CSharpFeaturesResources.record_));
}
[Fact]
public void Record_BaseInterface_Update3()
{
var src1 = "record C : IGoo, IBar { }";
var src2 = "record C : IBar, IGoo { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [: IGoo, IBar]@9 -> [: IBar, IGoo]@9");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "IBar, IGoo", CSharpFeaturesResources.record_));
}
[Fact]
public void Record_Method_Insert_AbstractVirtualOverride()
{
var src1 = "";
var src2 = @"
public abstract record C<T>
{
public abstract void F();
public virtual void G() {}
public override void H() {}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Theory]
[InlineData("Equals", "public virtual bool Equals(C rhs) => true;", "C rhs")]
[InlineData("PrintMembers", "protected virtual bool PrintMembers(System.Text.StringBuilder sb) => true;", "System.Text.StringBuilder sb")]
[InlineData("Deconstruct", "public void Deconstruct(out int Y) { Y = 1; }", "out int Y")]
[InlineData(".ctor", "protected C(C other) {}", "C other")]
public void Record_Method_Insert_ReplacingSynthesizedWithCustom_ParameterNameChanges(string methodName, string methodImpl, string parameterDecl)
{
var src1 = "record C(int X) { }";
var src2 = "record C(int X) { " + methodImpl + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, parameterDecl, FeaturesResources.parameter),
],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => methodName switch { ".ctor" => c.GetCopyConstructor("C"), "Equals" => c.GetSpecializedEqualsOverload("C"), _ => c.GetMember("C." + methodName) }),
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
}
[Fact]
public void Record_Method_Insert_ReplacingSynthesizedWithCustom_SemanticError()
{
var src1 = "record C { }";
var src2 = @"record C
{
protected virtual bool PrintMembers(System.Text.StringBuilder sb) => false;
protected virtual bool PrintMembers(System.Text.StringBuilder sb) => false;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "System.Text.StringBuilder sb", GetResource("parameter")),
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "System.Text.StringBuilder sb", GetResource("parameter"))
],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers("C.PrintMembers").First().ISymbol),
],
capabilities: EditAndContinueCapabilities.UpdateParameters | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("ToString", "public override string ToString() => null;")]
[InlineData("GetHashCode", "public override int GetHashCode() => 1;")]
[InlineData("Equals", "public virtual bool Equals(C other) => true;")]
[InlineData("PrintMembers", "protected virtual bool PrintMembers(System.Text.StringBuilder builder) => true;")]
[InlineData("Deconstruct", "public void Deconstruct(out int X) { X = 1; }")]
[InlineData(".ctor", "protected C(C original) {}")]
public void Record_Method_Insert_ReplacingSynthesizedWithCustom(string methodName, string methodImpl)
{
var src1 = "record C(int X) { }";
var src2 = "record C(int X) { " + methodImpl + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => methodName switch { ".ctor" => c.GetCopyConstructor("C"), "Equals" => c.GetSpecializedEqualsOverload("C"), _ => c.GetMember("C." + methodName) }));
}
[Theory]
[InlineData("ToString", "public override string ToString() => null;")]
[InlineData("GetHashCode", "public override int GetHashCode() => 1;")]
[InlineData("Equals", "public virtual bool Equals(C other) => true;")]
[InlineData("PrintMembers", "protected virtual bool PrintMembers(System.Text.StringBuilder builder) => true;")]
[InlineData("Deconstruct", "public void Deconstruct(out int X) { X = 1; }")]
[InlineData(".ctor", "protected C(C original) {}")]
public void Record_Method_Delete_ReplacingCustomWithSynthesized(string methodName, string methodImpl)
{
var src1 = "record C(int X) { " + methodImpl + " }";
var src2 = "record C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => methodName switch { ".ctor" => c.GetCopyConstructor("C"), "Equals" => c.GetSpecializedEqualsOverload("C"), _ => c.GetMember("C." + methodName) }));
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("Equals", "public virtual bool Equals(C rhs) => true;", "Equals(C rhs)", "rhs")]
[InlineData("PrintMembers", "protected virtual bool PrintMembers(System.Text.StringBuilder sb) => true;", "PrintMembers(StringBuilder sb)", "sb")]
[InlineData("Deconstruct", "public void Deconstruct(out int Y) { Y = 1; }", "Deconstruct(out int Y)", "Y")]
[InlineData(".ctor", "protected C(C other) {}", "C(C other)", "other")]
public void Record_Method_Delete_ReplacingSynthesizedWithCustom_ParameterNameChanges(string methodName, string methodImpl, string methodDisplay, string parameterDisplay)
{
var src1 = "record C(int X) { " + methodImpl + " }";
var src2 = "record C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(
RudeEditKind.RenamingNotSupportedByRuntime,
"record C",
GetResource("parameter", parameterDisplay, methodName switch { ".ctor" => "constructor", _ => "method" }, methodDisplay)),
],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => methodName switch { ".ctor" => c.GetCopyConstructor("C"), "Equals" => c.GetSpecializedEqualsOverload("C"), _ => c.GetMember("C." + methodName) }),
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
}
[Theory]
[InlineData("method", "ToString()", "public override string ToString() => G(stackalloc int[1]).ToString();")]
[InlineData("method", "GetHashCode()", "public override int GetHashCode() => G(stackalloc int[1]) ? 0 : 1;")]
[InlineData("method", "Equals(C other)", "public virtual bool Equals(C other) => G(stackalloc int[1]);")]
[InlineData("method", "PrintMembers(StringBuilder builder)", "protected virtual bool PrintMembers(System.Text.StringBuilder builder) => G(stackalloc int[1]);")]
[InlineData("method", "Deconstruct(out int X)", "public void Deconstruct(out int X) => G(stackalloc int[X = 1]);")]
[InlineData("constructor", "C(C original)", "protected C(C original) => G(stackalloc int[1]);")]
[WorkItem("https://github.com/dotnet/roslyn/issues/69493")]
public void Record_Method_Delete_ReplacingCustomWithSynthesized_StackAlloc(string kind, string methodDisplay, string methodImpl)
{
var src1 = "record C(int X) { bool G(Span<int> s) => true; " + methodImpl + " }";
var src2 = "record C(int X) { bool G(Span<int> s) => true; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "record C", GetResource(kind, methodDisplay)));
}
[Fact]
public void Record_Field_Insert()
{
var src1 = "record C(int X) { }";
var src2 = "record C(int X) { private int _y; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C._y")),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Record_Field_Insert_WithExplicitMembers()
{
var src1 = @"
record C(int X)
{
public C(C other)
{
}
}";
var src2 = @"
record C(int X)
{
private int _y;
public C(C other)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C._y")),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Record_Field_Insert_WithInitializer()
{
var src1 = "record C(int X) { }";
var src2 = "record C(int X) { private int _y = 1; }";
var syntaxMap = GetSyntaxMap(src1, src2);
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C._y")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Record_EventField_Insert_WithInitializer()
{
var src1 = "record C(int X) { }";
var src2 = "record C(int X) { event System.Action E = null; }";
var syntaxMap = GetSyntaxMap(src1, src2);
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.E")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Record_Field_Insert_WithExistingInitializer()
{
var src1 = "record C(int X) { private int _y = <N:0.0>1</N:0.0>; }";
var src2 = "record C(int X) { private int _y = <N:0.0>1</N:0.0>; private int _z; }";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C._z")),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Record_Field_Insert_WithInitializerAndExistingInitializer()
{
var src1 = "record C(int X) { private int _y = <N:0.0>1</N:0.0>; }";
var src2 = "record C(int X) { private int _y = <N:0.0>1</N:0.0>; private int _z = 1; }";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C._z")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), syntaxMap[0]),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Record_Field_Delete()
{
var src1 = "record C(int X) { private int _y; }";
var src2 = "record C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(Diagnostic(RudeEditKind.Delete, "record C", DeletedSymbolDisplay(FeaturesResources.field, "_y")));
}
[Fact]
public void Record_Property_Update_Initializer_NotPrimary()
{
var src1 = "record C { int X { get; } = 0; }";
var src2 = "record C { int X { get; } = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(c => c.Parameters.Length == 0), preserveLocalVariables: true));
}
[Fact]
public void Record_Property_Update_Initializer_Primary()
{
var src1 = "record C(int X) { int X { get; } = 0; }";
var src2 = "record C(int X) { int X { get; } = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(c => c.Parameters[0].Type.ToDisplayString() == "int"), preserveLocalVariables: true));
}
[Theory]
[InlineData("set")]
[InlineData("init")]
public void Record_Property_Delete_Writable(string setter)
{
var src1 = "record C(int X) { public int P { get; " + setter + "; } }";
var src2 = "record C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory]
[InlineData("get;")]
[InlineData("get => 1;")]
public void Record_Property_Delete_ReadOnly(string getter)
{
var src1 = "record C(int X) { public int P { " + getter + " } }";
var src2 = "record C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Delete_ReadOnly_ReplacingCustomWithSynthesized()
{
var src1 = "record C(int X) { public int X { get => 1; } }";
var src2 = "record C(int X);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "record C", GetResource("property getter", "X.get"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Delete_ReadOnly_ReplacingCustomWithSynthesized_Generic()
{
var src1 = "record C<T>(int X) { public int X { get => 1; } }";
var src2 = "record C<T>(int X);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities:
EditAndContinueCapabilities.AddInstanceFieldToExistingType |
EditAndContinueCapabilities.GenericAddFieldToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "int X", GetResource("property")),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "record C<T>", GetResource("property getter", "X.get"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Delete_ReadOnly_ReplacingCustomWithSynthesized_AutoProp()
{
var src1 = "record C(int X) { public int X { get; } }";
var src2 = "record C(int X);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Record_Property_Delete_WriteOnly()
{
var src1 = "record C(int X) { public int P { set { } } }";
var src2 = "record C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Delete_Static()
{
var src1 = "record C(int X) { public static int P { get; set; } }";
var src2 = "record C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Delete_WithInitializer()
{
var src1 = @"
record C(int X)
{
public int Y { get; set; } = 1;
public C(bool b) : this(1) { }
}";
var src2 = @"
record C(int X)
{
public C(bool b) : this(1) { }
}";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("")]
[InlineData(" = 1;")]
[InlineData(" = X;")]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_Auto(string initializer)
{
var src1 = "record C(int X) { public int X { get; init; }" + initializer + " }";
var src2 = "record C(int X);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_Struct()
{
var src1 = "record struct C(int X) { public int X { get; init; } }";
var src2 = "record struct C(int X);";
var edits = GetTopEdits(src1, src2);
// synthesized setter is writable in non-readonly struct
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.AccessorKindUpdate, "record struct C"));
}
[Fact]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_ReadOnlyStruct()
{
var src1 = "readonly record struct C(int X) { public int X { get; init; } }";
var src2 = "readonly record struct C(int X);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Theory]
[InlineData("")]
[InlineData(" = 1;")]
[InlineData(" = X;")]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_Auto_Partial(string initializer)
{
var srcA1 = "partial record C(int X);";
var srcB1 = "partial record C { public int X { get; init; }" + initializer + " }";
var srcA2 = "partial record C(int X);";
var srcB2 = "partial record C;";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), partialType: "C", preserveLocalVariables: true)
])
]);
}
[Theory]
[InlineData("get => 4; init => throw null;")]
[InlineData("get { return 4; } init { }")]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_WithBody(string body)
{
var src1 = "record C(int X) { public int X { " + body + " } }";
var src2 = "record C(int X);";
var edits = GetTopEdits(src1, src2);
// The property changes from custom property to field-backed auto-prop.
// Methods using backing field must be updated, unless they are explicitly declared.
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.X").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.X").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Theory]
[InlineData("PrintMembers", "protected virtual bool PrintMembers(System.Text.StringBuilder builder) => true;")]
[InlineData("GetHashCode", "public override int GetHashCode() => 1;")]
[InlineData(".ctor", "protected C(C original) {}")]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_WithBodyAndMethod(string methodName, string methodImpl)
{
var src1 = "record C(int X) { " + methodImpl + " public int X { get => 4; init => throw null; } }";
var src2 = "record C(int X) { " + methodImpl + " }";
var edits = GetTopEdits(src1, src2);
// The property changes from custom property to field-backed auto-prop.
// Methods using backing field must be updated, unless they are explicitly declared.
var expectedEdits = new List<SemanticEditDescription>();
if (methodName != "PrintMembers")
{
expectedEdits.Add(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")));
}
expectedEdits.Add(SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")));
if (methodName != "GetHashCode")
{
expectedEdits.Add(SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")));
}
if (methodName != ".ctor")
{
expectedEdits.Add(SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")));
}
expectedEdits.Add(SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.X").GetMethod));
expectedEdits.Add(SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.X").SetMethod));
expectedEdits.Add(SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
edits.VerifySemantics(
[.. expectedEdits],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Theory]
[InlineData("get => 4; init => throw null;")]
[InlineData("get { return 4; } init { }")]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_WithBody_Partial(string body)
{
var srcA1 = "partial record C(int X);";
var srcB1 = "partial record C { public int X { " + body + " } }";
var srcA2 = "partial record C(int X);";
var srcB2 = "partial record C;";
// The property changes from custom property to field-backed auto-prop.
// Methods using backing field must be updated, unless they are explicitly declared.
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.X").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.X").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), partialType : "C", preserveLocalVariables: true),
])
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_WithAttribute()
{
var src1 = "record C([property: System.Obsolete]int P) { public int P { get; init; } = P; }";
var src2 = "record C([property: System.Obsolete]int P) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int P", GetResource("auto-property"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_WithAttributeOnAccessor()
{
var src1 = "record C(int P) { public int P { get; [System.Obsolete] init; } = P; }";
var src2 = "record C(int P) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "record C", GetResource("property setter", "P.init"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Delete_ReplacingCustomWithSynthesized_TypeLayoutChange()
{
var src1 = "record struct C(int P) { public int P { readonly get => 1; set {} } }";
var src2 = "record struct C(int P);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertOrMoveStructMember, "int P", GetResource("auto-property"), GetResource("record struct"))],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("get; set;")]
[InlineData("get; init;")]
[InlineData("get {} set {}")]
[InlineData("get;")]
public void Record_Property_Insert(string accessors)
{
var src1 = "record C(int X);";
var src2 = "record C(int X) { public int Y { " + accessors + " } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Record_Property_Insert_WriteOnly()
{
var src1 = "record C(int X);";
var src2 = "record C(int X) { public int Y { set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Record_Property_Insert_Static()
{
var src1 = "record C(int X);";
var src2 = "record C(int X) { public static int Y { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y"))
],
capabilities: EditAndContinueCapabilities.AddStaticFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Record_Property_Insert_WithInitializer()
{
var src1 = @"
record C(int X)
{
public C(bool b) : this(1) { }
}";
var src2 = @"
record C(int X)
{
public int Y { get; set; } = 1;
public C(bool b) : this(1) { }
}";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("record")]
[InlineData("readonly record struct")]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_WithSetter(string keyword)
{
var src1 = keyword + " C(int P);";
var src2 = keyword + " C(int P) { public int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.AccessorKindUpdate, "set"));
}
[Fact]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_WithSetter_WritableStruct()
{
var src1 = "record struct C(int P);";
var src2 = "record struct C(int P) { public int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
]);
}
[Fact]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_ReadOnly()
{
var src1 = "record C(int X);";
var src2 = "record C(int X) { public int X { get; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_X"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
]);
}
[Fact]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_TypeLayoutChange()
{
var src1 = "record struct C(int P);";
var src2 = "record struct C(int P) { public int P { readonly get => 1; set {} } }";
var edits = GetTopEdits(src1, src2);
// Note: We do not report rude edits when a synthesized auto-property is replaced by an explicit one.
// The synthesized property accessors are updated to throw and the backing field remains in the type.
// The deleted field will remain unused since adding the primary property back is a rude edit.
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
]);
}
[Fact]
public void Record_Property_Insert_NotPrimary_WithExplicitMembers()
{
var src1 = @"
record C(int X)
{
protected virtual bool PrintMembers(System.Text.StringBuilder builder)
{
return false;
}
public override int GetHashCode()
{
return 0;
}
public virtual bool Equals(C other)
{
return false;
}
public C(C original)
{
}
}";
var src2 = @"
record C(int X)
{
public int Y { get; set; }
protected virtual bool PrintMembers(System.Text.StringBuilder builder)
{
return false;
}
public override int GetHashCode()
{
return 0;
}
public virtual bool Equals(C other)
{
return false;
}
public C(C original)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("")]
[InlineData(" = 1;")]
[InlineData(" = X;")]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_Auto(string initializer)
{
var src1 = "record C(int X);";
var src2 = "record C(int X) { public int X { get; init; } " + initializer + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("")]
[InlineData(" = 1;")]
[InlineData(" = X;")]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_Auto_Partial(string initializer)
{
var srcA1 = "partial record C(int X);";
var srcB1 = "partial record C;";
var srcA2 = "partial record C(int X);";
var srcB2 = "partial record C { public int X { get; init; }" + initializer + " }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), partialType: "C", preserveLocalVariables: true)
])
]);
}
[Theory]
[InlineData("get => 4; init => throw null;")]
[InlineData("get { return 4; } init { }")]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_WithBody(string body)
{
var src1 = "record C(int X);";
var src2 = "record C(int X) { public int X { " + body + " } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("get => 4; init => throw null;")]
[InlineData("get { return 4; } init { }")]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_WithBody_Partial(string body)
{
var srcA1 = "partial record C(int X);";
var srcB1 = "partial record C;";
var srcA2 = "partial record C(int X);";
var srcB2 = "partial record C { public int X { " + body + " } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_X")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), partialType: "C", preserveLocalVariables: true)
])
]);
}
[Fact]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_WithAttribute()
{
var src1 = "record C([property: System.Obsolete]int P) { }";
var src2 = "record C([property: System.Obsolete]int P) { public int P { get; init; } = P; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public int P", GetResource("auto-property"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_Insert_ReplacingSynthesizedWithCustom_WithAttributeOnAccessor()
{
var src1 = "record C(int P) { }";
var src2 = "record C(int P) { public int P { get; [System.Obsolete] init; } = P; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "init", GetResource("property setter"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Record_Property_DeleteInsert()
{
var srcA1 = "partial record C(int X) { public int Y { get; init; } }";
var srcB1 = "partial record C;";
var srcA2 = "partial record C(int X);";
var srcB2 = "partial record C { public int Y { get; init; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.Y").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.Y").SetMethod)
]),
]);
}
#endregion
#region Enums
[Fact]
public void Enum_NoModifiers_Insert()
{
var src1 = "";
var src2 = "enum C { A }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Enum_NoModifiers_IntoNamespace_Insert()
{
var src1 = "namespace N { }";
var src2 = "namespace N { enum C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Enum_NoModifiers_IntoType_Insert()
{
var src1 = "struct N { }";
var src2 = "struct N { enum C { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Enum_Attribute_Insert()
{
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(
"Update [enum E { }]@48 -> [[A]enum E { }]@48");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "enum E", FeaturesResources.enum_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Enum_Member_Attribute_Delete()
{
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(
"Update [[A]X]@57 -> [X]@57");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "X", FeaturesResources.enum_value)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Enum_Member_Attribute_Insert()
{
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(
"Update [X]@57 -> [[A]X]@57");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "[A]X", FeaturesResources.enum_value)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Enum_Member_Attribute_Update()
{
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]X]@107 -> [[A2]X]@107");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "[A2]X", FeaturesResources.enum_value)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Enum_Member_Attribute_InsertDeleteAndUpdate()
{
var srcA1 = "";
var srcB1 = "enum N { A = 1 }";
var srcA2 = "enum N { [System.Obsolete]A = 1 }";
var srcB2 = "";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("N.A"))
]),
DocumentResults()
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Enum_Rename()
{
var src1 = "enum Color { Red = 1, Blue = 2, }";
var src2 = "enum Colors { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [enum Color { Red = 1, Blue = 2, }]@0 -> [enum Colors { Red = 1, Blue = 2, }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "enum Colors", GetResource("enum", "Color")));
}
[Fact]
public void Enum_BaseType_Add()
{
var src1 = "enum Color { Red = 1, Blue = 2, }";
var src2 = "enum Color : ushort { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [: ushort]@11");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "ushort", FeaturesResources.enum_));
}
[Fact]
public void Enum_BaseType_Add_Unchanged()
{
var src1 = "enum Color { Red = 1, Blue = 2, }";
var src2 = "enum Color : int { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [: int]@11");
edits.VerifySemantics();
}
[Fact]
public void Enum_BaseType_Update()
{
var src1 = "enum Color : ushort { Red = 1, Blue = 2, }";
var src2 = "enum Color : long { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [: ushort]@11 -> [: long]@11");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "long", FeaturesResources.enum_));
}
[Fact]
public void Enum_BaseType_Delete_Unchanged()
{
var src1 = "enum Color : int { Red = 1, Blue = 2, }";
var src2 = "enum Color { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [: int]@11");
edits.VerifySemantics();
}
[Fact]
public void Enum_BaseType_Delete_Changed()
{
var src1 = "enum Color : ushort { Red = 1, Blue = 2, }";
var src2 = "enum Color { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [: ushort]@11");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "enum Color", FeaturesResources.enum_));
}
[Fact]
public void EnumAccessibilityChange()
{
var src1 = "public enum Color { Red = 1, Blue = 2, }";
var src2 = "enum Color { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public enum Color { Red = 1, Blue = 2, }]@0 -> [enum Color { Red = 1, Blue = 2, }]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingAccessibility, "enum Color", FeaturesResources.enum_));
}
[Fact]
public void EnumAccessibilityNoChange()
{
var src1 = "internal enum Color { Red = 1, Blue = 2, }";
var src2 = "enum Color { Red = 1, Blue = 2, }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Fact]
public void EnumInitializerUpdate()
{
var src1 = "enum Color { Red = 1, Blue = 2, }";
var src2 = "enum Color { Red = 1, Blue = 3, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [Blue = 2]@22 -> [Blue = 3]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, "Blue = 3", FeaturesResources.enum_value));
}
[Fact]
public void EnumInitializerUpdate2()
{
var src1 = "enum Color { Red = 1, Blue = 2, }";
var src2 = "enum Color { Red = 1 << 0, Blue = 2 << 1, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [Red = 1]@13 -> [Red = 1 << 0]@13",
"Update [Blue = 2]@22 -> [Blue = 2 << 1]@27");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, "Blue = 2 << 1", FeaturesResources.enum_value));
}
[Fact]
public void EnumInitializerUpdate3()
{
var src1 = "enum Color { Red = int.MinValue }";
var src2 = "enum Color { Red = int.MaxValue }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [Red = int.MinValue]@13 -> [Red = int.MaxValue]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, "Red = int.MaxValue", FeaturesResources.enum_value));
}
[Fact]
public void EnumInitializerUpdate_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]enum Color { Red = 1 }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]enum Color { Red = 2 }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [Red = 1]@185 -> [Red = 2]@185");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("Color"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void EnumInitializerAdd()
{
var src1 = "enum Color { Red, }";
var src2 = "enum Color { Red = 1, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [Red]@13 -> [Red = 1]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, "Red = 1", FeaturesResources.enum_value));
}
[Fact]
public void EnumInitializerDelete()
{
var src1 = "enum Color { Red = 1, }";
var src2 = "enum Color { Red, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [Red = 1]@13 -> [Red]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, "Red", FeaturesResources.enum_value));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916")]
public void EnumMemberAdd()
{
var src1 = "enum Color { Red }";
var src2 = "enum Color { Red, Blue}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [enum Color { Red }]@0 -> [enum Color { Red, Blue}]@0",
"Insert [Blue]@18");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "Blue", FeaturesResources.enum_value));
}
[Fact]
public void EnumMemberAdd2()
{
var src1 = "enum Color { Red, }";
var src2 = "enum Color { Red, Blue}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [Blue]@18");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "Blue", FeaturesResources.enum_value));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916")]
public void EnumMemberAdd3()
{
var src1 = "enum Color { Red, }";
var src2 = "enum Color { Red, Blue,}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [enum Color { Red, }]@0 -> [enum Color { Red, Blue,}]@0",
"Insert [Blue]@18");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "Blue", FeaturesResources.enum_value));
}
[Fact]
public void EnumMemberUpdate()
{
var src1 = "enum Color { Red }";
var src2 = "enum Color { Orange }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [Red]@13 -> [Orange]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "Orange", GetResource("enum value", "Red")));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916")]
public void EnumMemberDelete()
{
var src1 = "enum Color { Red, Blue}";
var src2 = "enum Color { Red }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [enum Color { Red, Blue}]@0 -> [enum Color { Red }]@0",
"Delete [Blue]@18");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "enum Color", DeletedSymbolDisplay(FeaturesResources.enum_value, "Blue")));
}
[Fact]
public void EnumMemberDelete2()
{
var src1 = "enum Color { Red, Blue}";
var src2 = "enum Color { Red, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [Blue]@18");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "enum Color", DeletedSymbolDisplay(FeaturesResources.enum_value, "Blue")));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/793197")]
public void EnumTrailingCommaAdd()
{
var src1 = "enum Color { Red }";
var src2 = "enum Color { Red, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [enum Color { Red }]@0 -> [enum Color { Red, }]@0");
edits.VerifySemantics(ActiveStatementsDescription.Empty, NoSemanticEdits);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/793197")]
public void EnumTrailingCommaAdd_WithInitializer()
{
var src1 = "enum Color { Red = 1 }";
var src2 = "enum Color { Red = 1, }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [enum Color { Red = 1 }]@0 -> [enum Color { Red = 1, }]@0");
edits.VerifySemantics(ActiveStatementsDescription.Empty, NoSemanticEdits);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/793197")]
public void EnumTrailingCommaDelete()
{
var src1 = "enum Color { Red, }";
var src2 = "enum Color { Red }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [enum Color { Red, }]@0 -> [enum Color { Red }]@0");
edits.VerifySemantics(ActiveStatementsDescription.Empty, NoSemanticEdits);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754916"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/793197")]
public void EnumTrailingCommaDelete_WithInitializer()
{
var src1 = "enum Color { Red = 1, }";
var src2 = "enum Color { Red = 1 }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [enum Color { Red = 1, }]@0 -> [enum Color { Red = 1 }]@0");
edits.VerifySemantics(ActiveStatementsDescription.Empty, NoSemanticEdits);
}
#endregion
#region Delegates
[Fact]
public void Delegate_NoModifiers_Insert()
{
var src1 = "";
var src2 = "delegate void D();";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_NoModifiers_IntoNamespace_Insert()
{
var src1 = "namespace N { }";
var src2 = "namespace N { delegate void D(); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_NoModifiers_IntoType_Insert()
{
var src1 = "class C { }";
var src2 = "class C { delegate void D(); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_Public_IntoType_Insert()
{
var src1 = "class C { }";
var src2 = "class C { public delegate void D(); }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [public delegate void D();]@10",
"Insert [()]@32");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_Generic_Insert()
{
var src1 = "class C { }";
var src2 = "class C { private delegate void D<T>(T a); }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [private delegate void D<T>(T a);]@10",
"Insert [<T>]@33",
"Insert [(T a)]@36",
"Insert [T]@34",
"Insert [T a]@37");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_Delete()
{
var src1 = "class C { private delegate void D(); }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [private delegate void D();]@10",
"Delete [()]@33");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.delegate_, "D")));
}
[Fact]
public void Delegate_Rename()
{
var src1 = "public delegate void D();";
var src2 = "public delegate void Z();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public delegate void D();]@0 -> [public delegate void Z();]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "public delegate void Z()", GetResource("delegate", "D")));
}
[Fact]
public void Delegate_Accessibility_Update()
{
var src1 = "public delegate void D();";
var src2 = "private delegate void D();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public delegate void D();]@0 -> [private delegate void D();]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingAccessibility, "private delegate void D()", FeaturesResources.delegate_));
}
[Fact]
public void Delegate_ReturnType_Update_RuntimeTypeChanged()
{
var src1 = "public delegate int D();";
var src2 = "public delegate void D();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public delegate int D();]@0 -> [public delegate void D();]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "public delegate void D()", FeaturesResources.delegate_));
}
[Fact]
public void Delegate_ReturnType_Update_RuntimeTypeUnchanged()
{
var src1 = "public delegate object D();";
var src2 = "public delegate dynamic D();";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.EndInvoke"))
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Delegate_ReturnType_AddAttribute()
{
var attribute = "public class A : 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);]@39 -> [[return: A]public delegate int D(int a);]@39");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.EndInvoke"))
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Delegate_Parameter_Insert()
{
var src1 = "public delegate int D();";
var src2 = "public delegate int D(int a);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [int a]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "int a", GetResource("delegate")));
}
[Fact]
public void Delegate_Parameter_Insert_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public delegate int D();";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal delegate bool D(int a);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("D"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_Parameter_Delete()
{
var src1 = "public delegate int D(int a);";
var src2 = "public delegate int D();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [int a]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "public delegate int D()", GetResource("delegate")));
}
[Fact]
public void Delegate_Parameter_Rename()
{
var src1 = "public delegate int D(int a);";
var src2 = "public delegate int D(int b);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a]@22 -> [int b]@22");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", GetResource("parameter"))],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.BeginInvoke"))
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
}
[Fact]
public void Delegate_Parameter_Update_Type_RuntimeTypeChanged()
{
var src1 = "public delegate int D(int a);";
var src2 = "public delegate int D(byte a);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a]@22 -> [byte a]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "byte a", GetResource("delegate")));
}
[Fact]
public void Delegate_Parameter_Update_Type_RuntimeTypeUnchanged()
{
var src1 = "public delegate int D(object a);";
var src2 = "public delegate int D(dynamic a);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.BeginInvoke")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")));
}
[Fact]
public void Delegate_Parameter_Update_Attribute()
{
var attribute = "public class A : 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]@61 -> [[A]int a]@61");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.BeginInvoke"))
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", GetResource("parameter"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Delegate_TypeParameter_Insert()
{
var src1 = "public delegate int D();";
var src2 = "public delegate int D<T>();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [<T>]@21",
"Insert [T]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "T", FeaturesResources.type_parameter));
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/54881")]
[WorkItem("https://github.com/dotnet/roslyn/issues/54881")]
public void Delegate_TypeParameter_Insert_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]public delegate int D<out T>();";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]internal delegate bool D<in T, out S>(int a);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("D")));
}
[Fact]
public void Delegate_TypeParameter_Delete()
{
var src1 = "public delegate int D<T>();";
var src2 = "public delegate int D();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [<T>]@21",
"Delete [T]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "public delegate int D()", DeletedSymbolDisplay(FeaturesResources.type_parameter, "T")));
}
[Fact]
public void Delegate_TypeParameter_Rename()
{
var src1 = "public delegate int D<T>();";
var src2 = "public delegate int D<S>();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [T]@22 -> [S]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "S", GetResource("type parameter", "T")));
}
[Fact]
public void Delegate_TypeParameter_Variance1()
{
var src1 = "public delegate int D<T>();";
var src2 = "public delegate int D<in T>();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [T]@22 -> [in T]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.VarianceUpdate, "T", FeaturesResources.type_parameter));
}
[Fact]
public void Delegate_TypeParameter_Variance2()
{
var src1 = "public delegate int D<out T>();";
var src2 = "public delegate int D<T>();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [out T]@22 -> [T]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.VarianceUpdate, "T", FeaturesResources.type_parameter));
}
[Fact]
public void Delegate_TypeParameter_Variance3()
{
var src1 = "public delegate int D<out T>();";
var src2 = "public delegate int D<in T>();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [out T]@22 -> [in T]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.VarianceUpdate, "T", FeaturesResources.type_parameter));
}
[Fact]
public void Delegate_TypeParameter_AddAttribute()
{
var attribute = "public class AAttribute : System.Attribute { }\n\n";
var src1 = attribute + "public delegate int D<T>();";
var src2 = attribute + "public delegate int D<[A]T>();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [T]@70 -> [[A]T]@70");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.GenericTypeUpdate, "T")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Delegate_Attribute_Add_NotSupportedByRuntime()
{
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.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public delegate int D(int a)", FeaturesResources.delegate_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Delegate_Attribute_Add()
{
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(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Delegate_Attribute_Add_WithReturnTypeAttribute()
{
var attribute = "public class A : System.Attribute { }\n\n";
var src1 = attribute + "public delegate int D(int a);";
var src2 = attribute + "[return: A][A]public delegate int D(int a);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public delegate int D(int a);]@39 -> [[return: A][A]public delegate int D(int a);]@39");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.Invoke")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D.EndInvoke")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("D")),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Delegate_ReadOnlyRef_Parameter_InsertWhole()
{
var src1 = "";
var src2 = "public delegate int D(in int b);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [public delegate int D(in int b);]@0",
"Insert [(in int b)]@21",
"Insert [in int b]@22");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_ReadOnlyRef_Parameter_InsertParameter()
{
var src1 = "public delegate int D();";
var src2 = "public delegate int D(in int b);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [in int b]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "in int b", GetResource("delegate")));
}
[Fact]
public void Delegate_ReadOnlyRef_Parameter_Update()
{
var src1 = "public delegate int D(int b);";
var src2 = "public delegate int D(in int b);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int b]@22 -> [in int b]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "in int b", GetResource("delegate")));
}
[Fact]
public void Delegate_ReadOnlyRef_ReturnType_Insert()
{
var src1 = "";
var src2 = "public delegate ref readonly int D();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [public delegate ref readonly int D();]@0",
"Insert [()]@34");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Delegate_ReadOnlyRef_ReturnType_Update()
{
var src1 = "public delegate int D();";
var src2 = "public delegate ref readonly int D();";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public delegate int D();]@0 -> [public delegate ref readonly int D();]@0");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "public delegate ref readonly int D()", FeaturesResources.delegate_));
}
#endregion
#region Nested Types
[Fact]
public void NestedType_Replace_WithUpdateInNestedType_Partial_DifferentDocument()
{
var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int N() => 1; }";
var srcB1 = "partial class C { class D { int M() => 1; } }";
var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int N() => 2; }";
var srcB2 = "partial class C { class D { int M() => 2; } }";
var editsA = GetTopEdits(srcA1, srcA2);
var editsB = GetTopEdits(srcB1, srcB2);
EditAndContinueValidation.VerifySemantics(
[editsA, editsB],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.D.M"))
])
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedType_Replace_WithUpdateInNestedType_Partial_SameDocument()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int N() => 1; } partial class C { class D { int M() => 1; } }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int N() => 2; } partial class C { class D { int M() => 2; } }";
var edits = GetTopEdits(src1, src2);
EditAndContinueValidation.VerifySemantics(
edits,
semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.D.M"))
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedType_Replace_WithUpdateInNestedType()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { int N() => 1; class D { int M() => 1; } }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { int N() => 2; class D { int M() => 2; } }";
var edits = GetTopEdits(src1, src2);
EditAndContinueValidation.VerifySemantics(
edits,
semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.D.M"))
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedType_Update_WithUpdateInNestedType()
{
var src1 = @"[System.Obsolete(""A"")]class C { int N() => 1; class D { int M() => 1; } }";
var src2 = @"[System.Obsolete(""B"")]class C { int N() => 1; class D { int M() => 2; } }";
var edits = GetTopEdits(src1, src2);
EditAndContinueValidation.VerifySemantics(
edits,
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.D.M"))
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void NestedType_Move_Sideways()
{
var src1 = @"class N { class C {} } class M { }";
var src2 = @"class N { } class M { class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [class C {}]@10 -> @33");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "class C", GetResource("class")));
}
[Fact]
public void NestedType_Move_Outside()
{
var src1 = @"class C { class D { } }";
var src2 = @"class C { } class D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [class D { }]@10 -> @12");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "class D", FeaturesResources.class_));
}
[Fact]
public void NestedType_Move_Insert()
{
var src1 = @"class C { class D { } }";
var src2 = @"class C { class E { class D { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [class E { class D { } }]@10",
"Move [class D { }]@10 -> @20");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "class D", FeaturesResources.class_));
}
[Fact]
public void NestedType_MoveAndNamespaceChange()
{
var src1 = @"namespace N { class C { class D { } } } namespace M { }";
var src2 = @"namespace N { class C { } } namespace M { class D { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [class D { }]@24 -> @42");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "class D", FeaturesResources.class_));
}
[Fact]
public void NestedType_Move_MultiFile()
{
var srcA1 = @"partial class N { class C {} } partial class M { }";
var srcB1 = @"partial class N { } partial class M { class C {} }";
var srcA2 = @"partial class N { } partial class M { class C {} }";
var srcB2 = @"partial class N { class C {} } partial class M { }";
var editsA = GetTopEdits(srcA1, srcA2);
editsA.VerifyEdits(
"Move [class C {}]@18 -> @49");
var editsB = GetTopEdits(srcB1, srcB2);
editsB.VerifyEdits(
"Move [class C {}]@49 -> @18");
EditAndContinueValidation.VerifySemantics(
[editsA, editsB],
[
DocumentResults(),
DocumentResults(),
]);
}
[Fact]
public void NestedType_Move_PartialTypesInSameFile()
{
var src1 = @"partial class N { class C {} class D {} } partial class N { }";
var src2 = @"partial class N { class C {} } partial class N { class D {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [class D {}]@29 -> @60");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void NestedType_Move_Reloadable()
{
var src1 = ReloadableAttributeSrc + "class N { [CreateNewOnMetadataUpdate]class C {} } class M { }";
var src2 = ReloadableAttributeSrc + "class N { } class M { [CreateNewOnMetadataUpdate]class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "class C", GetResource("class")));
}
[Fact]
public void NestedType_Insert1()
{
var src1 = @"class C { }";
var src2 = @"class C { class D { class E { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [class D { class E { } }]@10",
"Insert [class E { }]@20");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void NestedType_Insert2()
{
var src1 = @"class C { }";
var src2 = @"class C { protected class D { public class E { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [protected class D { public class E { } }]@10",
"Insert [public class E { }]@30");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void NestedType_Insert3()
{
var src1 = @"class C { }";
var src2 = @"class C { private class D { public class E { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [private class D { public class E { } }]@10",
"Insert [public class E { }]@28");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void NestedType_Insert4()
{
var src1 = @"class C { }";
var src2 = @"class C { private class D { public D(int a, int b) { } public int P { get; set; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [private class D { public D(int a, int b) { } public int P { get; set; } }]@10",
"Insert [public D(int a, int b) { }]@28",
"Insert [public int P { get; set; }]@55",
"Insert [(int a, int b)]@36",
"Insert [{ get; set; }]@68",
"Insert [int a]@37",
"Insert [int b]@44",
"Insert [get;]@70",
"Insert [set;]@75");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void NestedType_Insert_ReloadableIntoReloadable1()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { [CreateNewOnMetadataUpdate]class D { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedType_Insert_ReloadableIntoReloadable2()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { [CreateNewOnMetadataUpdate]class D { [CreateNewOnMetadataUpdate]class E { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedType_Insert_ReloadableIntoReloadable3()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { class D { [CreateNewOnMetadataUpdate]class E { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedType_Insert_ReloadableIntoReloadable4()
{
var src1 = ReloadableAttributeSrc + "class C { }";
var src2 = ReloadableAttributeSrc + "class C { [CreateNewOnMetadataUpdate]class D { [CreateNewOnMetadataUpdate]class E { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.D")));
}
[Fact]
public void NestedType_Insert_Member_Reloadable()
{
var src1 = ReloadableAttributeSrc + "class C { [CreateNewOnMetadataUpdate]class D { } }";
var src2 = ReloadableAttributeSrc + "class C { [CreateNewOnMetadataUpdate]class D { int x; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C.D"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedType_InsertMemberWithInitializer1()
{
var src1 = @"
class C
{
}";
var src2 = @"
class C
{
private class D
{
public int P = 1;
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.D"), preserveLocalVariables: false)
]);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")]
public void NestedType_Insert_PInvoke()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
class C
{
}";
var src2 = @"
using System;
using System.Runtime.InteropServices;
class C
{
abstract class D
{
public extern D();
public static extern int P { [DllImport(""msvcrt.dll"")]get; [DllImport(""msvcrt.dll"")]set; }
[DllImport(""msvcrt.dll"")]
public static extern int puts(string c);
[DllImport(""msvcrt.dll"")]
public static extern int operator +(D d, D g);
[DllImport(""msvcrt.dll"")]
public static extern explicit operator int (D d);
}
}
";
var edits = GetTopEdits(src1, src2);
// Adding P/Invoke is not supported by the CLR.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertExtern, "public extern D()", FeaturesResources.constructor),
Diagnostic(RudeEditKind.InsertExtern, "public static extern int P", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertExtern, "public static extern int puts(string c)", FeaturesResources.method),
Diagnostic(RudeEditKind.InsertExtern, "public static extern int operator +(D d, D g)", FeaturesResources.operator_),
Diagnostic(RudeEditKind.InsertExtern, "public static extern explicit operator int (D d)", CSharpFeaturesResources.conversion_operator));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")]
public void NestedType_Insert_VirtualAbstract()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
class C
{
}";
var src2 = @"
using System;
using System.Runtime.InteropServices;
class C
{
abstract class D
{
public abstract int P { get; }
public abstract int this[int i] { get; }
public abstract int puts(string c);
public virtual event Action E { add { } remove { } }
public virtual int Q { get { return 1; } }
public virtual int this[string i] { get { return 1; } }
public virtual int M(string c) { return 1; }
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void NestedType_TypeReorder1()
{
var src1 = @"class C { struct E { } class F { } delegate void D(); interface I {} }";
var src2 = @"class C { class F { } interface I {} delegate void D(); struct E { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [struct E { }]@10 -> @56",
"Reorder [interface I {}]@54 -> @22");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void NestedType_MethodDeleteInsert()
{
var src1 = @"public class C { public void goo() {} }";
var src2 = @"public class C { private class D { public void goo() {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [private class D { public void goo() {} }]@17",
"Insert [public void goo() {}]@35",
"Insert [()]@50",
"Delete [public void goo() {}]@17",
"Delete [()]@32");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.D")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.goo"), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void NestedType_ClassDeleteInsert()
{
var src1 = @"public class C { public class X {} }";
var src2 = @"public class C { public class D { public class X {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [public class D { public class X {} }]@17",
"Move [public class X {}]@17 -> @34");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "public class X", FeaturesResources.class_));
}
/// <summary>
/// A new generic type can be added whether it's nested and inherits generic parameters from the containing type, or top-level.
/// </summary>
[Fact]
public void NestedClassGeneric_Insert()
{
var src1 = @"
using System;
class C<T>
{
}
";
var src2 = @"
using System;
class C<T>
{
class D {}
struct S {}
enum N {}
interface I {}
delegate void D();
}
class D<T>
{
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void NestedEnum_InsertMember()
{
var src1 = "struct S { enum N { A = 1 } }";
var src2 = "struct S { enum N { A = 1, B = 2 } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [enum N { A = 1 }]@11 -> [enum N { A = 1, B = 2 }]@11",
"Insert [B = 2]@27");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "B = 2", FeaturesResources.enum_value));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50876")]
public void NestedEnumInPartialType_InsertDelete()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { enum N { A = 1 } }";
var srcA2 = "partial struct S { enum N { A = 1 } }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults()
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50876")]
public void NestedEnumInPartialType_InsertDeleteAndUpdateMember()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { enum N { A = 1 } }";
var srcA2 = "partial struct S { enum N { A = 2 } }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.InitializerUpdate, "A = 2", FeaturesResources.enum_value),
]),
DocumentResults()
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50876")]
public void NestedEnumInPartialType_InsertDeleteAndUpdateBase()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { enum N : uint { A = 1 } }";
var srcA2 = "partial struct S { enum N : int { A = 1 } }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.EnumUnderlyingTypeUpdate, "enum N", FeaturesResources.enum_),
]),
DocumentResults()
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/50876")]
public void NestedEnumInPartialType_InsertDeleteAndInsertMember()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { enum N { A = 1 } }";
var srcA2 = "partial struct S { enum N { A = 1, B = 2 } }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.Insert, "B = 2", FeaturesResources.enum_value)]),
DocumentResults()
]);
}
[Fact]
public void NestedDelegateInPartialType_InsertDelete()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { delegate void D(); }";
var srcA2 = "partial struct S { delegate void D(); }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
// delegate does not have any user-defined method body and this does not need a PDB update
semanticEdits: NoSemanticEdits),
DocumentResults()
]);
}
[Fact]
public void NestedDelegateInPartialType_InsertDeleteAndChangeParameters()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { delegate void D(); }";
var srcA2 = "partial struct S { delegate void D(int x); }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.TypeUpdate, "delegate void D(int x)", GetResource("delegate"))]),
DocumentResults()
]);
}
[Fact]
public void NestedDelegateInPartialType_InsertDeleteAndChangeReturnType()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { delegate ref int D(); }";
var srcA2 = "partial struct S { delegate ref readonly int D(); }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.TypeUpdate, "delegate ref readonly int D()", FeaturesResources.delegate_)
]),
DocumentResults()
]);
}
[Fact]
public void NestedDelegateInPartialType_InsertDeleteAndChangeOptionalParameterValue()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { delegate void D(int x = 1); }";
var srcA2 = "partial struct S { delegate void D(int x = 2); }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.InitializerUpdate, "int x = 2", GetResource("parameter"))
]),
DocumentResults()
]);
}
[Fact]
public void NestedPartialTypeInPartialType_InsertDeleteAndChange()
{
var srcA1 = "partial struct S { partial class C { void F1() {} } }";
var srcB1 = "partial struct S { partial class C { void F2(byte x) {} } }";
var srcC1 = "partial struct S { }";
var srcA2 = "partial struct S { partial class C { void F1() {} } }";
var srcB2 = "partial struct S { }";
var srcC2 = "partial struct S { partial class C { void F2(int x) {} } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("S.C.F2").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Byte))?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("S.C"))
]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("S.C.F2").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Int32))?.ISymbol)])
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Type_Insert_Partial_Multiple()
{
var srcA1 = "";
var srcB1 = "";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Delete_Partial_Multiple()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = "";
var srcB2 = "";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(diagnostics: [Diagnostic(RudeEditKind.Delete, null, GetResource("class", "C"))]),
DocumentResults(diagnostics: [Diagnostic(RudeEditKind.Delete, null, GetResource("class", "C"))]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Type_Partial_InsertDeleteAndChange_Attribute()
{
var srcA1 = "partial class C { }";
var srcB1 = "";
var srcC1 = "partial class C { }";
var srcA2 = "";
var srcB2 = "[A]partial class C { }";
var srcC2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Type_Partial_InsertDeleteAndChange_TypeParameterAttribute()
{
var srcA1 = "partial class C<T> { }";
var srcB1 = "";
var srcC1 = "partial class C<T> { }";
var srcA2 = "";
var srcB2 = "partial class C<[A]T> { }";
var srcC2 = "partial class C<T> { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.GenericTypeUpdate, "T")
]),
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter),
]),
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Type_Partial_InsertDeleteAndChange_Constraint()
{
var srcA1 = "partial class C<T> { }";
var srcB1 = "";
var srcC1 = "partial class C<T> { }";
var srcA2 = "";
var srcB2 = "partial class C<T> where T : new() { }";
var srcC2 = "partial class C<T> { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.ChangingConstraints, "where T : new()", GetResource("type parameter"))
]),
DocumentResults(),
]);
}
[Fact]
public void Type_Partial_InsertDeleteRefactor()
{
var srcA1 = "partial class C : I { void F() { } }";
var srcB1 = "[A][B]partial class C : J { void G() { } }";
var srcC1 = "";
var srcD1 = "";
var srcA2 = "";
var srcB2 = "";
var srcC2 = "[A]partial class C : I, J { void F() { } }";
var srcD2 = "[B]partial class C { void G() { } }";
var srcE = "interface I {} interface J {}";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2), GetTopEdits(srcE, srcE)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("F"))]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("G"))]),
DocumentResults(),
]);
}
[Fact]
public void Type_Partial_Attribute_AddMultiple()
{
var attributes = @"
class A : System.Attribute {}
class B : System.Attribute {}
";
var srcA1 = "partial class C { }" + attributes;
var srcB1 = "partial class C { }";
var srcA2 = "[A]partial class C { }" + attributes;
var srcB2 = "[B]partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Type_Partial_InsertDeleteRefactor_AttributeListSplitting()
{
var srcA1 = "partial class C { void F() { } }";
var srcB1 = "[A,B]partial class C { void G() { } }";
var srcC1 = "";
var srcD1 = "";
var srcA2 = "";
var srcB2 = "";
var srcC2 = "[A]partial class C { void F() { } }";
var srcD2 = "[B]partial class C { void G() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.G"))
]),
]);
}
[Fact]
public void Type_Partial_InsertDeleteChangeMember()
{
var srcA1 = "partial class C { void F(int y = 1) { } }";
var srcB1 = "partial class C { void G(int x = 1) { } }";
var srcC1 = "";
var srcA2 = "";
var srcB2 = "partial class C { void G(int x = 2) { } }";
var srcC2 = "partial class C { void F(int y = 2) { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(diagnostics: [Diagnostic(RudeEditKind.InitializerUpdate, "int x = 2", FeaturesResources.parameter)]),
DocumentResults(diagnostics: [Diagnostic(RudeEditKind.InitializerUpdate, "int y = 2", FeaturesResources.parameter)]),
]);
}
[Fact]
public void NestedPartialTypeInPartialType_InsertDeleteAndInsertVirtual()
{
var srcA1 = "partial interface I { partial class C { virtual void F1() {} } }";
var srcB1 = "partial interface I { partial class C { virtual void F2() {} } }";
var srcC1 = "partial interface I { partial class C { } }";
var srcD1 = "partial interface I { partial class C { } }";
var srcE1 = "partial interface I { }";
var srcF1 = "partial interface I { }";
var srcA2 = "partial interface I { partial class C { } }";
var srcB2 = "";
var srcC2 = "partial interface I { partial class C { virtual void F1() {} } }"; // move existing virtual into existing partial decl
var srcD2 = "partial interface I { partial class C { virtual void N1() {} } }"; // insert new virtual into existing partial decl
var srcE2 = "partial interface I { partial class C { virtual void F2() {} } }"; // move existing virtual into a new partial decl
var srcF2 = "partial interface I { partial class C { virtual void N2() {} } }"; // insert new virtual into new partial decl
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2), GetTopEdits(srcE1, srcE2), GetTopEdits(srcF1, srcF2)],
[
// A
DocumentResults(),
// B
DocumentResults(),
// C
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember<INamedTypeSymbol>("C").GetMember("F1"))]),
// D
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.InsertVirtual, "virtual void N1()", FeaturesResources.method)]),
// E
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("I").GetMember<INamedTypeSymbol>("C").GetMember("F2"))]),
// F
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.InsertVirtual, "virtual void N2()", FeaturesResources.method)]),
]);
}
#endregion
#region Namespaces
[Fact]
public void Namespace_Empty_Insert()
{
var src1 = @"";
var src2 = @"namespace C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [namespace C { }]@0");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Empty_InsertNested()
{
var src1 = @"namespace C { }";
var src2 = @"namespace C { namespace D { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [namespace D { }]@14");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Empty_DeleteNested()
{
var src1 = @"namespace C { namespace D { } }";
var src2 = @"namespace C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [namespace D { }]@14");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Empty_Move()
{
var src1 = @"namespace C { namespace D { } }";
var src2 = @"namespace C { } namespace D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [namespace D { }]@14 -> @16");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Empty_Reorder1()
{
var src1 = @"namespace C { namespace D { } class T { } namespace E { } }";
var src2 = @"namespace C { namespace E { } class T { } namespace D { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [class T { }]@30 -> @30",
"Reorder [namespace E { }]@42 -> @14");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Empty_Reorder2()
{
var src1 = @"namespace C { namespace D1 { } namespace D2 { } namespace D3 { } class T { } namespace E { } }";
var src2 = @"namespace C { namespace E { } class T { } namespace D1 { } namespace D2 { } namespace D3 { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [class T { }]@65 -> @65",
"Reorder [namespace E { }]@77 -> @14");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Empty_FileScoped_Insert()
{
var src1 = @"";
var src2 = @"namespace N;";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [namespace N;]@0");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Empty_FileScoped_Delete()
{
var src1 = @"namespace N;";
var src2 = @"";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [namespace N;]@0");
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("namespace N; class C {}")]
[InlineData("namespace N { class C {} }")]
public void Namespace_Insert_NewType(string src2)
{
var src1 = @"";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "class C", GetResource("class"))],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("N.C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Theory]
[InlineData("namespace N.M { class C {} }")]
[InlineData("namespace N.M; class C {}")]
public void Namespace_Insert_NewType_Qualified(string src2)
{
var src1 = "";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "class C", GetResource("class"))],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("N.M.C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Theory]
[InlineData("class")]
[InlineData("interface")]
[InlineData("enum")]
[InlineData("struct")]
[InlineData("record")]
[InlineData("record struct")]
public void Namespace_Insert(string keyword)
{
var declaration = keyword + " X {}";
var src1 = declaration;
var src2 = "namespace N { " + declaration + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, keyword + " X", GetResource(keyword), "<global namespace>", "N")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Insert_Delegate()
{
var declaration = "delegate void X();";
var src1 = declaration;
var src2 = "namespace N { " + declaration + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "delegate void X()", GetResource("delegate"), "<global namespace>", "N")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Insert_MultipleDeclarations()
{
var src1 = @"class C {} class D {}";
var src2 = "namespace N { class C {} class D { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "<global namespace>", "N"),
Diagnostic(RudeEditKind.ChangingNamespace, "class D", GetResource("class"), "<global namespace>", "N")
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Insert_FileScoped()
{
var src1 = @"class C {}";
var src2 = @"namespace N; class C {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "<global namespace>", "N")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Insert_Nested()
{
var src1 = @"namespace N { class C {} }";
var src2 = @"namespace N { namespace M { class C {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N", "N.M")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Insert_Qualified()
{
var src1 = @"class C {}";
var src2 = @"namespace N.M { class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "<global namespace>", "N.M")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Insert_Qualified_FileScoped()
{
var src1 = @"class C {}";
var src2 = @"namespace N.M; class C {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "<global namespace>", "N.M")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Theory]
[InlineData("class")]
[InlineData("interface")]
[InlineData("enum")]
[InlineData("struct")]
[InlineData("record")]
[InlineData("record struct")]
public void Namespace_Delete(string keyword)
{
var declaration = keyword + " X {}";
var src1 = "namespace N { " + declaration + " }";
var src2 = declaration;
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, keyword + " X", GetResource(keyword), "N", "<global namespace>")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Delete_Delegate()
{
var declaration = "delegate void X();";
var src1 = "namespace N { " + declaration + " }";
var src2 = declaration;
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "delegate void X()", GetResource("delegate"), "N", "<global namespace>")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Delete_MultipleDeclarations()
{
var src1 = @"namespace N { class C {} class D { } }";
var src2 = @"class C {} class D {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N", "<global namespace>"),
Diagnostic(RudeEditKind.ChangingNamespace, "class D", GetResource("class"), "N", "<global namespace>")
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Theory]
[InlineData("namespace N.M { class C {} }")]
[InlineData("namespace N.M; class C {}")]
public void Namespace_Delete_Qualified(string src1)
{
var src2 = @"class C {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N.M", "<global namespace>")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Qualified_ToFileScoped()
{
var src1 = @"namespace N.M { class C {} }";
var src2 = @"namespace N.M; class C {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("class")]
[InlineData("interface")]
[InlineData("enum")]
[InlineData("struct")]
[InlineData("record")]
[InlineData("record struct")]
public void Namespace_Update(string keyword)
{
var declaration = keyword + " X {}";
var src1 = "namespace N { " + declaration + " }";
var src2 = "namespace M { " + declaration + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, keyword + " X", GetResource(keyword), "N", "M")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_Delegate()
{
var declaration = "delegate void X();";
var src1 = "namespace N { " + declaration + " }";
var src2 = "namespace M { " + declaration + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "delegate void X()", GetResource("delegate"), "N", "M")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_Multiple()
{
var src1 = @"namespace N { class C {} class D {} }";
var src2 = @"namespace M { class C {} class D {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N", "M"),
Diagnostic(RudeEditKind.ChangingNamespace, "class D", GetResource("class"), "N", "M"),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_Qualified1()
{
var src1 = @"namespace N.M { class C {} }";
var src2 = @"namespace N.M.O { class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N.M", "N.M.O")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_Qualified2()
{
var src1 = @"namespace N.M { class C {} }";
var src2 = @"namespace N { class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N.M", "N")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_Qualified3()
{
var src1 = @"namespace N.M1.O { class C {} }";
var src2 = @"namespace N.M2.O { class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N.M1.O", "N.M2.O")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_FileScoped()
{
var src1 = @"namespace N; class C {}";
var src2 = @"namespace M; class C {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N", "M")],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_MultiplePartials1()
{
var srcA1 = @"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }";
var srcB1 = @"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }";
var srcA2 = @"namespace N { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }";
var srcB2 = @"namespace M { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("M.C"), partialType: "M.C"),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("M.C"), partialType: "M.C"),
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_MultiplePartials2()
{
var srcA1 = @"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }";
var srcB1 = @"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }";
var srcA2 = @"namespace M { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }";
var srcB2 = @"namespace M { partial class/*3*/C {} } namespace M { partial class/*4*/C {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(diagnostics:
[
Diagnostic(RudeEditKind.ChangingNamespace, "partial class/*1*/C", GetResource("class"), "N", "M")
]),
DocumentResults(diagnostics:
[
Diagnostic(RudeEditKind.ChangingNamespace, "partial class/*3*/C", GetResource("class"), "N", "M")
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Namespace_Update_MultiplePartials_MergeInNewNamspace()
{
var src1 = @"namespace N { partial class C {} } namespace M { partial class C {} }";
var src2 = @"namespace X { partial class C {} } namespace X { partial class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingNamespace, "partial class C", GetResource("class"), "M", "X"),
Diagnostic(RudeEditKind.Delete, "partial class C", DeletedSymbolDisplay(GetResource("class"), "C")));
}
[Fact]
public void Namespace_Update_MultipleTypesWithSameNameAndArity()
{
var src1 = @"namespace N1 { class C {} } namespace N2 { class C {} } namespace O { class C {} }";
var src2 = @"namespace M1 { class C {} } namespace M2 { class C {} } namespace O { class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N2", "M2"),
Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N1", "M1"));
}
[Fact]
public void Namespace_UpdateAndInsert()
{
var src1 = @"namespace N.M { class C {} }";
var src2 = @"namespace N { namespace M { class C {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_UpdateAndDelete()
{
var src1 = @"namespace N { namespace M { class C {} } }";
var src2 = @"namespace N.M { class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Namespace_Move1()
{
var src1 = @"namespace N { namespace M { class C {} class C<T> {} } class D {} }";
var src2 = @"namespace N { class D {} } namespace M { class C<T> {} class C {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [namespace M { class C {} class C<T> {} }]@14 -> @27",
"Reorder [class C<T> {}]@39 -> @41");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingNamespace, "class C", GetResource("class"), "N.M", "M"),
Diagnostic(RudeEditKind.ChangingNamespace, "class C<T>", GetResource("class"), "N.M", "M"));
}
[Fact]
public void Namespace_Move2()
{
var src1 = @"namespace N1 { namespace M { class C {} } namespace N2 { } }";
var src2 = @"namespace N1 { } namespace N2 { namespace M { class C {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Move [namespace N2 { }]@42 -> @17",
"Move [namespace M { class C {} }]@15 -> @32");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingNamespace, "class C", "class", "N1.M", "N2.M"));
}
#endregion
#region Members
[Fact]
public void PartialMember_DeleteInsert_SingleDocument()
{
var src1 = @"
using System;
partial class C
{
void M() {}
int P1 { get; set; }
int P2 { get => 1; set {} }
int this[int i] { get => 1; set {} }
int this[byte i] { get => 1; set {} }
event Action E { add {} remove {} }
event Action EF;
int F1;
int F2;
}
partial class C
{
}
";
var src2 = @"
using System;
partial class C
{
}
partial class C
{
void M() {}
int P1 { get; set; }
int P2 { get => 1; set {} }
int this[int i] { get => 1; set {} }
int this[byte i] { get => 1; set {} }
event Action E { add {} remove {} }
event Action EF;
int F1, F2;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [void M() {}]@68",
"Insert [int P1 { get; set; }]@85",
"Insert [int P2 { get => 1; set {} }]@111",
"Insert [int this[int i] { get => 1; set {} }]@144",
"Insert [int this[byte i] { get => 1; set {} }]@186",
"Insert [event Action E { add {} remove {} }]@229",
"Insert [event Action EF;]@270",
"Insert [int F1, F2;]@292",
"Insert [()]@74",
"Insert [{ get; set; }]@92",
"Insert [{ get => 1; set {} }]@118",
"Insert [[int i]]@152",
"Insert [{ get => 1; set {} }]@160",
"Insert [[byte i]]@194",
"Insert [{ get => 1; set {} }]@203",
"Insert [{ add {} remove {} }]@244",
"Insert [Action EF]@276",
"Insert [int F1, F2]@292",
"Insert [get;]@94",
"Insert [set;]@99",
"Insert [get => 1;]@120",
"Insert [set {}]@130",
"Insert [int i]@153",
"Insert [get => 1;]@162",
"Insert [set {}]@172",
"Insert [byte i]@195",
"Insert [get => 1;]@205",
"Insert [set {}]@215",
"Insert [add {}]@246",
"Insert [remove {}]@253",
"Insert [EF]@283",
"Insert [F1]@296",
"Insert [F2]@300",
"Delete [void M() {}]@43",
"Delete [()]@49",
"Delete [int P1 { get; set; }]@60",
"Delete [{ get; set; }]@67",
"Delete [get;]@69",
"Delete [set;]@74",
"Delete [int P2 { get => 1; set {} }]@86",
"Delete [{ get => 1; set {} }]@93",
"Delete [get => 1;]@95",
"Delete [set {}]@105",
"Delete [int this[int i] { get => 1; set {} }]@119",
"Delete [[int i]]@127",
"Delete [int i]@128",
"Delete [{ get => 1; set {} }]@135",
"Delete [get => 1;]@137",
"Delete [set {}]@147",
"Delete [int this[byte i] { get => 1; set {} }]@161",
"Delete [[byte i]]@169",
"Delete [byte i]@170",
"Delete [{ get => 1; set {} }]@178",
"Delete [get => 1;]@180",
"Delete [set {}]@190",
"Delete [event Action E { add {} remove {} }]@204",
"Delete [{ add {} remove {} }]@219",
"Delete [add {}]@221",
"Delete [remove {}]@228",
"Delete [event Action EF;]@245",
"Delete [Action EF]@251",
"Delete [EF]@258",
"Delete [int F1;]@267",
"Delete [int F1]@267",
"Delete [F1]@271",
"Delete [int F2;]@280",
"Delete [int F2]@280",
"Delete [F2]@284");
EditAndContinueValidation.VerifySemantics(
[edits],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.M")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P1").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P1").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P2").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P2").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters[0].Type.Name == "Int32").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters[0].Type.Name == "Int32").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters[0].Type.Name == "Int32")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters[0].Type.Name == "Byte").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters[0].Type.Name == "Byte").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters[0].Type.Name == "Byte")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.E").AddMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.E").RemoveMethod),
])
]);
}
[Fact]
public void PartialMember_InsertDelete_MultipleDocuments()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { void F() {} }";
var srcA2 = "partial class C { void F() {} }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("F"), preserveLocalVariables: false)
]),
DocumentResults()
]);
}
[Fact]
public void PartialMember_DeleteInsert_MultipleDocuments()
{
var srcA1 = "partial class C { void F() {} }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { void F() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
])
]);
}
[Fact]
public void PartialMember_DeleteInsert_GenericMethod()
{
var srcA1 = "partial class C { void F<T>() {} }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { void F<T>() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
])
],
capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(diagnostics:
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "void F<T>()", GetResource("method"))
])
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void PartialMember_DeleteInsert_GenericType()
{
var srcA1 = "partial class C<T> { void F() {} }";
var srcB1 = "partial class C<T> { }";
var srcA2 = "partial class C<T> { }";
var srcB2 = "partial class C<T> { void F() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
])
],
capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(diagnostics:
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "void F()", GetResource("method"))
])
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void PartialMember_DeleteInsert_Destructor()
{
var srcA1 = "partial class C { ~C() {} }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { ~C() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("Finalize"), preserveLocalVariables: false),
])
]);
}
[Fact]
public void PartialNestedType_InsertDeleteAndChange()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { class D { void M() {} } interface I { } }";
var srcA2 = "partial class C { class D : I { void M() {} } interface I { } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.BaseTypeOrInterfaceUpdate, "class D", FeaturesResources.class_),
]),
DocumentResults()
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51011")]
public void PartialMember_RenameInsertDelete()
{
var srcA1 = "partial class C { void F1() {} }";
var srcB1 = "partial class C { void F2() {} }";
var srcA2 = "partial class C { void F2() {} }";
var srcB2 = "partial class C { void F1() {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F2")),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F1")),
])
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51011")]
public void PartialMember_RenameInsertDelete_SameFile()
{
var src1 = """
partial class C { void F1(int a) {} void F4(int d) {} }
partial class C { void F3(int c) {} void F2(int b) {} }
partial class C { }
""";
var src2 = """
partial class C { void F2(int b) {} void F4(int d) {} }
partial class C { void F1(int a) {} }
partial class C { void F3(int c) {} }
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [void F1(int a) {}]@18 -> [void F2(int b) {}]@18",
"Update [void F3(int c) {}]@75 -> [void F1(int a) {}]@75",
"Insert [void F3(int c) {}]@114",
"Insert [(int c)]@121",
"Update [int a]@26 -> [int b]@26",
"Update [int c]@83 -> [int a]@83",
"Insert [int c]@122",
"Delete [void F2(int b) {}]@93",
"Delete [(int b)]@100",
"Delete [int b]@101");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F2")),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51011")]
public void PartialMember_SignatureChangeInsertDelete()
{
var srcA1 = "partial class C { void F(byte x) {} }";
var srcB1 = "partial class C { void F(char x) {} }";
var srcA2 = "partial class C { void F(char x) {} }";
var srcB2 = "partial class C { void F(byte x) {} }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IMethodSymbol>("C.F").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Char }]))]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IMethodSymbol>("C.F").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Byte }]))]),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51011")]
public void PartialMember_SignatureChangeInsertDelete_Indexer()
{
var srcA1 = "partial class C { int this[byte x] { get => 1; set {} } }";
var srcB1 = "partial class C { int this[char x] { get => 1; set {} } }";
var srcA2 = "partial class C { int this[char x] { get => 1; set {} } }";
var srcB2 = "partial class C { int this[byte y] { get => 1; set {} } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Char }])),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Char }]).GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Char }]).SetMethod),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Byte }])),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Byte }]).GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("C.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Byte }]).SetMethod),
])
]);
}
[Fact]
public void PartialMember_DeleteInsert_UpdateMethodBodyError()
{
var srcA1 = @"
using System.Collections.Generic;
partial class C
{
IEnumerable<int> F() { yield return 1; }
}
";
var srcB1 = @"
using System.Collections.Generic;
partial class C
{
}
";
var srcA2 = @"
using System.Collections.Generic;
partial class C
{
}
";
var srcB2 = @"
using System.Collections.Generic;
partial class C
{
IEnumerable<int> F() { yield return 1; yield return 2; }
}
";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true)
])
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void PartialMember_DeleteInsert_UpdatePropertyAccessors()
{
var srcA1 = "partial class C { int P { get => 1; set { Console.WriteLine(1); } } }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int P { get => 2; set { Console.WriteLine(2); } } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").SetMethod)
])
]);
}
[Fact]
public void PartialMember_DeleteInsert_UpdateAutoProperty()
{
var srcA1 = "partial class C { int P => 1; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int P => 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").GetMethod)
])
]);
}
[Fact]
public void PartialMember_DeleteInsert_AddFieldInitializer()
{
var srcA1 = "partial class C { int f; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int f = 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
])
]);
}
[Fact]
public void PartialMember_DeleteInsert_RemoveFieldInitializer()
{
var srcA1 = "partial class C { int f = 1; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int f; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
])
]);
}
[Fact]
public void PartialMember_DeleteInsert_ConstructorWithInitializers()
{
var srcA1 = "partial class C { int f = 1; C(int x) { f = x; } }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { int f = 1; }";
var srcB2 = "partial class C { C(int x) { f = x + 1; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), partialType: "C", preserveLocalVariables: true)
])
]);
}
[Fact]
public void PartialMember_DeleteInsert_MethodAddParameter()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { void F() {} }";
var srcA2 = "partial struct S { void F(int x) {} }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("S.F").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol)
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("S.F").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("S"))
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void PartialMember_DeleteInsert_UpdateMethodParameterType()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { void F(int x); }";
var srcA2 = "partial struct S { void F(byte x); }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("S.F").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Byte))?.ISymbol)
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("S.F").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Int32))?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("S"))
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void PartialMember_DeleteInsert_MethodAddTypeParameter()
{
var srcA1 = "partial struct S { }";
var srcB1 = "partial struct S { void F(); }";
var srcA2 = "partial struct S { void F<T>(); }";
var srcB2 = "partial struct S { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("S.F").FirstOrDefault(m => m.GetArity() == 1)?.ISymbol)
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("S.F").FirstOrDefault(m => m.GetArity() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("S"))
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType);
}
#endregion
#region Methods
[Theory]
[InlineData("static")]
[InlineData("virtual")]
[InlineData("abstract")]
[InlineData("override")]
[InlineData("sealed override", "override")]
public void Method_Modifiers_Update(string oldModifiers, string newModifiers = "")
{
if (oldModifiers != "")
{
oldModifiers += " ";
}
if (newModifiers != "")
{
newModifiers += " ";
}
var src1 = "class C { " + oldModifiers + "int F() => 0; }";
var src2 = "class C { " + newModifiers + "int F() => 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [" + oldModifiers + "int F() => 0;]@10 -> [" + newModifiers + "int F() => 0;]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "int F()", FeaturesResources.method));
}
[Fact]
public void Method_NewModifier_Add()
{
var src1 = "class C { int F() => 0; }";
var src2 = "class C { new int F() => 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [int F() => 0;]@10 -> [new int F() => 0;]@10");
// Currently, an edit is produced eventhough there is no metadata/IL change. Consider improving.
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IMethodSymbol>("F")));
}
[Fact]
public void Method_NewModifier_Remove()
{
var src1 = "class C { new int F() => 0; }";
var src2 = "class C { int F() => 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [new int F() => 0;]@10 -> [int F() => 0;]@10");
// Currently, an edit is produced eventhough there is no metadata/IL change. Consider improving.
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IMethodSymbol>("F")));
}
[Fact]
public void Method_ReadOnlyModifier_Add_InMutableStruct()
{
var src1 = @"
struct S
{
public int M() => 1;
}";
var src2 = @"
struct S
{
public readonly int M() => 1;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.M")));
}
[Fact]
public void Method_ReadOnlyModifier_Add_InReadOnlyStruct1()
{
var src1 = @"
readonly struct S
{
public int M()
=> 1;
}";
var src2 = @"
readonly struct S
{
public readonly int M()
=> 1;
}";
var edits = GetTopEdits(src1, src2);
// Currently, an edit is produced eventhough the body nor IsReadOnly attribute have changed. Consider improving.
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("S").GetMember<IMethodSymbol>("M")));
}
[Fact]
public void Method_ReadOnlyModifier_Add_InReadOnlyStruct2()
{
var src1 = @"
readonly struct S
{
public int M() => 1;
}";
var src2 = @"
struct S
{
public readonly int M() => 1;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "struct S", "struct"));
}
[Fact]
public void Method_AsyncModifier_Remove()
{
var src1 = @"
class Test
{
public async Task<int> WaitAsync()
{
return 1;
}
}";
var src2 = @"
class Test
{
public Task<int> WaitAsync()
{
return Task.FromResult(1);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingFromAsynchronousToSynchronous, "public Task<int> WaitAsync()", FeaturesResources.method));
}
[Fact]
public void Method_AsyncModifier_Add()
{
var src1 = @"
class Test
{
public Task<int> WaitAsync()
{
return 1;
}
}";
var src2 = @"
class Test
{
public async Task<int> WaitAsync()
{
await Task.Delay(1000);
return 1;
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition | EditAndContinueCapabilities.AddExplicitInterfaceImplementation);
VerifyPreserveLocalVariables(edits, preserveLocalVariables: false);
}
[Fact]
public void Method_AsyncModifier_Add_NotSupported()
{
var src1 = @"
class Test
{
public Task<int> WaitAsync()
{
return 1;
}
}";
var src2 = @"
class Test
{
public async Task<int> WaitAsync()
{
await Task.Delay(1000);
return 1;
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.MakeMethodAsyncNotSupportedByRuntime, "public async Task<int> WaitAsync()")],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory]
[InlineData("string", "string?")]
[InlineData("object", "dynamic")]
[InlineData("(int a, int b)", "(int a, int c)")]
public void Method_Update_ReturnType_RuntimeTypeUnchanged(string oldType, string newType)
{
var src1 = "class C { " + oldType + " M() => default; }";
var src2 = "class C { " + newType + " M() => default; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")));
}
[Theory]
[InlineData("int", "string")]
[InlineData("int", "int?")]
[InlineData("(int a, int b)", "(int a, double b)")]
public void Method_Update_ReturnType_RuntimeTypeChanged(string oldType, string newType)
{
var src1 = "class C { " + oldType + " M() => default; }";
var src2 = "class C { " + newType + " M() => default; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, newType + " M()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_ReturnType_WithBodyChange()
{
var src1 = "class C { int M() => 1; }";
var src2 = "class C { char M() => 'a'; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "char M()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_ReturnType_Tuple()
{
var src1 = "class C { (int, int) M() { throw new System.Exception(); } }";
var src2 = "class C { (string, int) M() { throw new System.Exception(); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [(int, int) M() { throw new System.Exception(); }]@10 -> [(string, int) M() { throw new System.Exception(); }]@10");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "(string, int) M()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_ReturnType_Tuple_ElementDelete()
{
var src1 = "class C { (int, int, int a) M() { return (1, 2, 3); } }";
var src2 = "class C { (int, int) M() { return (1, 2); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [(int, int, int a) M() { return (1, 2, 3); }]@10 -> [(int, int) M() { return (1, 2); }]@10");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "(int, int) M()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_ReturnType_Tuple_ElementAdd()
{
var src1 = "class C { (int, int) M() { return (1, 2); } }";
var src2 = "class C { (int, int, int a) M() { return (1, 2, 3); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [(int, int) M() { return (1, 2); }]@10 -> [(int, int, int a) M() { return (1, 2, 3); }]@10");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "(int, int, int a) M()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update()
{
var src1 = @"
class C
{
static void F()
{
int a = 1;
int b = 2;
System.Console.WriteLine(a + b);
}
}
";
var src2 = @"
class C
{
static void F()
{
int b = 2;
int a = 1;
System.Console.WriteLine(a + b);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
@"Update [static void F()
{
int a = 1;
int b = 2;
System.Console.WriteLine(a + b);
}]@18 -> [static void F()
{
int b = 2;
int a = 1;
System.Console.WriteLine(a + b);
}]@18");
edits.VerifySemanticDiagnostics();
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: false)]);
}
[Fact]
public void MethodWithExpressionBody_Update()
{
var src1 = @"
class C
{
static int M() => F(1);
static int F(int a) => 1;
}
";
var src2 = @"
class C
{
static int M() => F(2);
static int F(int a) => 1;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
@"Update [static int M() => F(1);]@18 -> [static int M() => F(2);]@18");
edits.VerifySemanticDiagnostics();
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"), preserveLocalVariables: false)]);
}
[Fact]
public void MethodWithExpressionBody_ToBlockBody()
{
var src1 = "class C { static int F(int a) => 1; }";
var src2 = "class C { static int F(int a) { return 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [static int F(int a) => 1;]@10 -> [static int F(int a) { return 2; }]@10");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: false)
]);
}
[Fact]
public void MethodWithBlockBody_ToExpressionBody()
{
var src1 = "class C { static int F(int a) { return 2; } }";
var src2 = "class C { static int F(int a) => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [static int F(int a) { return 2; }]@10 -> [static int F(int a) => 1;]@10");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: false)
]);
}
[Fact]
public void MethodWithLambda_Update()
{
var src1 = @"
using System;
class C
{
static void F()
{
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
}
}
";
var src2 = @"
using System;
class C
{
static void F()
{
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
Console.WriteLine(1);
}
}";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), syntaxMap[0])]);
}
[Fact]
public void MethodUpdate_LocalVariableDeclaration()
{
var src1 = @"
class C
{
static void F()
{
int x = 1;
Console.WriteLine(x);
}
}
";
var src2 = @"
class C
{
static void F()
{
int x = 2;
Console.WriteLine(x);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
@"Update [static void F()
{
int x = 1;
Console.WriteLine(x);
}]@18 -> [static void F()
{
int x = 2;
Console.WriteLine(x);
}]@18");
}
[Fact]
public void Method_Delete()
{
var src1 = @"
class C
{
void goo() { }
}
";
var src2 = @"
class C
{
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [void goo() { }]@18",
"Delete [()]@26");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.goo"), deletedSymbolContainerProvider: c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory]
[InlineData("virtual")]
[InlineData("abstract")]
[InlineData("override")]
public void Method_Delete_Modifiers(string modifier)
{
/* TODO: https://github.com/dotnet/roslyn/issues/59264
This should be a supported edit. Consider the following inheritance chain:
public class C { public virtual void M() => Console.WriteLine("C"); }
public class D : C { public override void M() { base.M(); Console.WriteLine("D"); } }
public class E : D { public override void M() { base.M(); Console.WriteLine("E"); } }
If D.M is deleted we expect E.M to print "C E" and not throw.
*/
var src1 = $$"""
class C
{
{{modifier}} void goo() { }
}
""";
var src2 = """
class C
{
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
$"Delete [{modifier} void goo() {{ }}]@16",
$"Delete [()]@{25 + modifier.Length}");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "goo()")));
}
[Fact]
public void MethodWithExpressionBody_Delete()
{
var src1 = @"
class C
{
int goo() => 1;
}
";
var src2 = @"
class C
{
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [int goo() => 1;]@18",
"Delete [()]@25");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.goo"), deletedSymbolContainerProvider: c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754853")]
public void MethodDelete_WithParameterAndAttribute()
{
var src1 = @"
class C
{
[Obsolete]
void goo(int a) { }
}
";
var src2 = @"
class C
{
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
@"Delete [[Obsolete]
void goo(int a) { }]@18",
"Delete [(int a)]@42",
"Delete [int a]@43");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.goo"), deletedSymbolContainerProvider: c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754853")]
public void MethodDelete_PInvoke()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
class C
{
[DllImport(""msvcrt.dll"")]
public static extern int puts(string c);
}
";
var src2 = @"
using System;
using System.Runtime.InteropServices;
class C
{
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
@"Delete [[DllImport(""msvcrt.dll"")]
public static extern int puts(string c);]@74",
"Delete [(string c)]@134",
"Delete [string c]@135");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.method, "puts(string c)")));
}
[Fact]
public void MethodInsert_NotSupportedByRuntime()
{
var src1 = "class C { }";
var src2 = "class C { void goo() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "void goo()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void PrivateMethodInsert()
{
var src1 = @"
class C
{
static void F()
{
Console.ReadLine();
}
}";
var src2 = @"
class C
{
void goo() { }
static void F()
{
Console.ReadLine();
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [void goo() { }]@18",
"Insert [()]@26");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755784")]
public void PrivateMethodInsert_WithParameters()
{
var src1 = @"
using System;
class C
{
static void F()
{
Console.ReadLine();
}
}";
var src2 = @"
using System;
class C
{
void goo(int a) { }
static void F()
{
Console.ReadLine();
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [void goo(int a) { }]@35",
"Insert [(int a)]@43",
"Insert [int a]@44");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.goo"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755784")]
public void PrivateMethodInsert_WithAttribute()
{
var src1 = @"
class C
{
static void F()
{
Console.ReadLine();
}
}";
var src2 = @"
class C
{
[System.Obsolete]
void goo(int a) { }
static void F()
{
Console.ReadLine();
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
@"Insert [[System.Obsolete]
void goo(int a) { }]@18",
"Insert [(int a)]@49",
"Insert [int a]@50");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void MethodInsert_Virtual()
{
var src1 = @"
class C
{
}";
var src2 = @"
class C
{
public virtual void F() {}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertVirtual, "public virtual void F()", FeaturesResources.method));
}
[Fact]
public void MethodInsert_Abstract()
{
var src1 = @"
abstract class C
{
}";
var src2 = @"
abstract class C
{
public abstract void F();
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertVirtual, "public abstract void F()", FeaturesResources.method));
}
[Fact]
public void MethodInsert_Override()
{
var src1 = @"
class C
{
}";
var src2 = @"
class C
{
public override void F() { }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertVirtual, "public override void F()", FeaturesResources.method));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755784"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")]
public void ExternMethod_Insert()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
class C
{
}";
var src2 = @"
using System;
using System.Runtime.InteropServices;
class C
{
[DllImport(""msvcrt.dll"")]
private static extern int puts(string c);
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
@"Insert [[DllImport(""msvcrt.dll"")]
private static extern int puts(string c);]@74",
"Insert [(string c)]@135",
"Insert [string c]@136");
// CLR doesn't support methods without a body
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertExtern, "private static extern int puts(string c)", FeaturesResources.method));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755784"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")]
public void ExternMethod_DeleteInsert()
{
var srcA1 = @"
using System;
using System.Runtime.InteropServices;
class C
{
[DllImport(""msvcrt.dll"")]
private static extern int puts(string c);
}";
var srcA2 = @"
using System;
using System.Runtime.InteropServices;
";
var srcB1 = @"
using System;
using System.Runtime.InteropServices;
";
var srcB2 = @"
using System;
using System.Runtime.InteropServices;
class C
{
[DllImport(""msvcrt.dll"")]
private static extern int puts(string c);
}
";
// TODO: The method does not need to be updated since there are no sequence points generated for it.
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.puts")),
])
]);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755784"), WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")]
public void ExternMethod_Attribute_DeleteInsert()
{
var srcA1 = @"
using System;
using System.Runtime.InteropServices;
class C
{
[DllImport(""msvcrt.dll"")]
private static extern int puts(string c);
}";
var srcA2 = @"
using System;
using System.Runtime.InteropServices;
";
var srcB1 = @"
using System;
using System.Runtime.InteropServices;
";
var srcB2 = @"
using System;
using System.Runtime.InteropServices;
class C
{
[DllImport(""msvcrt.dll"")]
[Obsolete]
private static extern int puts(string c);
}
";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.puts")),
])
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Method_Reorder()
{
var src1 = """
class C
{
void F() {}
void G() {}
void H() {}
}
""";
var src2 = """
class C
{
void H() {}
void F() {}
void G() {}
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics([SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.H"))]);
}
[Theory]
[CombinatorialData]
public void Method_Reorder_Interface(bool isStatic)
{
var (modifier, body) = isStatic ? ("static", ";") : ("", " {}");
var src1 = $$"""
interface I
{
{{modifier}} void F(){{body}}
{{modifier}} void G(){{body}}
{{modifier}} void H(){{body}}
}
""";
var src2 = $$"""
interface I
{
{{modifier}} void H(){{body}}
{{modifier}} void F(){{body}}
{{modifier}} void G(){{body}}
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics([SemanticEdit(SemanticEditKind.Update, c => c.GetMember("I.H"))]);
}
[Theory]
[InlineData("static extern ")] // static method must be extern in COM interface
[InlineData("")]
public void Method_Reorder_Interface_ComImport(string modifier)
{
var src1 = $$"""
using System.Runtime.InteropServices;
[ComImport]
[Guid("DBE8A85B-7883-4657-83FD-1FA27A9561EB")]
interface I
{
{{modifier}}void F();
{{modifier}}void G();
{{modifier}}void H();
}
""";
var src2 = $$"""
using System.Runtime.InteropServices;
[ComImport]
[Guid("DBE8A85B-7883-4657-83FD-1FA27A9561EB")]
interface I
{
{{modifier}}void H();
{{modifier}}void F();
{{modifier}}void G();
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveComInterfaceMember, modifier + "void H()", GetResource("method")));
}
[Fact]
public void Method_Insert_Interface_ComImport()
{
var src1 = $$"""
using System.Runtime.InteropServices;
[ComImport]
[Guid("DBE8A85B-7883-4657-83FD-1FA27A9561EB")]
interface I
{
}
""";
var src2 = $$"""
using System.Runtime.InteropServices;
[ComImport]
[Guid("DBE8A85B-7883-4657-83FD-1FA27A9561EB")]
interface I
{
void F();
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertVirtual, "void F()", GetResource("method")),
Diagnostic(RudeEditKind.InsertOrMoveComInterfaceMember, "void F()", GetResource("method")));
}
[Fact]
public void Method_Insert_Interface_ComImport_Static()
{
var src1 = $$"""
[System.Runtime.InteropServices.ComImport]
[Guid("DBE8A85B-7883-4657-83FD-1FA27A9561EB")]
interface I
{
}
""";
var src2 = $$"""
[System.Runtime.InteropServices.ComImport]
[Guid("DBE8A85B-7883-4657-83FD-1FA27A9561EB")]
interface I
{
extern static void F();
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertExtern, "extern static void F()", GetResource("method")),
Diagnostic(RudeEditKind.InsertOrMoveComInterfaceMember, "extern static void F()", GetResource("method")));
}
[Fact]
public void Method_InsertDelete()
{
var src1 = "class C { class D { } void f(int a, int b) { a = b; } }";
var src2 = "class C { class D { void f(int a, int b) { a = b; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [void f(int a, int b) { a = b; }]@20",
"Insert [(int a, int b)]@26",
"Insert [int a]@27",
"Insert [int b]@34",
"Delete [void f(int a, int b) { a = b; }]@22",
"Delete [(int a, int b)]@28",
"Delete [int a]@29",
"Delete [int b]@36");
}
[Fact]
public void Method_Update_Parameter_Insert()
{
var src1 = @"
class C
{
static void F()
{
}
}";
var src2 = @"
class C
{
static void F(int a)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [int a]@32");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.F"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int a", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_Parameter_Insert_Multiple()
{
var src1 = @"
class C
{
void M(int a)
{
}
}";
var src2 = @"
class C
{
void M(int a, int b, int c)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int b", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_Parameter_Insert_Partial()
{
var src1 = @"
class C
{
partial void M(int a);
partial void M(int a)
{
}
}";
var src2 = @"
class C
{
partial void M(int a, int/*1*/b, int c);
partial void M(int a, int/*2*/b, int c)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.M").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.M").PartialImplementationPart, partialType: "C")
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int/*1*/b", GetResource("method")),
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int/*2*/b", GetResource("method"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_Type()
{
var src1 = @"
class C
{
static void Main(bool x)
{
}
}";
var src2 = @"
class C
{
static void Main(int x)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [bool x]@35 -> [int x]@35");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Boolean))?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Int32))?.ISymbol)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int x", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_Parameter_Type_WithRename()
{
var src1 = @"
class C
{
static void Main(bool someBool)
{
}
}";
var src2 = @"
class C
{
static void Main(int someInt)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [bool someBool]@35 -> [int someInt]@35");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Boolean))?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.Main").FirstOrDefault(m => m.GetParameterTypes().Any(t => t.SpecialType == SpecialType.System_Int32))?.ISymbol)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Method_Update_Parameter_Delete()
{
var src1 = @"
class C
{
static void F(int a)
{
}
}";
var src2 = @"
class C
{
static void F()
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [int a]@32");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.F"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "static void F()", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_Parameter_Delete_Multiple()
{
var src1 = @"
class C
{
void M(int a, int b, int c)
{
}
}";
var src2 = @"
class C
{
void M(int a)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "void M(int a)", GetResource("method"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Update_Parameter_Rename()
{
var src1 = @"
class C
{
static void F(int a)
{
}
}";
var src2 = @"
class C
{
static void F(int b)
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a]@32 -> [int b]@32");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
}
[Fact]
public void Method_Update_Parameter_Rename_WithBodyUpdate()
{
var src1 = @"
class C
{
static void F(int a)
{
}
}";
var src2 = @"
class C
{
static void F(int b)
{
System.Console.Write(1);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
}
[Fact]
public void Method_Rename()
{
var src1 = @"
class C
{
static void F(int a)
{
}
}";
var src2 = @"
class C
{
static void G(int a)
{
}
}";
var edits = GetTopEdits(src1, src2);
var expectedEdit = @"Update [static void F(int a)
{
}]@18 -> [static void G(int a)
{
}]@18";
edits.VerifyEdits(expectedEdit);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.G"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "static void G(int a)", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_Rename_GenericType()
{
var src1 = @"
class C<T>
{
static void F()
{
}
}";
var src2 = @"
class C<T>
{
static void G()
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "static void G()", FeaturesResources.method)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "static void G()", FeaturesResources.method)
],
capabilities:
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.GenericAddMethodToExistingType);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.G"))
],
capabilities:
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.GenericAddMethodToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void Method_Rename_GenericMethod()
{
var src1 = @"
class C
{
static void F<T>()
{
}
}";
var src2 = @"
class C
{
static void G<T>()
{
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "static void G<T>()", GetResource("method"))
],
capabilities:
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "static void G<T>()", GetResource("method"))
],
capabilities:
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.GenericAddMethodToExistingType);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.G"))
],
capabilities:
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.GenericAddMethodToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod);
}
[Theory]
[InlineData("virtual")]
[InlineData("abstract")]
[InlineData("override")]
public void Method_Rename_Modifiers(string modifier)
{
/* TODO: https://github.com/dotnet/roslyn/issues/59264
This should be a supported edit. Consider the following inheritance chain:
public class C { public virtual void M() => Console.WriteLine("C"); }
public class D : C { public override void M() { base.M(); Console.WriteLine("D"); } }
public class E : D { public override void M() { base.M(); Console.WriteLine("E"); } }
If D.M is deleted we expect E.M to print "C E" and not throw.
*/
var src1 = $$"""
class C
{
{{modifier}} void goo() { }
}
""";
var src2 = $$"""
class C
{
{{modifier}} void boo() { }
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
$"Update [{modifier} void goo() {{ }}]@16 -> [{modifier} void boo() {{ }}]@16");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, $"{modifier} void boo()", GetResource("method", "goo()")));
}
[Fact]
public void MethodUpdate_AsyncMethod0()
{
var src1 = @"
class C
{
public async Task F()
{
await Task.Delay(1000);
}
}";
var src2 = @"
class C
{
public async Task F()
{
await Task.Delay(500);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.UpdatingStateMachineMethodNotSupportedByRuntime, "public async Task F()")],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_AsyncMethod1()
{
var src1 = """
class Test
{
static void F()
{
Test f = new Test();
string result = f.WaitAsync().Result;
}
public async Task<string> WaitAsync()
{
await Task.Delay(1000);
return "Done";
}
}
""";
var src2 = """
class Test
{
static void F()
{
Test f = new Test();
string result = f.WaitAsync().Result;
}
public async Task<string> WaitAsync()
{
await Task.Delay(1000);
return "Not Done";
}
}
""";
var edits = GetTopEdits(src1, src2);
var expectedEdit = """
Update [public async Task<string> WaitAsync()
{
await Task.Delay(1000);
return "Done";
}]@133 -> [public async Task<string> WaitAsync()
{
await Task.Delay(1000);
return "Not Done";
}]@133
""";
edits.VerifyEdits(expectedEdit);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void MethodUpdate_AsyncMethod_Generic()
{
var src1 = @"
class C
{
public async Task F<T>()
{
await Task.FromResult(1);
}
}";
var src2 = @"
class C
{
public async Task F<T>()
{
await Task.FromResult(2);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities:
EditAndContinueCapabilities.AddInstanceFieldToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod |
EditAndContinueCapabilities.GenericAddFieldToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingStateMachineMethodNotSupportedByRuntime, "public async Task F<T>()"),
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "public async Task F<T>()", GetResource("method")),
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "public async Task F<T>()", GetResource("method"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_AddReturnTypeAttribute()
{
var src1 = @"
using System;
class Test
{
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[return: Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(@"Update [static void F()
{
System.Console.Write(5);
}]@38 -> [[return: Obsolete]
static void F()
{
System.Console.Write(5);
}]@38");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_AddAttribute()
{
var src1 = @"
using System;
class Test
{
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(@"Update [static void F()
{
System.Console.Write(5);
}]@38 -> [[Obsolete]
static void F()
{
System.Console.Write(5);
}]@38");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_AttributeWithTypeAtConstructor()
{
var src1 = "using System; [AttributeUsage(AttributeTargets.All)] public class AAttribute : Attribute { public AAttribute(Type t) { } } class C { [A(typeof(C))] public void M() { Console.WriteLine(\"2\"); } }";
var src2 = "using System; [AttributeUsage(AttributeTargets.All)] public class AAttribute : Attribute { public AAttribute(Type t) { } } class C { [A(typeof(C))] public void M() { Console.WriteLine(\"1\"); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(@"Update [[A(typeof(C))] public void M() { Console.WriteLine(""2""); }]@133 -> [[A(typeof(C))] public void M() { Console.WriteLine(""1""); }]@133");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_AttributeWithTypeAtConstructor2()
{
var src1 = "using System; [AttributeUsage(AttributeTargets.All)] public class AAttribute : Attribute { public AAttribute(Type t) { } } class C { [A(typeof(object))] public void M() { Console.WriteLine(\"2\"); } }";
var src2 = "using System; [AttributeUsage(AttributeTargets.All)] public class AAttribute : Attribute { public AAttribute(Type t) { } } class C { [A(typeof(dynamic))] public void M() { Console.WriteLine(\"1\"); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(@"Update [[A(typeof(object))] public void M() { Console.WriteLine(""2""); }]@133 -> [[A(typeof(dynamic))] public void M() { Console.WriteLine(""1""); }]@133");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_AddAttribute_SupportedByRuntime()
{
var src1 = """
using System;
class Test
{
static void F()
{
System.Console.Write(5);
}
}
""";
var src2 = """
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("""
Update [static void F()
{
System.Console.Write(5);
}]@36 -> [[Obsolete]
static void F()
{
System.Console.Write(5);
}]@36
""");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Test.F"))],
capabilities: 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,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))],
capabilities: 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,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))]);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_AddAttribute2()
{
var src1 = @"
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[Obsolete, STAThread]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_AddAttribute3()
{
var src1 = @"
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[Obsolete]
[STAThread]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_AddAttribute4()
{
var src1 = @"
using System;
class Test
{
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[Obsolete, STAThread]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_UpdateAttribute()
{
var src1 = @"
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[Obsolete("""")]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/754853")]
public void MethodUpdate_DeleteAttribute()
{
var src1 = @"
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_DeleteAttribute2()
{
var src1 = @"
using System;
class Test
{
[Obsolete, STAThread]
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_DeleteAttribute3()
{
var src1 = @"
using System;
class Test
{
[Obsolete]
[STAThread]
static void F()
{
System.Console.Write(5);
}
}";
var src2 = @"
using System;
class Test
{
[Obsolete]
static void F()
{
System.Console.Write(5);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "static void F()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_ExplicitlyImplemented1()
{
var src1 = @"
class C : I, J
{
void I.Goo() { Console.WriteLine(2); }
void J.Goo() { Console.WriteLine(1); }
}";
var src2 = @"
class C : I, J
{
void I.Goo() { Console.WriteLine(1); }
void J.Goo() { Console.WriteLine(2); }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [void I.Goo() { Console.WriteLine(2); }]@25 -> [void I.Goo() { Console.WriteLine(1); }]@25",
"Update [void J.Goo() { Console.WriteLine(1); }]@69 -> [void J.Goo() { Console.WriteLine(2); }]@69");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_ExplicitlyImplemented2()
{
var interfaces = @"
interface I { void Goo(); }
interface J { void Goo(); }
";
var src1 = @"
class C : I, J
{
void I.Goo() { Console.WriteLine(1); }
void J.Goo() { Console.WriteLine(2); }
}
" + interfaces;
var src2 = @"
class C : I, J
{
void Goo() { Console.WriteLine(1); }
void J.Goo() { Console.WriteLine(2); }
}
" + interfaces;
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [void I.Goo() { Console.WriteLine(1); }]@25 -> [void Goo() { Console.WriteLine(1); }]@25");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "void Goo()", GetResource("method", "I.Goo()")));
}
[Fact]
public void MethodUpdate_StackAlloc_Update()
{
var src1 = @"
class C
{
static void Main()
{
int i = 1;
unsafe
{
char* buffer = stackalloc char[16];
int* px2 = &i;
}
}
}";
var src2 = @"
class C
{
static void Main()
{
int i = 2;
unsafe
{
char* buffer = stackalloc char[16];
int* px2 = &i;
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc char[16]", FeaturesResources.method),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "static void Main()", GetResource("method"))
]);
}
[Fact]
public void MethodUpdate_StackAlloc_Insert()
{
var src1 = @"
class C
{
static void F()
{
int i = 10;
unsafe
{
int* px2 = &i;
}
}
}";
var src2 = @"
class C
{
static void F()
{
int i = 10;
unsafe
{
char* buffer = stackalloc char[16];
int* px2 = &i;
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc char[16]", FeaturesResources.method));
}
[Fact]
public void MethodUpdate_StackAlloc_Delete()
{
var src1 = @"
class C
{
static void F()
{
int i = 10;
unsafe
{
char* buffer = stackalloc char[16];
int* px2 = &i;
}
}
}";
var src2 = @"
class C
{
static void F()
{
int i = 10;
unsafe
{
int* px2 = &i;
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "static void F()", FeaturesResources.method));
}
[Fact]
public void MethodUpdate_SwitchExpressionInLambda1()
{
var src1 = "class C { void M() { F(1, a => a switch { 0 => 0, _ => 2 }); } }";
var src2 = "class C { void M() { F(2, a => a switch { 0 => 0, _ => 2 }); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_SwitchExpressionInLambda2()
{
var src1 = "class C { void M() { F(1, a => a switch { 0 => 0, _ => 2 }); } }";
var src2 = "class C { void M() { F(2, a => a switch { 0 => 0, _ => 2 }); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_SwitchExpressionInAnonymousMethod()
{
var src1 = "class C { void M() { F(1, delegate(int a) { return a switch { 0 => 0, _ => 2 }; }); } }";
var src2 = "class C { void M() { F(2, delegate(int a) { return a switch { 0 => 0, _ => 2 }; }); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_SwitchExpressionInLocalFunction()
{
var src1 = "class C { void M() { int f(int a) => a switch { 0 => 0, _ => 2 }; f(1); } }";
var src2 = "class C { void M() { int f(int a) => a switch { 0 => 0, _ => 2 }; f(2); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_SwitchExpressionInQuery()
{
var src1 = "class C { void M() { var x = from z in new[] { 1, 2, 3 } where z switch { 0 => true, _ => false } select z + 1; } }";
var src2 = "class C { void M() { var x = from z in new[] { 1, 2, 3 } where z switch { 0 => true, _ => false } select z + 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_UpdateAnonymousMethod()
{
var src1 = "class C { void M() { F(1, delegate(int a) { return a; }); } }";
var src2 = "class C { void M() { F(2, delegate(int a) { return a; }); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodWithExpressionBody_Update_UpdateAnonymousMethod()
{
var src1 = "class C { void M() => F(1, delegate(int a) { return a; }); }";
var src2 = "class C { void M() => F(2, delegate(int a) { return a; }); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_AnonymousType()
{
var src1 = "class C { void M() { F(1, new { A = 1, B = 2 }); } }";
var src2 = "class C { void M() { F(2, new { A = 1, B = 2 }); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodWithExpressionBody_Update_AnonymousType()
{
var src1 = "class C { void M() => F(new { A = 1, B = 2 }); }";
var src2 = "class C { void M() => F(new { A = 10, B = 20 }); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_Iterator_YieldReturn()
{
var src1 = "class C { IEnumerable<int> M() { yield return 1; } }";
var src2 = "class C { IEnumerable<int> M() { yield return 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
VerifyPreserveLocalVariables(edits, preserveLocalVariables: true);
}
[Fact]
public void MethodUpdate_AddYieldReturn()
{
var src1 = "class C { IEnumerable<int> M() { return new[] { 1, 2, 3}; } }";
var src2 = "class C { IEnumerable<int> M() { yield return 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.NewTypeDefinition | EditAndContinueCapabilities.AddExplicitInterfaceImplementation);
VerifyPreserveLocalVariables(edits, preserveLocalVariables: false);
}
[Fact]
public void MethodUpdate_AddYieldReturn_NotSupported()
{
var src1 = "class C { IEnumerable<int> M() { return new[] { 1, 2, 3}; } }";
var src2 = "class C { IEnumerable<int> M() { yield return 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.MakeMethodIteratorNotSupportedByRuntime, "IEnumerable<int> M()")],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodUpdate_Iterator_YieldBreak()
{
var src1 = "class C { IEnumerable<int> M() { F(); yield break; } }";
var src2 = "class C { IEnumerable<int> M() { G(); yield break; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
VerifyPreserveLocalVariables(edits, preserveLocalVariables: false);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1087305")]
public void MethodUpdate_LabeledStatement()
{
var src1 = @"
class C
{
static void F()
{
goto Label1;
Label1:
{
Console.WriteLine(1);
}
}
}";
var src2 = @"
class C
{
static void F()
{
goto Label1;
Label1:
{
Console.WriteLine(2);
}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MethodUpdate_LocalFunctionsParameterRefnessInBody()
{
var src1 = @"class C { public void M(int a) { void f(ref int b) => b = 1; } }";
var src2 = @"class C { public void M(int a) { void f(out int b) => b = 1; } } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public void M(int a) { void f(ref int b) => b = 1; }]@10 -> [public void M(int a) { void f(out int b) => b = 1; }]@10");
}
[Fact]
public void MethodUpdate_LambdaParameterRefnessInBody()
{
var src1 = @"class C { public void M(int a) { f((ref int b) => b = 1); } }";
var src2 = @"class C { public void M(int a) { f((out int b) => b = 1); } } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public void M(int a) { f((ref int b) => b = 1); }]@10 -> [public void M(int a) { f((out int b) => b = 1); }]@10");
}
[Fact]
public void Method_ReadOnlyRef_Parameter_InsertWhole()
{
var src1 = "class Test { }";
var src2 = "class Test { int M(in int b) => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [int M(in int b) => throw null;]@13",
"Insert [(in int b)]@18",
"Insert [in int b]@19");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Method_ReadOnlyRef_Parameter_InsertParameter()
{
var src1 = "class C { int M() => throw null; }";
var src2 = "class C { int M(in int b) => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [in int b]@16");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "in int b", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_ReadOnlyRef_Parameter_Update()
{
var src1 = "class C { int M(int b) => throw null; }";
var src2 = "class C { int M(in int b) => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int b]@16 -> [in int b]@16");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Method_ReadOnlyRef_ReturnType_Insert()
{
var src1 = "class Test { }";
var src2 = "class Test { ref readonly int M() => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [ref readonly int M() => throw null;]@13",
"Insert [()]@31");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Method_ReadOnlyRef_ReturnType_Update()
{
var src1 = "class Test { int M() => throw null; }";
var src2 = "class Test { ref readonly int M() => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int M() => throw null;]@13 -> [ref readonly int M() => throw null;]@13");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("Test.M"), deletedSymbolContainerProvider: c => c.GetMember("Test")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("Test.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "ref readonly int M()", FeaturesResources.method)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Method_ImplementingInterface_Add()
{
var src1 = @"
using System;
public interface ISample
{
string Get();
}
public interface IConflict
{
string Get();
}
public class BaseClass : ISample
{
public virtual string Get() => string.Empty;
}
public class SubClass : BaseClass, IConflict
{
public override string Get() => string.Empty;
}
";
var src2 = @"
using System;
public interface ISample
{
string Get();
}
public interface IConflict
{
string Get();
}
public class BaseClass : ISample
{
public virtual string Get() => string.Empty;
}
public class SubClass : BaseClass, IConflict
{
public override string Get() => string.Empty;
string IConflict.Get() => String.Empty;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [string IConflict.Get() => String.Empty;]@325",
"Insert [()]@345");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertMethodWithExplicitInterfaceSpecifier, "string IConflict.Get()", FeaturesResources.method));
}
[Fact]
public void Method_Partial_DeleteInsert_DefinitionPart()
{
var srcA1 = "partial class C { partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcC1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { partial void F() { } }";
var srcC2 = "partial class C { partial void F(); }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
]);
}
[Fact]
public void Method_Partial_DeleteInsert_ImplementationPart()
{
var srcA1 = "partial class C { partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcC1 = "partial class C { }";
var srcA2 = "partial class C { partial void F(); }";
var srcB2 = "partial class C { }";
var srcC2 = "partial class C { partial void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/51011")]
public void Method_Partial_Swap_ImplementationAndDefinitionParts()
{
var srcA1 = "partial class C { partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcA2 = "partial class C { partial void F() { } }";
var srcB2 = "partial class C { partial void F(); }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
]);
}
[Fact]
public void Method_Partial_DeleteImplementation()
{
var srcA1 = "partial class C { partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcA2 = "partial class C { partial void F(); }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C")
]),
]);
}
[Fact]
public void Method_Partial_DeleteBoth()
{
var srcA1 = "partial class C { partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C")]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C")]),
]);
}
[Fact]
public void Method_Partial_DeleteInsertBoth()
{
var srcA1 = "partial class C { partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcC1 = "partial class C { }";
var srcD1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { }";
var srcC2 = "partial class C { partial void F(); }";
var srcD2 = "partial class C { partial void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")])
]);
}
[Fact]
public void Method_Partial_Insert()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { partial void F(); }";
var srcB2 = "partial class C { partial void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart)]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Method_Partial_Insert_Reloadable()
{
var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { partial void F(); }";
var srcB2 = "partial class C { partial void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Method_Partial_Update_Attribute_Definition()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var srcA1 = attribute +
"partial class C { [A(1)]partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcA2 = attribute +
"partial class C { [A(2)]partial void F(); }";
var srcB2 = "partial class C { partial void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Method_Partial_Update_Attribute_Implementation()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var srcA1 = attribute +
"partial class C { partial void F(); }";
var srcB1 = "partial class C { [A(1)]partial void F() { } }";
var srcA2 = attribute +
"partial class C { partial void F(); }";
var srcB2 = "partial class C { [A(2)]partial void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Method_Partial_Update_Attribute_DefinitionAndImplementation()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var srcA1 = attribute +
"partial class C { [A(1)]partial void F(); }";
var srcB1 = "partial class C { [A(1)]partial void F() { } }";
var srcA2 = attribute +
"partial class C { [A(2)]partial void F(); }";
var srcB2 = "partial class C { [A(2)]partial void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Method_Partial_DeleteInsert_DefinitionWithAttributeChange()
{
var attribute = """
public class A : System.Attribute {}
""";
var srcA1 = attribute +
"partial class C { [A]partial void F(); }";
var srcB1 = "partial class C { partial void F() { } }";
var srcA2 = attribute +
"partial class C { }";
var srcB2 = "partial class C { partial void F() { } partial void F(); }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Method_Partial_Parameter_TypeChange()
{
var srcA1 = "partial class C { partial void F(long x); }";
var srcB1 = "partial class C { partial void F(long x) { } }";
var srcA2 = "partial class C { partial void F(byte x); }";
var srcB2 = "partial class C { partial void F(byte x) { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.F").PartialImplementationPart, partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "byte x", GetResource("method"))]),
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "byte x", GetResource("method"))]),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
#endregion
#region Operators
[Theory]
[InlineData("implicit", "explicit")]
[InlineData("explicit", "implicit")]
public void Operator_Modifiers_Update(string oldModifiers, string newModifiers)
{
var src1 = "class C { public static " + oldModifiers + " operator int (C c) => 0; }";
var src2 = "class C { public static " + newModifiers + " operator int (C c) => 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [public static " + oldModifiers + " operator int (C c) => 0;]@10 -> [public static " + newModifiers + " operator int (C c) => 0;]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public static " + newModifiers + " operator int (C c)", CSharpFeaturesResources.conversion_operator));
}
[Fact]
public void Operator_Modifiers_Update_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { public static implicit operator int (C c) => 0; }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { public static explicit operator int (C c) => 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Operator_Conversion_ExternModifiers_Add()
{
var src1 = "class C { public static implicit operator bool (C c) => default; }";
var src2 = "class C { extern public static implicit operator bool (C c); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "extern public static implicit operator bool (C c)", CSharpFeaturesResources.conversion_operator));
}
[Fact]
public void Operator_Conversion_ExternModifiers_Remove()
{
var src1 = "class C { extern public static implicit operator bool (C c); }";
var src2 = "class C { public static implicit operator bool (C c) => default; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public static implicit operator bool (C c)", CSharpFeaturesResources.conversion_operator));
}
[Fact]
public void OperatorInsert()
{
var src1 = @"
class C
{
}
";
var src2 = @"
class C
{
public static implicit operator bool (C c)
{
return false;
}
public static C operator +(C c, C d)
{
return c;
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOperator, "public static implicit operator bool (C c)", CSharpFeaturesResources.conversion_operator),
Diagnostic(RudeEditKind.InsertOperator, "public static C operator +(C c, C d)", FeaturesResources.operator_));
}
[Fact]
public void OperatorDelete()
{
var src1 = @"
class C
{
public static implicit operator bool (C c)
{
return false;
}
public static C operator +(C c, C d)
{
return c;
}
}
";
var src2 = @"
class C
{
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.op_Implicit"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.op_Addition"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void OperatorInsertDelete()
{
var srcA1 = @"
partial class C
{
public static implicit operator bool (C c) => false;
}
";
var srcB1 = @"
partial class C
{
public static C operator +(C c, C d) => c;
}
";
var srcA2 = srcB1;
var srcB2 = srcA1;
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("op_Addition"))
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember("op_Implicit"))
]),
]);
}
[Fact]
public void OperatorUpdate()
{
var src1 = @"
class C
{
public static implicit operator bool (C c)
{
return false;
}
public static C operator +(C c, C d)
{
return c;
}
}
";
var src2 = @"
class C
{
public static implicit operator bool (C c)
{
return true;
}
public static C operator +(C c, C d)
{
return d;
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.op_Implicit")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.op_Addition")),
]);
}
[Fact]
public void OperatorWithExpressionBody_Update()
{
var src1 = @"
class C
{
public static implicit operator bool (C c) => false;
public static C operator +(C c, C d) => c;
}
";
var src2 = @"
class C
{
public static implicit operator bool (C c) => true;
public static C operator +(C c, C d) => d;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.op_Implicit")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.op_Addition")),
]);
}
[Fact]
public void OperatorWithExpressionBody_ToBlockBody()
{
var src1 = "class C { public static C operator +(C c, C d) => d; }";
var src2 = "class C { public static C operator +(C c, C d) { return c; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [public static C operator +(C c, C d) => d;]@10 -> [public static C operator +(C c, C d) { return c; }]@10");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.op_Addition"))
]);
}
[Fact]
public void OperatorWithBlockBody_ToExpressionBody()
{
var src1 = "class C { public static C operator +(C c, C d) { return c; } }";
var src2 = "class C { public static C operator +(C c, C d) => d; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [public static C operator +(C c, C d) { return c; }]@10 -> [public static C operator +(C c, C d) => d;]@10");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.op_Addition"))
]);
}
[Fact]
public void Operator_Rename()
{
var src1 = @"
class C
{
public static C operator +(C c, C d) { return c; }
}
";
var src2 = @"
class C
{
public static C operator -(C c, C d) { return d; }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.op_Addition"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.op_Subtraction"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void OperatorReorder1()
{
var src1 = @"
class C
{
public static implicit operator bool (C c) { return false; }
public static implicit operator int (C c) { return 1; }
}
";
var src2 = @"
class C
{
public static implicit operator int (C c) { return 1; }
public static implicit operator bool (C c) { return false; }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [public static implicit operator int (C c) { return 1; }]@84 -> @18");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void OperatorReorder2()
{
var src1 = @"
class C
{
public static C operator +(C c, C d) { return c; }
public static C operator -(C c, C d) { return d; }
}
";
var src2 = @"
class C
{
public static C operator -(C c, C d) { return d; }
public static C operator +(C c, C d) { return c; }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [public static C operator -(C c, C d) { return d; }]@74 -> @18");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Operator_ReadOnlyRef_Parameter_InsertWhole()
{
var src1 = "class Test { }";
var src2 = "class Test { public static bool operator !(in Test b) => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [public static bool operator !(in Test b) => throw null;]@13",
"Insert [(in Test b)]@42",
"Insert [in Test b]@43");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOperator, "public static bool operator !(in Test b)", FeaturesResources.operator_));
}
[Fact]
public void Operator_Delete()
{
var src1 = "class C { public static bool operator !(C b) => true; }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.op_LogicalNot"), deletedSymbolContainerProvider: c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
#endregion
#region Constructor
[Fact]
public void Constructor_Parameter_AddAttribute()
{
var src1 = "class C { public C(int a) { } }";
var src2 = "class C { public C([System.Obsolete]int a) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C..ctor"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Constructor_Parameter_AddAttribute_Primary()
{
var src1 = "class C(int a);";
var src2 = "class C([System.Obsolete] int a);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C..ctor"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Constructor_Parameter_Update_Attribute_Record_ParamTarget()
{
var src1 = "record C([param: A(1)] int P);" + s_attributeSource;
var src2 = "record C([param: A(2)] int P);" + s_attributeSource;
var edits = GetTopEdits(src1, src2);
// Attribute is only applied to the parameter.
// Currently we don't filter the property update out.
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Constructor_Parameter_Update_Attribute_Record_PropertyTarget()
{
var src1 = "record C([property: A(1)] int P);" + s_attributeSource;
var src2 = "record C([property: A(2)] int P);" + s_attributeSource;
var edits = GetTopEdits(src1, src2);
// Attribute is only applied to the parameter.
// Currently we don't filter the constructor update out.
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("record")]
[InlineData("record struct")]
public void Constructor_Parameter_DefaultValue_Primary(string keyword)
{
var src1 = keyword + " C(int X = 1) : D { }";
var src2 = keyword + " C(int X = 2) : D { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, "int X = 2", GetResource("parameter")));
}
[Theory(Skip = "https://github.com/dotnet/roslyn/issues/68458")]
[InlineData("field")]
[InlineData("property")]
[WorkItem("https://github.com/dotnet/roslyn/issues/68458")]
public void Constructor_Parameter_AddAttribute_Record_NonParamTargets(string target)
{
var src1 = "record C(int P);" + s_attributeSource;
var src2 = "record C([" + target + ": A]int P);" + s_attributeSource;
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68458")]
public void Constructor_Parameter_AddAttribute_Record_ReplacingSynthesizedWithCustomProperty()
{
var src1 = "record C(int P) { }" + s_attributeSource;
var src2 = "record C([property: A][field: A][param: A]int P) { public int P { get; init; } }" + s_attributeSource;
var edits = GetTopEdits(src1, src2);
// We update more members than strictly necessary to avoid more complex analysis.
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
// TOOD: Should include update of P: https://github.com/dotnet/roslyn/issues/68458
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Constructor_Parameter_AddAttribute_Record_ReplacingCustomPropertyWithSynthesized()
{
var src1 = "record C(int P) { public int P { get; init; } }" + s_attributeSource;
var src2 = "record C([property: A][field: A][param: A]int P) {} " + s_attributeSource;
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Theory, CombinatorialData]
public void Constructor_Parameter_Update_TypeOrRefKind_RuntimeTypeChanged(
[CombinatorialValues("int", "in byte", "ref byte", "out byte", "ref readonly byte")] string type,
bool direction)
{
var (oldType, newType) = direction ? (type, "byte") : ("byte", type);
var src1 = "class C { C(" + oldType + " a) => throw null!; }";
var src2 = "class C { C(" + newType + " a) => throw null!; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single())
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, newType + " a", GetResource("constructor"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Parameter_Update_Type_Primary()
{
var src1 = @"class C(bool x);";
var src2 = @"class C(int x);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int x", GetResource("constructor"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Parameter_Update_Type_Primary_PartialMove()
{
var srcA1 = "partial class C(bool a);";
var srcB1 = "partial class C;";
var srcA2 = "partial class C;";
var srcB2 = "partial class C(int a);";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Update_Type_Record()
{
var src1 = @"record C(bool x);";
var src2 = @"record C(int x);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.x"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_x"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_x"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.x")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_x")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_x")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int x", GetResource("auto-property")),
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int x", GetResource("constructor"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Parameter_Update_Type_Record_TypeLayout()
{
var src1 = @"record struct C(bool x);";
var src2 = @"record struct C(int x);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertOrMoveStructMember, "int x", GetResource("auto-property"), GetResource("record struct"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Update_Type_ReplacingClassWithRecord()
{
var src1 = @"class C(bool x);";
var src2 = @"record C(int x);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.TypeKindUpdate, "record C")
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete()
{
var src1 = "class C { C(int x, int y) { } }";
var src2 = "class C { C(int x) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single()),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary()
{
var src1 = "class C(int x, int y) { }";
var src2 = "class C(int x) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record()
{
var src1 = "record C(int X, int Y);";
var src2 = "record C(int X);";
var edits = GetTopEdits(src1, src2);
// Note: We do not report rude edits when deleting auto-properties of a type with a sequential or explicit layout.
// The properties are updated to throw and the backing field remains in the type.
// The deleted field will remain unused since adding the property back is a rude edit.
edits.VerifySemantics(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_LayoutClass()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
record C(int X, int Y);
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
record C(int X);
";
var edits = GetTopEdits(src1, src2);
// Note: We do not report rude edits when deleting auto-properties of a type with a sequential or explicit layout.
// The properties are updated to throw and the backing field remains in the type.
// The deleted field will remain unused since adding the property back is a rude edit.
edits.VerifySemantics(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_Struct()
{
var src1 = "record struct C(int X, int Y);";
var src2 = "record struct C(int X);";
var edits = GetTopEdits(src1, src2);
// Note: We do not report rude edits when deleting auto-properties of a type with a sequential or explicit layout.
// The properties are updated to throw and the backing field remains in the type.
// The deleted field will remain unused since adding the property back is a rude edit.
edits.VerifySemantics(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_ReplacingSynthesizedWithCustomProperty()
{
var src1 = "record C(int X, int Y) { }";
var src2 = "record C(int X) { public int Y { get; init; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Y")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Y")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomProperty_WithUpdate()
{
var src1 = "record C(int X, int Y) { public int Y { get; init; } }";
var src2 = "record C(int X ) { [Obsolete]public int Y { get; init; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomProperty_WithAccessorUpdate()
{
var src1 = "record C(int X, int Y) { public int Y { get => new System.Func<int>(<N:0.0>() => 1</N:0.0>).Invoke(); init { } } }";
var src2 = "record C(int X ) { public int Y { get => new System.Func<int>(<N:0.0>() => 2</N:0.0>).Invoke(); init { } } }";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Y"), syntaxMap: syntaxMap[0]),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomField_WithUpdate()
{
var src1 = "record C(int X, int Y) { public int Y; }";
var src2 = "record C(int X ) { [Obsolete]public int Y; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomPropertyDelete()
{
var src1 = "record C(int X, int Y) { public int Y { get => 0; init {} } }";
var src2 = "record C(int X ) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomDeconstructor_NoUpdate()
{
var src1 = "record C(int X, int Y) { public void Deconstruct(out int X) => X = 1; }";
var src2 = "record C(int X ) { public void Deconstruct(out int X) => X = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomDeconstructor_Update()
{
var src1 = "record C(int X, int Y) { public void Deconstruct(out int X) => X = 1; }";
var src2 = "record C(int X ) { public void Deconstruct(out int X) => X = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IMethodSymbol>("C.Deconstruct").Single(m => m.Parameters is [_])),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomDeconstructor_Insert()
{
var src1 = "record C(int X, int Y) { }";
var src2 = "record C(int X ) { public void Deconstruct(out int X) => X = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_Record_WithCustomDeconstructor_Delete()
{
var src1 = "record C(int X, int Y) { public void Deconstruct(out int X, out int Y) => X = Y = 1; }";
var src2 = "record C(int X ) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Y"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_LayoutClass_NotCaptured()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x)
{
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Delete_Primary_LayoutClass_Captured()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
public int M() => x + y;
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x)
{
public int M() => x;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.NotCapturingPrimaryConstructorParameter, "M", GetResource("class with explicit or sequential layout"), "y"));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/743552")]
public void Constructor_Parameter_Insert()
{
var src1 = "class C { public C(int a) { } }";
var src2 = "class C { public C(int a, int b) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [(int a)]@18 -> [(int a, int b)]@18",
"Insert [int b]@26");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single())
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int b", GetResource("constructor"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory]
[InlineData("class ")]
[InlineData("struct")]
public void Constructor_Parameter_Insert_Primary_Uncaptured(string keyword)
{
var src1 = keyword + " C(int a) { }";
var src2 = keyword + " C(int a, int b) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [(int a)]@8 -> [(int a, int b)]@8",
"Insert [int b]@16");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "(int a, int b)", GetResource("constructor"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69159")]
public void Constructor_Parameter_Insert_Primary_Captured_Class()
{
var src1 = "class C(int a) { int X => a; }";
var src2 = "class C(int a, int b) { int X => a + b; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_X")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
// TODO: should report rude edit https://github.com/dotnet/roslyn/issues/69159
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Captured_Struct()
{
var src1 = "struct C(int a) { int X => a; }";
var src2 = "struct C(int a, int b) { int X => a + b; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.CapturingPrimaryConstructorParameter, "b", GetResource("struct"), "b"),
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "int b", GetResource("parameter"), GetResource("struct"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record()
{
var src1 = "record C(int X) { }";
var src2 = "record C(int X, int Y, int Z, int U) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Z")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.U")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_ClassWithLayout()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
record C(int X) { }
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
record C(int X, int Y) { }
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "int Y", GetResource("auto-property"), GetResource("record")));
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_Struct()
{
var src1 = @"
record struct C(int x)
{
}
";
var src2 = @"
record struct C(int x, int y)
{
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "int y", GetResource("auto-property"), GetResource("record struct")));
}
[Fact]
public void Constructor_Parameter_Insert_In()
{
var src1 = "class C { C() => throw null; }";
var src2 = "class C { C(in int b) => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [in int b]@12");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single())
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "in int b", GetResource("constructor"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_WithCustomDeconstructor_NoUpdate1()
{
var src1 = "record C(int X ) { public void Deconstruct(out int X) => X = 1; }";
var src2 = "record C(int X, int Y) { public void Deconstruct(out int X) => X = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_WithCustomDeconstructor_NoUpdate2()
{
var src1 = "record C(int X ) { public void Deconstruct(out int X, out int Y) => X = Y = 1; }";
var src2 = "record C(int X, int Y) { public void Deconstruct(out int X, out int Y) => X = Y = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_WithCustomDeconstructor_Update()
{
var src1 = "record C(int X ) { public void Deconstruct(out int X, out int Y) => X = Y = 1; }";
var src2 = "record C(int X, int Y) { public void Deconstruct(out int X, out int Y) => X = Y = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IMethodSymbol>("C.Deconstruct").Single(m => m.Parameters is [_, _])),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_WithCustomDeconstructor_Insert()
{
var src1 = "record C(int X ) { }";
var src2 = "record C(int X, int Y) { public void Deconstruct(out int X, out int Y) => X = Y = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_WithCustomDeconstructor_Delete()
{
var src1 = "record C(int X ) { public void Deconstruct(out int X) => X = 1; }";
var src2 = "record C(int X, int Y) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_Record_WithCustomDeconstructor_Delete_Partial()
{
var srcA1 = "partial record C(int X ) { public partial void Deconstruct(out int X) => X = 1; }";
var srcB1 = "partial record C { public partial void Deconstruct(out int X); }";
var srcA2 = "partial record C(int X, int Y);";
var srcB2 = "partial record C;";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Y")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_IntoLayoutClass_NotLifted()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x)
{
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_IntoLayoutClass_Lifted()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x)
{
public int M() => x;
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
public int M() => x + y;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.CapturingPrimaryConstructorParameter, "y", GetResource("class with explicit or sequential layout"), "y"),
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "int y", GetResource("parameter"), GetResource("class"))
]);
}
[Fact]
public void Constructor_Parameter_Insert_Primary_IntoLayoutClass_LiftedInLambda()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x)
{
public Func<int> M() => () => x;
}
";
var src2 = @"
using System;
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
public Func<int> M() => () => x + y;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.CapturingPrimaryConstructorParameter, "y", GetResource("class with explicit or sequential layout"), "y"),
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "int y", GetResource("parameter"), GetResource("class"))
]);
}
[Theory]
[InlineData("struct")]
[InlineData("class")]
[WorkItem("https://github.com/dotnet/roslyn/issues/68708")]
public void Constructor_Parameter_Reorder_Primary_NotLifted(string keyword)
{
var src1 = keyword + " C(int x, byte y) { }";
var src2 = keyword + " C(byte y, int x) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68708")]
public void Constructor_Parameter_Reorder_Primary_NotLifted_Record_Struct()
{
var src1 = "record struct C(int x, byte y) { }";
var src2 = "record struct C(byte y, int x) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertOrMoveStructMember, "byte y", GetResource("auto-property"), GetResource("record struct"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68708")]
public void Constructor_Parameter_Reorder_Primary_Lifted_Struct()
{
var src1 = "struct C(int x, byte y) { int M() => x + y; }";
var src2 = "struct C(byte y, int x) { int M() => x + y; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertOrMoveStructMember, "byte y", GetResource("parameter"), GetResource("struct"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68708")]
public void Constructor_Parameter_Reorder_Primary_Lifted_Class()
{
var src1 = "class C(int x, byte y) { int M() => x + y; }";
var src2 = "class C(byte y, int x) { int M() => x + y; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68708")]
[WorkItem("https://github.com/dotnet/roslyn/issues/69894")]
public void Constructor_Parameter_Reorder_Primary_Lifted_Record()
{
var src1 = "record C(int x, byte y) { int M() => x + y; }";
var src2 = "record C(byte y, int x) { int M() => x + y; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.Deconstruct"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Deconstruct")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.x")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_x")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_x")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_y")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_y")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Parameter_Capture_Primary_Class()
{
var src1 = @"
class C(int x)
{
public int M() => 1;
}
";
var src2 = @"
class C(int x)
{
public int M() => x;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")));
}
[Fact]
public void Constructor_Parameter_Capture_Primary_Struct()
{
var src1 = @"
struct C(int x, int y)
{
public int M1() => 1;
public int M2() => y;
}
";
var src2 = @"
struct C(int x, int y)
{
public int M1() => y;
public int M2() => x;
}
";
var edits = GetTopEdits(src1, src2);
// note: 'y' is not reported since it is still captured
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.CapturingPrimaryConstructorParameter, "x", GetResource("struct"), "x"));
}
[Fact]
public void Constructor_Parameter_Capture_Primary_ClassWithLayout()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
public int M1() => 1;
public int M2() => y;
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
public int M1() => y;
public int M2() => x;
}
";
var edits = GetTopEdits(src1, src2);
// note: 'y' is not reported since it is still captured
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.CapturingPrimaryConstructorParameter, "x", GetResource("class with explicit or sequential layout"), "x"));
}
[Fact]
public void Constructor_Parameter_CeaseCapturing_Primary_Struct()
{
var src1 = @"
struct C(int x, int y)
{
public int M1() => 1;
public int M2() => x;
public int M3() => y;
}
";
var src2 = @"
struct C(int x, int y)
{
public int M1() => y;
public int M2() => 1;
public int M3() => 2;
}
";
var edits = GetTopEdits(src1, src2);
// note: 'y' is not reported since it is still captured
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.NotCapturingPrimaryConstructorParameter, "M2", GetResource("struct"), "x"));
}
[Fact]
public void Constructor_Parameter_CeaseCapturing_Primary_ClassWithLayout()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
public int M1() => 1;
public int M2() => x;
public int M3() => y;
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C(int x, int y)
{
public int M1() => y;
public int M2() => 1;
public int M3() => 2;
}
";
var edits = GetTopEdits(src1, src2);
// note: 'y' is not reported since it is still captured
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.NotCapturingPrimaryConstructorParameter, "M2", GetResource("class with explicit or sequential layout"), "x"));
}
[Theory]
[InlineData("partial class")]
[InlineData("partial struct")]
[InlineData("readonly partial struct")]
public void Constructor_Parameter_DeleteInsert_Primary(string keywords)
{
var srcA1 = keywords + " C(int P);";
var srcB1 = keywords + " C;";
var srcA2 = keywords + " C;";
var srcB2 = keywords + " C(int P);";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Theory]
[InlineData("partial record")]
[InlineData("partial record struct")]
[InlineData("readonly partial record struct")]
public void Constructor_Parameter_DeleteInsert_Primary_Record(string keywords)
{
var srcA1 = keywords + " C(int P);";
var srcB1 = keywords + " C;";
var srcA2 = keywords + " C;";
var srcB2 = keywords + " C(int P);";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
public void Constructor_Parameter_DeleteInsert_ReplacingPrimaryWithNonPrimary(string keyword)
{
var srcA1 = "partial " + keyword + " C(int a);";
var srcB1 = "partial " + keyword + " C;";
var srcA2 = "partial " + keyword + " C;";
var srcB2 = "partial " + keyword + " C { public C(int a) { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "a"}]), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Fact]
public void Constructor_Parameter_DeleteInsert_ReplacingPrimaryWithNonPrimary_Record()
{
var srcA1 = "partial record C(int P);";
var srcB1 = "partial record C;";
var srcA2 = "partial record C;";
var srcB2 = "partial record C { public int P { get; init; } public C(int P) { } public void Deconstruct(out int P) { P = this.P; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Deconstruct")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "P"}]), partialType: "C", preserveLocalVariables: true),
]),
]);
}
[Fact]
public void Constructor_Parameter_DeleteInsert_ReplacingNonPrimaryWithPrimary_Record()
{
var srcA1 = "partial record C { public int P { get; init; } public C(int P) { } public void Deconstruct(out int P) { P = this.P; } }";
var srcB1 = "partial record C;";
var srcA2 = "partial record C;";
var srcB2 = "partial record C(int P);";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "P" }]), partialType: "C", preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Deconstruct")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
]),
]);
}
[Fact]
public void Constructor_Parameter_DeleteInsert_ReplacingNonPrimaryWithPrimary_WithExplicitPropertyAdded_Record()
{
var srcA1 = "partial record C { public int P { get; init; } public C(int P) { } public void Deconstruct(out int P) { P = this.P; } }";
var srcB1 = "partial record C;";
var srcA2 = "partial record C;";
var srcB2 = "partial record C(int P) { public int P { get; init; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "P" }]), partialType: "C", preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Deconstruct")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
]),
]);
}
[Fact]
public void Constructor_Parameter_DeleteInsert_ReplacingNonPrimaryWithPrimary_WithExplicitFieldAdded_Record()
{
var srcA1 = "partial record C { public int P { get; init; } public C(int P) { } public void Deconstruct(out int P) { P = this.P; } }";
var srcB1 = "partial record C;";
var srcA2 = "partial record C;";
var srcB2 = "partial record C(int P) { public int P; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.Parameters is [{ Name: "P" }]), partialType: "C", preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Deconstruct")),
]),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Parameter_DeleteInsert_SwappingNonPrimaryWithPrimary_Record()
{
var srcA1 = "partial record C(int P) { public C() : this(1) { } }";
var srcB1 = "partial record C;";
var srcA2 = "partial record C;";
var srcB2 = "partial record C() { public C(int P) : this() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
]
),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.Parameters is [{ Name: "P" }])),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.Parameters is []), partialType: "C", preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
]),
]);
}
[Fact]
public void Constructor_Parameter_DeleteInsert_ReplacingPropertyWithField_Record()
{
var srcA1 = "partial record C(int P) { public int P { get; init; } }";
var srcB1 = "partial record C;";
var srcA2 = "partial record C;";
var srcB2 = "partial record C(int P) { public int P; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), partialType: "C", preserveLocalVariables: true),
]),
], capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory(Skip = "https://github.com/dotnet/roslyn/issues/68458")]
[CombinatorialData]
[WorkItem("https://github.com/dotnet/roslyn/issues/68458")]
public void Constructor_Instance_Update_Primary_Attributes(
[CombinatorialValues("class", "struct", "record", "record struct")] string keyword)
{
var src1 = keyword + " C() { }";
var src2 = "[method: System.Obsolete] " + keyword + " C() { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_Update_Initializer_Update()
{
var src1 = @"
class C
{
public C(int a) : base(a) { }
}";
var src2 = @"
class C
{
public C(int a) : base(a + 1) { }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public C(int a) : base(a) { }]@18 -> [public C(int a) : base(a + 1) { }]@18");
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_Update_Initializer_Update_Generic()
{
var src1 = @"
class C<T>
{
public C(int a) : base(a) { }
}";
var src2 = @"
class C<T>
{
public C(int a) : base(a + 1) { }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public C(int a) : base(a) { }]@21 -> [public C(int a) : base(a + 1) { }]@21");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "public C(int a)", GetResource("constructor"))
],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.Baseline | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Theory]
[InlineData("class")]
[InlineData("record")]
public void Constructor_Instance_Update_Initializer_Update_Primary(string keyword)
{
var src1 = keyword + " C(int a) : D(a);";
var src2 = keyword + " C(int a) : D(a + 1);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Theory]
[InlineData("class")]
[InlineData("record")]
public void Constructor_Instance_Update_Initializer_Update_Primary_WithInterface(string keyword)
{
var src1 = keyword + " C(int a) : D(a), I;";
var src2 = keyword + " C(int a) : D(a + 1), I;";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_Update_Initializer_Delete()
{
var src1 = @"
class C<T>
{
public C(int a) : base(a) { }
}";
var src2 = @"
class C<T>
{
public C(int a) { }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public C(int a) : base(a) { }]@21 -> [public C(int a) { }]@21");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "public C(int a)", GetResource("constructor"))
],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.Baseline | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Theory]
[InlineData("class ")]
[InlineData("record")]
public void Constructor_Instance_Update_Initializer_Delete_Primary(string keyword)
{
var src1 = keyword + " C(int a) : D(a);";
var src2 = keyword + " C(int a) : D;";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [: D(a)]@16 -> [: D]@16",
"Delete [D(a)]@18");
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_Update_Initializer_Insert()
{
var src1 = @"
class C
{
public C(int a) { }
}";
var src2 = @"
class C
{
public C(int a) : base(a) { }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public C(int a) { }]@18 -> [public C(int a) : base(a) { }]@18");
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("class ")]
[InlineData("record")]
public void Constructor_Instance_Update_Initializer_Insert_Primary(string keyword)
{
var src1 = keyword + " C(int a) : D;";
var src2 = keyword + " C(int a) : D(a);";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [: D]@16 -> [: D(a)]@16",
"Insert [D(a)]@18");
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68731")]
public void Constructor_Instance_Update_Initializer_StackAlloc_Update()
{
var src1 = "class C { C() : this(stackalloc int[1]) {} C(Span<int> span) {} }";
var src2 = "class C { C() : this(stackalloc int[2]) {} C(Span<int> span) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[2]", FeaturesResources.constructor));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68731")]
public void Constructor_Instance_Update_Initializer_StackAlloc_Delete()
{
var src1 = "class C { C() : this(stackalloc int[1]) {} C(Span<int> span) {} }";
var src2 = "class C { C() : this(default) {} C(Span<int> span) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "C()", FeaturesResources.constructor));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68731")]
public void Constructor_Instance_Update_Initializer_StackAlloc_Insert()
{
var src1 = "class C { C() : this(default) {} C(Span<int> span) {} }";
var src2 = "class C { C() : this(stackalloc int[1]) {} C(Span<int> span) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", FeaturesResources.constructor));
}
[Fact]
public void Constructor_Instance_Update_AnonymousTypeInFieldInitializer()
{
var src1 = "class C { int a = F(new { A = 1, B = 2 }); C() { x = 1; } }";
var src2 = "class C { int a = F(new { A = 1, B = 2 }); C() { x = 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true));
}
[Theory]
[InlineData("class ")]
[InlineData("record")]
public void Constructor_Instance_Update_AnonymousTypeInMemberInitializer_Field_Primary(string keyword)
{
var src1 = keyword + " C() : D(1) { int a = F(new { A = 1, B = 2 }); }";
var src2 = keyword + " C() : D(2) { int a = F(new { A = 1, B = 2 }); }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [D(1)]@13 -> [D(2)]@13");
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Constructor_Instance_Update_BlockBodyToExpressionBody()
{
var src1 = @"
public class C
{
private int _value;
public C(int value) { _value = value; }
}
";
var src2 = @"
public class C
{
private int _value;
public C(int value) => _value = value;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [public C(int value) { _value = value; }]@52 -> [public C(int value) => _value = value;]@52");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Constructor_Instance_Update_BlockBodyToExpressionBody_WithInitializer()
{
var src1 = @"
public class B { B(int value) {} }
public class C : B
{
private int _value;
public C(int value) : base(value) { _value = value; }
}
";
var src2 = @"
public class B { B(int value) {} }
public class C : B
{
private int _value;
public C(int value) : base(value) => _value = value;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [public C(int value) : base(value) { _value = value; }]@90 -> [public C(int value) : base(value) => _value = value;]@90");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Constructor_Instance_Update_ExpressionBodyToBlockBody()
{
var src1 = @"
public class C
{
private int _value;
public C(int value) => _value = value;
}
";
var src2 = @"
public class C
{
private int _value;
public C(int value) { _value = value; }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(@"Update [public C(int value) => _value = value;]@52 -> [public C(int value) { _value = value; }]@52");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Constructor_Instance_Update_ExpressionBodyToBlockBody_WithInitializer()
{
var src1 = @"
public class B { B(int value) {} }
public class C : B
{
private int _value;
public C(int value) : base(value) => _value = value;
}
";
var src2 = @"
public class B { B(int value) {} }
public class C : B
{
private int _value;
public C(int value) : base(value) { _value = value; }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(@"Update [public C(int value) : base(value) => _value = value;]@90 -> [public C(int value) : base(value) { _value = value; }]@90");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
]);
}
[Fact]
public void Constructor_Instance_Update_SemanticError_Partial()
{
var src1 = @"
partial class C
{
partial void C(int x);
}
partial class C
{
partial void C(int x)
{
System.Console.WriteLine(1);
}
}
";
var src2 = @"
partial class C
{
partial void C(int x);
}
partial class C
{
partial void C(int x)
{
System.Console.WriteLine(2);
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IMethodSymbol>("C").PartialImplementationPart, partialType: "C"));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2068")]
public void Constructor_Instance_Update_Modifier_Extern_Add()
{
var src1 = "class C { }";
var src2 = "class C { public extern C(); }";
var edits = GetTopEdits(src1, src2);
// This can be allowed as the compiler generates an empty constructor, but it's not worth the complexity.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "public extern C()", GetResource("constructor")));
}
[Theory]
[InlineData("struct")]
[InlineData("record struct")]
[InlineData("readonly struct")]
[InlineData("readonly record struct")]
public void Constructor_Instance_Insert_Struct(string keyword)
{
var src1 = keyword + " C { }";
var src2 = keyword + " C { public C(int X) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "X" }]))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Insert_Struct_Primary()
{
var src1 = "struct C { }";
var src2 = "struct C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "X" }]))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Insert_Struct_Primary_Record()
{
var src1 = "record struct C { }";
var src2 = "record struct C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "int X", GetResource("auto-property"), GetResource("record struct"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Theory]
[InlineData("class")]
[InlineData("record class")]
public void Constructor_Instance_Insert_ReplacingDefault_Class(string keyword)
{
var src1 = keyword + " C { }";
var src2 = keyword + " C { public C(int X) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "X"}])),
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Insert_ReplacingDefault_Class_Primary()
{
var src1 = "class C { }";
var src2 = "class C(int X) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Insert_ReplacingDefault_Class_Primary_Record()
{
var src1 = "record C { }";
var src2 = "record C(int P) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Constructor_Instance_Insert_ReplacingDefault_Class_Primary_Record_Generic()
{
var src1 = "record C<T> { }";
var src2 = "record C<T>(int P) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryConstructor("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetPrimaryDeconstructor("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities:
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.AddInstanceFieldToExistingType |
EditAndContinueCapabilities.GenericAddFieldToExistingType |
EditAndContinueCapabilities.GenericAddMethodToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "(int P)", GetResource("constructor")),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", GetResource("parameter"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("class ")]
[InlineData("struct")]
public void Constructor_Instance_Insert_ReplacingDefault_Class_WithMemberInitializers(string typeKind)
{
var src1 = @"
" + typeKind + @" C
{
private int a = 10;
private int b;
}
";
var src2 = @"
" + typeKind + @" C
{
private int a = 10;
private int b;
public C() { b = 3; }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [public C() { b = 3; }]@66", "Insert [()]@74");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)
]);
}
[Fact]
public void Constructor_Instance_Insert_ReplacingDefault_WithStackAllocInMemberInitializer()
{
var src1 = "class C { int a = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; }";
var src2 = "class C { int a = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; public C() {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "public C()", GetResource("constructor")));
}
[Fact]
public void Constructor_Instance_Insert_ReplacingDefault_WithStackAllocInMemberInitializer_Static()
{
var src1 = "class C { static int a = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; }";
var src2 = "class C { static int a = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; public C() {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Constructor_Instance_Insert_ReplacingDefault_WithStackAllocInMemberInitializer_Partial()
{
var srcA1 = "partial class C { int a = G(stackalloc int[10]); }";
var srcB1 = "partial class C { static int G(System.Span<int> span) => 1; }";
var srcA2 = "partial class C { int a = G(stackalloc int[10]); }";
var srcB2 = "partial class C { static int G(System.Span<int> span) => 1; public C() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.StackAllocUpdate, "public C()", GetResource("constructor"))
]),
]);
}
[Fact]
public void Constructor_Instance_Delete_ReplacingDefault_Partial()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { public C(int a) { } }";
var srcB2 = "partial class C { public C(int a, int b) { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), partialType: "C", deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.Parameters.Length == 1), partialType: "C")
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), partialType: "C", deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.Parameters.Length == 2), partialType: "C")
])
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Insert_ReplacingExplicitWithDefault_WithStackAllocInMemberInitializer_Partial()
{
var srcA1 = "partial class C { int a = G(stackalloc int[10]); }";
var srcB1 = "partial class C { static int G(System.Span<int> span) => 1; public C() { } }";
var srcA2 = "partial class C { int a = G(stackalloc int[10]); }";
var srcB2 = "partial class C { static int G(System.Span<int> span) => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.StackAllocUpdate, "partial class C", GetResource("constructor", "C()"))
]),
]);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_UpdatingImplicit(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("public", "protected")] string accessibility)
{
if (accessibility == "protected")
keyword = "abstract " + keyword;
var src1 = keyword + " C { }";
var src2 = keyword + " C { [System.Obsolete] " + accessibility + " C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_UpdatingImplicit_Partial(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("public", "protected")] string accessibility)
{
if (accessibility == "protected")
keyword = "abstract " + keyword;
var srcA1 = "partial " + keyword + " C { }";
var srcB1 = "partial " + keyword + " C { }";
var srcA2 = "partial " + keyword + " C { }";
var srcB2 = "partial " + keyword + " C { " + accessibility + " C() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
// no change in document A
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)]),
]);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_AddingParameterless(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("public", "internal", "private", "protected", "private protected", "internal protected")] string accessibility)
{
var src1 = keyword + " C { C(int a) { } }";
var src2 = keyword + " C { C(int a) { } " + accessibility + " C() : this(1) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Insert, c => c.GetParameterlessConstructor("C"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_AddingParameterless_Primary(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("public", "internal", "private", "protected", "private protected", "internal protected")] string accessibility)
{
var src1 = keyword + " C(int a) { }";
var src2 = keyword + " C(int a) { " + accessibility + " C() : this(1) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Insert, c => c.GetParameterlessConstructor("C"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_AddingParameterless_Partial(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("public", "internal", "private", "protected", "private protected", "internal protected")] string accessibility)
{
var srcA1 = "partial " + keyword + " C { }";
var srcB1 = "partial " + keyword + " C { public C(int a) { } }";
var srcA2 = "partial " + keyword + " C { " + accessibility + " C() { } }";
var srcB2 = "partial " + keyword + " C { public C(int a) { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetParameterlessConstructor("C"), partialType: "C")
]),
// no change in document B
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_AddingParameterless_Partial_Primary(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("public", "internal", "private", "protected", "private protected", "internal protected")] string accessibility)
{
var srcA1 = "partial " + keyword + " C { }";
var srcB1 = "partial " + keyword + " C(int a) { }";
var srcA2 = "partial " + keyword + " C { " + accessibility + " C() { } }";
var srcB2 = "partial " + keyword + " C(int a) { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetParameterlessConstructor("C"))
]),
// no change in document B
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_ReplacingSynthesizedWithCustom_ChangingAccessibilty(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("", "private", "protected", "internal", "private protected", "internal protected")] string accessibility)
{
var src1 = keyword + " C { }";
var src2 = keyword + " C { " + accessibility + " C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAccessibility, (accessibility + " C()").Trim(), GetResource("constructor"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Insert_ReplacingSynthesizedWithCustom_ChangingAccessibilty_AbstractType(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("", "private", "public", "internal", "private protected", "internal protected")] string accessibility)
{
var src1 = "abstract " + keyword + " C { }";
var src2 = "abstract " + keyword + " C { " + accessibility + " C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAccessibility, (accessibility + " C()").Trim(), FeaturesResources.constructor)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Instance_Insert_ReplacingSynthesizedWithCustom_Primary()
{
var src1 = "class C { }";
var src2 = "class C() { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_Insert_ReplacingSynthesizedWithCustom_Primary_Record()
{
var src1 = "record C { }";
var src2 = "record C() { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")));
}
[Theory]
[InlineData("struct")]
[InlineData("record struct")]
[InlineData("readonly struct")]
[InlineData("readonly record struct")]
public void Constructor_Instance_Delete_Struct(string keyword)
{
var src1 = keyword + " C { public C(int X) {} }";
var src2 = keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "X" }]), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Delete_Struct_Primary()
{
var src1 = "struct C(int X) { }";
var src2 = "struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "X" }]), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Delete_Struct_Primary_Record()
{
var src1 = "record struct C(int P) { }";
var src2 = "record struct C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("class")]
[InlineData("record class")]
public void Constructor_Instance_Delete_ReplacingWithDefault_Class(string keyword)
{
var src1 = keyword + " C { public C(int X) {} }";
var src2 = keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
// The compiler emits default constructor automatically
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "X"}]), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Delete_ReplacingWithDefault_Class_Primary()
{
var src1 = "class C(int X) { }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
// The compiler emits default constructor automatically
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Delete_ReplacingWithDefault_Class_Primary_Record()
{
var src1 = "record C(int P) { }";
var src2 = "record C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
// The compiler emits default constructor automatically
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Delete_SemanticError()
{
var src1 = "class C { D() {} }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
// The compiler interprets D() as a constructor declaration.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingAccessibility, "class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()")));
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_UpdatingImplicit(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("public", "protected")] string accessibility)
{
if (accessibility == "protected")
keyword = "abstract " + keyword;
var src1 = keyword + " C { [System.Obsolete] " + accessibility + " C() { } }";
var src2 = keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Constructor_Instance_Delete_Parameterless()
{
var src1 = @"
class C
{
private int a = 10;
private int b;
public C() { b = 3; }
}
";
var src2 = @"
class C
{
private int a = 10;
private int b;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [public C() { b = 3; }]@65",
"Delete [()]@73");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)
]);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_WithParameters([CombinatorialValues("record", "class")] string keyword)
{
var src1 = keyword + " C { public C(int x) { } }";
var src2 = keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "x" }]), deletedSymbolContainerProvider: c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_Primary_WithParameters([CombinatorialValues("record", "class")] string keyword)
{
var src1 = keyword + " C(int a) { public C(bool b) { } }";
var src2 = keyword + " C(int a) { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [{ Name: "b" }]), deletedSymbolContainerProvider: c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Instance_Delete_Primary_Class()
{
var src1 = "class C(int a) { }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.FirstOrDefault(c => c.Parameters.Length == 1), deletedSymbolContainerProvider: c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Instance_Delete_Primary_Record()
{
var src1 = "record C(int P) { }";
var src2 = "record C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryDeconstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetPrimaryConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Instance_Delete_Primary_Struct_CeasingCapture()
{
var src1 = "class C(int a) { int A => a; }";
var src2 = "class C { int A => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Constructor_Instance_Delete_Public_PartialWithInitializerUpdate()
{
var srcA1 = "partial class C { public C() { } }";
var srcB1 = "partial class C { int x = 1; }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int x = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)])
]);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_ReplacingCustomWithSynthesized(
[CombinatorialValues("record", "class")] string keyword)
{
var src1 = keyword + " C { public C() { } }";
var src2 = keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is []), preserveLocalVariables: true));
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_ReplacingCustomWithSynthesized_ChangingAccessibility(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("", "private", "protected", "internal", "private protected", "internal protected")] string accessibility)
{
var src1 = keyword + " C { " + accessibility + " C() { } }";
var src2 = keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAccessibility, keyword + " C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_ReplacingCustomWithSynthesized_AbstractType(
[CombinatorialValues("record", "class")] string keyword)
{
var src1 = "abstract " + keyword + " C { protected C() { } }";
var src2 = "abstract " + keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true));
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_ReplacingCustomWithSynthesized_AbstractType_ChangingAccessibility(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("", "private", "public", "internal", "private protected", "internal protected")] string accessibility)
{
var src1 = "abstract " + keyword + " C { " + accessibility + " C() { } }";
var src2 = "abstract " + keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAccessibility, "abstract " + keyword + " C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Instance_Delete_ReplacingCustomWithSynthesized_Partial()
{
var srcA1 = "partial class C { public C(int a) { } }";
var srcB1 = "partial class C { public C(int a, int b) { } }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.Parameters.Length == 1), deletedSymbolContainerProvider: c => c.GetMember("C"))]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.Parameters.Length == 2), deletedSymbolContainerProvider: c => c.GetMember("C"))])
]);
}
[Fact]
public void Constructor_Instance_Delete_Primary_ReplacingWithSynthesized()
{
var src1 = "class C() { }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_Delete_Primary_ReplacingWithSynthesized_Record()
{
var src1 = "record C() { }";
var src2 = "record C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.PrintMembers")),
SemanticEdit(SemanticEditKind.Update, c => c.GetSpecializedEqualsOverload("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.GetHashCode")),
SemanticEdit(SemanticEditKind.Update, c => c.GetCopyConstructor("C")));
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_Primary_ReplacingWithRegular(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("", "private", "protected", "internal", "private protected", "internal protected")] string accessibility)
{
var src1 = keyword + " C() { }";
var src2 = keyword + " C { " + accessibility + " C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAccessibility, (accessibility + " C()").Trim(), GetResource("constructor"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory, CombinatorialData]
public void Constructor_Instance_Delete_Primary_ReplacingWithRegular_AbstractType(
[CombinatorialValues("record", "class")] string keyword,
[CombinatorialValues("", "private", "public", "internal", "private protected", "internal protected")] string accessibility)
{
var src1 = "abstract " + keyword + " C() { }";
var src2 = "abstract " + keyword + " C { " + accessibility + " C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAccessibility, (accessibility + " C()").Trim(), GetResource("constructor"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Constructor_Instance_InsertDelete_Primary_Partial_Class()
{
var src1 = @"
partial class C { }
partial class C(int P);
";
var src2 = @"
partial class C(int P) { }
partial class C;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_InsertDelete_Primary_Partial_Record()
{
var src1 = @"
partial record C { }
partial record C(int P);
";
var src2 = @"
partial record C(int P) { }
partial record C;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void Constructor_Instance_Partial_DeletePrivateInsertPrivate()
{
var srcA1 = "partial class C { C() { } }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { C() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Fact]
public void Constructor_Instance_Partial_DeletePublicInsertPublic()
{
var srcA1 = "partial class C { public C() { } }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { public C() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Fact]
public void Constructor_Instance_Partial_DeletePrivateInsertPublic()
{
var srcA1 = "partial class C { C() { } }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { public C() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
// delete of the constructor in partial part will be reported as rude edit in the other document where it was inserted back with changed accessibility
DocumentResults(
semanticEdits: NoSemanticEdits),
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingAccessibility, "public C()", FeaturesResources.constructor)]),
]);
}
[Fact]
public void Constructor_Instance_Partial_InsertPublicDeletePublic()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { public C() { } }";
var srcA2 = "partial class C { public C() { } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
]);
}
[Fact]
public void Constructor_Instance_Partial_InsertPrivateDeletePrivate()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { private C() { } }";
var srcA2 = "partial class C { private C() { } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
]);
}
[Fact]
public void Constructor_Instance_Partial_DeleteInternalInsertInternal()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { internal C() { } }";
var srcA2 = "partial class C { internal C() { } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
]);
}
[Fact]
public void Constructor_Instance_Partial_InsertInternalDeleteInternal_WithBody()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { internal C() { } }";
var srcA2 = "partial class C { internal C() { Console.WriteLine(1); } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
]);
}
[Fact]
public void Constructor_Instance_Partial_InsertPublicDeletePrivate()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { private C() { } }";
var srcA2 = "partial class C { public C() { } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingAccessibility, "public C()", FeaturesResources.constructor)]),
// delete of the constructor in partial part will be reported as rude in the the other document where it was inserted with changed accessibility
DocumentResults(),
]);
}
[Fact]
public void Constructor_Instance_Partial_InsertInternalDeletePrivate()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { private C() { } }";
var srcA2 = "partial class C { internal C() { } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingAccessibility, "internal C()", FeaturesResources.constructor)]),
DocumentResults(),
]);
}
[Fact]
public void Constructor_Instance_Partial_Update_LambdaInInitializer1()
{
var src1 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
<N:0.3>public C()
{
F(<N:0.2>c => c + 1</N:0.2>);
}</N:0.3>
}
";
var src2 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
<N:0.3>public C()
{
F(<N:0.2>c => c + 2</N:0.2>);
}</N:0.3>
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])]);
}
[Fact]
public void Constructor_Instance_Partial_Update_LambdaInInitializer_Trivia1()
{
var src1 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
public C() { F(<N:0.2>c => c + 1</N:0.2>); }
}
";
var src2 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
/*new trivia*/public C() { F(<N:0.2>c => c + 1</N:0.2>); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])]);
}
[Fact]
public void Constructor_Instance_Partial_Update_LambdaInInitializer_ExplicitInterfaceImpl1()
{
var src1 = @"
using System;
public interface I { int B { get; } }
public interface J { int B { get; } }
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C : I, J
{
int I.B { get; } = F(<N:0.1>ib => ib + 1</N:0.1>);
int J.B { get; } = F(<N:0.2>jb => jb + 1</N:0.2>);
public C()
{
F(<N:0.3>c => c + 1</N:0.3>);
}
}
";
var src2 = @"
using System;
public interface I { int B { get; } }
public interface J { int B { get; } }
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C : I, J
{
int I.B { get; } = F(<N:0.1>ib => ib + 1</N:0.1>);
int J.B { get; } = F(<N:0.2>jb => jb + 1</N:0.2>);
public C()
{
F(<N:0.3>c => c + 2</N:0.3>);
}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])]);
}
[Fact]
public void Constructor_Instance_Partial_Insert_Parameterless_LambdaInInitializer1()
{
var src1 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
}
";
var src2 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
public C() // new ctor
{
F(c => c + 1);
}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])],
capabilities: EditAndContinueCapabilities.AddStaticFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.NewTypeDefinition);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "c", GetResource("lambda"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2504")]
public void Constructor_Instance_Partial_Insert_WithParameters_LambdaInInitializer1()
{
var src1 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
}
";
var src2 = @"
using System;
partial class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int B { get; } = F(<N:0.1>b => b + 1</N:0.1>);
public C(int x) // new ctor
{
F(c => c + 1);
}
}
";
var edits = GetTopEdits(src1, src2);
_ = GetSyntaxMap(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "public C(int x)"));
// TODO: bug https://github.com/dotnet/roslyn/issues/2504
//edits.VerifySemantics(
// ActiveStatementsDescription.Empty,
// new[] { SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<NamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0]) });
}
[Fact]
public void Constructor_Instance_Partial_Explicit_Update()
{
var srcA1 = @"
using System;
partial class C
{
C(int arg) => Console.WriteLine(0);
C(bool arg) => Console.WriteLine(1);
}
";
var srcB1 = @"
using System;
partial class C
{
int a <N:0.0>= 1</N:0.0>;
C(uint arg) => Console.WriteLine(2);
}
";
var srcA2 = @"
using System;
partial class C
{
C(int arg) => Console.WriteLine(0);
C(bool arg) => Console.WriteLine(1);
}
";
var srcB2 = @"
using System;
partial class C
{
int a <N:0.0>= 2</N:0.0>; // updated field initializer
C(uint arg) => Console.WriteLine(2);
C(byte arg) => Console.WriteLine(3); // new ctor
}
";
var syntaxMapB = GetSyntaxMap(srcB1, srcB2)[0];
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
// No changes in document A
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "Int32"), partialType: "C", syntaxMap: syntaxMapB),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "Boolean"), partialType: "C", syntaxMap: syntaxMapB),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "UInt32"), partialType: "C", syntaxMap: syntaxMapB),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(c => c.Parameters.Single().Type.Name == "Byte"), partialType: "C"),
])
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Instance_Partial_Explicit_Update_SemanticError()
{
var srcA1 = @"
using System;
partial class C
{
C(int arg) => Console.WriteLine(0);
C(int arg) => Console.WriteLine(1);
}
";
var srcB1 = @"
using System;
partial class C
{
int a = 1;
}
";
var srcA2 = @"
using System;
partial class C
{
C(int arg) => Console.WriteLine(0);
C(int arg) => Console.WriteLine(1);
}
";
var srcB2 = @"
using System;
partial class C
{
int a = 2;
C(int arg) => Console.WriteLine(2);
}
";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
// No changes in document A
DocumentResults(),
// The actual edits do not matter since there are semantic errors in the compilation.
// We just should not crash.
DocumentResults(diagnostics: [])
]);
}
[Fact]
public void Constructor_Instance_Partial_Implicit_Update()
{
var srcA1 = "partial class C { int F = 1; }";
var srcB1 = "partial class C { int G = 1; }";
var srcA2 = "partial class C { int F = 2; }";
var srcB2 = "partial class C { int G = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Fact]
public void PartialDeclaration_Delete()
{
var srcA1 = "partial class C { public C() { } void F() { } }";
var srcB1 = "partial class C { int x = 1; }";
var srcA2 = "";
var srcB2 = "partial class C { int x = 2; void F() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IMethodSymbol>("F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Fact]
public void PartialDeclaration_Insert()
{
var srcA1 = "";
var srcB1 = "partial class C { int x = 1; void F() { } }";
var srcA2 = "partial class C { public C() { } void F() { } }";
var srcB2 = "partial class C { int x = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IMethodSymbol>("F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)]),
]);
}
[Fact]
public void PartialDeclaration_Insert_Reloadable()
{
var srcA1 = "";
var srcB1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int x = 1; void F() { } }";
var srcA2 = "partial class C { public C() { } void F() { } }";
var srcB2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int x = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2068")]
public void Constructor_Static_Update_Modifier_Extern_Add()
{
var src1 = "class C { }";
var src2 = "class C { static extern C(); }";
var edits = GetTopEdits(src1, src2);
// This can be allowed as the compiler generates an empty constructor, but it's not worth the complexity.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertExtern, "static extern C()", GetResource("static constructor")));
}
[Fact]
public void Constructor_Static_Delete()
{
var src1 = "class C { static C() { } }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.static_constructor, "C()")));
}
[Fact]
public void Constructor_Static_Delete_Reloadable()
{
var src1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { static C() { } }";
var src2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Constructor_Static_Insert()
{
var src1 = "class C { }";
var src2 = "class C { static C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single())],
EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Constructor_Static_Partial_DeleteInsert()
{
var srcA1 = "partial class C { static C() { } }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { static C() { } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Fact]
public void Constructor_Static_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { static C() { } }";
var srcA2 = "partial class C { static C() { } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), partialType: "C", preserveLocalVariables: true)
]),
// delete of the constructor in partial part will be represented as a semantic update in the other document where it was inserted back
DocumentResults(),
]);
}
#endregion
#region Destructors
[Fact]
public void DestructorDelete()
{
var src1 = @"class B { ~B() { } }";
var src2 = @"class B { }";
var expectedEdit1 = @"Delete [~B() { }]@10";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(expectedEdit1);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class B", DeletedSymbolDisplay(CSharpFeaturesResources.destructor, "~B()")));
}
[Fact]
public void DestructorDelete_InsertConstructor()
{
var src1 = @"class B { ~B() { } }";
var src2 = @"class B { B() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [B() { }]@10",
"Insert [()]@11",
"Delete [~B() { }]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingAccessibility, "B()", FeaturesResources.constructor),
Diagnostic(RudeEditKind.Delete, "class B", DeletedSymbolDisplay(CSharpFeaturesResources.destructor, "~B()")));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Destructor_BlockBodyToExpressionBody()
{
var src1 = @"
public class C
{
~C() { Console.WriteLine(0); }
}
";
var src2 = @"
public class C
{
~C() => Console.WriteLine(0);
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [~C() { Console.WriteLine(0); }]@25 -> [~C() => Console.WriteLine(0);]@25");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Finalize"), preserveLocalVariables: false)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Destructor_ExpressionBodyToBlockBody()
{
var src1 = @"
public class C
{
~C() => Console.WriteLine(0);
}
";
var src2 = @"
public class C
{
~C() { Console.WriteLine(0); }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [~C() => Console.WriteLine(0);]@25 -> [~C() { Console.WriteLine(0); }]@25");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Finalize"), preserveLocalVariables: false)
]);
}
#endregion
#region Members with Initializers
[Fact]
public void MemberInitializer_Update_Field()
{
var src1 = "class C { int a = 0; }";
var src2 = "class C { int a = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 0]@14 -> [a = 1]@14");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_EventField()
{
var src1 = "class C { event System.Action a = F(0); static System.Action F(int a) => null; }";
var src2 = "class C { event System.Action a = F(1); static System.Action F(int a) => null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Property()
{
var src1 = "class C { int a { get; } = 0; }";
var src2 = "class C { int a { get; } = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a { get; } = 0;]@10 -> [int a { get; } = 1;]@10");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Remove_Field()
{
var src1 = "class C { int a = 0; }";
var src2 = "class C { int a; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 0]@14 -> [a]@14");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_Remove_Partial_Field()
{
var srcA1 = "partial class C { int F = 1; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int F ; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
]);
}
[Fact]
public void MemberInitializer_Update_Remove_Partial_Property()
{
var srcA1 = "partial class C { int F { get; } = 1; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int F { get; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_F"))
]),
]);
}
[Fact]
public void MemberInitializer_Update_DeleteInsert_Field()
{
var srcA1 = "partial class C { int F = 1; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int F = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true),
]),
]);
}
[Fact]
public void MemberInitializer_Update_DeleteInsert_Property()
{
var srcA1 = "partial class C { int F { get; } = 1; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { int F { get; } = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true),
]),
]);
}
[Fact]
public void MemberInitializer_PropertyUpdate2()
{
var src1 = "class C { int a { get; } = 0; }";
var src2 = "class C { int a { get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a { get; } = 0;]@10 -> [int a { get { return 1; } }]@10",
"Update [get;]@18 -> [get { return 1; }]@18");
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.a").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), preserveLocalVariables: true));
}
[Fact]
public void MemberInitializer_PropertyInsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int a { get; } = 1; }";
var srcA2 = "partial class C { int a { get { return 1; } } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.a").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), partialType: "C", preserveLocalVariables: true)
]),
DocumentResults(),
]);
}
[Fact]
public void MemberInitializer_Field_Update3()
{
var src1 = "class C { int a; }";
var src2 = "class C { int a = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@14 -> [a = 0]@14");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_PropertyUpdate3()
{
var src1 = "class C { int a { get { return 1; } } }";
var src2 = "class C { int a { get; } = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a { get { return 1; } }]@10 -> [int a { get; } = 0;]@10",
"Update [get { return 1; }]@18 -> [get;]@18");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.a").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
public void FieldInitializer_Update_AccessPrimaryConstructorParameter(string keyword)
{
var src1 = keyword + " C(int x) { public int F = 1; }";
var src2 = keyword + " C(int x) { public int F = 1 + x; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void MemberInitializer_Field_Delete()
{
var src1 = "class C { int a = 1; }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.field, "a")));
}
[Theory]
[InlineData("")]
[InlineData("public C() { }")]
[InlineData("public C(int x) { }")]
public void MemberInitializer_PropertyDelete(string ctor)
{
var src1 = "class C { " + ctor + " int a { get; set; } = 1; }";
var src2 = "class C { " + ctor + " }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.a"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_a"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_a"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
]);
}
[Fact]
public void MemberInitializer_Update_Field_StaticCtorUpdate1()
{
var src1 = "class C { static int a; static C() { } }";
var src2 = "class C { static int a = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@21 -> [a = 0]@21",
"Delete [static C() { }]@24",
"Delete [()]@32");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Property_StaticCtorUpdate1()
{
var src1 = "class C { static int a { get; } = 1; static C() { } }";
var src2 = "class C { static int a { get; } = 2;}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Field_InstanceCtorUpdate_Private()
{
var src1 = "class C { int a; [System.Obsolete]C() { } }";
var src2 = "class C { int a = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, $"class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()")),
Diagnostic(RudeEditKind.ChangingAccessibility, $"class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MemberInitializer_Update_Property_InstanceCtorUpdate_Private()
{
var src1 = "class C { int a { get; } = 1; C() { } }";
var src2 = "class C { int a { get; } = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingAccessibility, $"class C", DeletedSymbolDisplay(FeaturesResources.constructor, "C()")));
}
[Fact]
public void MemberInitializer_Update_Field_InstanceCtorUpdate_Public()
{
var src1 = "class C { int a; public C() { } }";
var src2 = "class C { int a = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Property_InstanceCtorUpdate_Public()
{
var src1 = "class C { int a { get; } = 1; public C() { } }";
var src2 = "class C { int a { get; } = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Field_StaticCtorUpdate2()
{
var src1 = "class C { static int a; static C() { } }";
var src2 = "class C { static int a = 0; static C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@21 -> [a = 0]@21");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Property_StaticCtorUpdate2()
{
var src1 = "class C { static int a { get; } = 1; static C() { } }";
var src2 = "class C { static int a { get; } = 2; static C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single(), preserveLocalVariables: true)]);
}
[Theory]
[InlineData("class ")]
[InlineData("struct")]
public void MemberInitializer_Update_Field_InstanceCtorUpdate2(string typeKind)
{
var src1 = typeKind + " C { int a; public C() { } }";
var src2 = typeKind + " C { int a = 0; public C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@15 -> [a = 0]@15");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("readonly struct")]
public void MemberInitializer_Update_Property_InstanceCtorUpdate2(string typeKind)
{
var src1 = typeKind + " C { int a { get; } = 1; public C() { } }";
var src2 = typeKind + " C { int a { get; } = 2; public C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Field_InstanceCtorUpdate3()
{
var src1 = "class C { int a; }";
var src2 = "class C { int a = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@14 -> [a = 0]@14");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Property_InstanceCtorUpdate3()
{
var src1 = "class C { int a { get; } = 1; }";
var src2 = "class C { int a { get; } = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Field_InstanceCtorUpdate4()
{
var src1 = "class C { int a = 0; }";
var src2 = "class C { int a; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 0]@14 -> [a]@14");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Field_InstanceCtorUpdate_Class()
{
var src1 = "class C { int a; private C(int a) { } private C(bool a) : this() { } private C() : this(1) { } private C(string a) : base() { } }";
var src2 = "class C { int a = 1; private C(int a) { } private C(bool a) : this() { } private C() : this(1) { } private C(string a) : base() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@14 -> [a = 1]@14");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(int)"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(string)"), preserveLocalVariables: true),
]);
}
[Fact]
public void MemberInitializer_Update_Field_InstanceCtorUpdate_Struct()
{
var src1 = "struct C { int a; private C(int a) { } private C(bool a) : this() { } private C(char a) : this(1) { } }";
var src2 = "struct C { int a = 1; private C(int a) { } private C(bool a) : this() { } private C(char a) : this(1) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@15 -> [a = 1]@15");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(int)"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(bool)"), preserveLocalVariables: true),
]);
}
[Fact]
public void MemberInitializer_Update_Property_InstanceCtorUpdate5()
{
var src1 = "class C { int a { get; } = 1; private C(int a) { } private C(bool a) { } }";
var src2 = "class C { int a { get; } = 10000; private C(int a) { } private C(bool a) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(int)"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(bool)"), preserveLocalVariables: true),
]);
}
[Fact]
public void MemberInitializer_Update_Property_Struct_InstanceCtorUpdate5()
{
var src1 = "struct C { int a { get; } = 1; private C(int a) { } private C(bool a) { } }";
var src2 = "struct C { int a { get; } = 10000; private C(int a) { } private C(bool a) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(int)"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(bool)"), preserveLocalVariables: true),
]);
}
[Fact]
public void MemberInitializer_Update_Field_InstanceCtorUpdate6()
{
var src1 = "class C { int a; private C(int a) : this(true) { } private C(bool a) { } }";
var src2 = "class C { int a = 0; private C(int a) : this(true) { } private C(bool a) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@14 -> [a = 0]@14");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(m => m.ToString() == "C.C(bool)"), preserveLocalVariables: true)
]);
}
[Fact]
public void MemberInitializer_Update_Field_StaticCtorInsertImplicit()
{
var src1 = "class C { static int a; }";
var src2 = "class C { static int a = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@21 -> [a = 0]@21");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single())]);
}
[Fact]
public void MemberInitializer_Update_Field_StaticCtorInsertExplicit()
{
var src1 = "class C { static int a; }";
var src2 = "class C { static int a = 0; static C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [static C() { }]@28",
"Insert [()]@36",
"Update [a]@21 -> [a = 0]@21");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").StaticConstructors.Single())],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("readonly struct")]
public void MemberInitializer_Update_Field_Constructor_Instance_InsertExplicit(string typeKind)
{
var src1 = typeKind + " C { int a; }";
var src2 = typeKind + " C { int a = 0; public C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
[InlineData("readonly struct")]
public void MemberInitializer_Update_Property_Constructor_Instance_InsertExplicit(string typeKind)
{
var src1 = typeKind + " C { int a { get; } = 1; }";
var src2 = typeKind + " C { int a { get; } = 2; public C() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void MemberInitializer_Update_Field_GenericType()
{
var src1 = "class C<T> { int a = 1; }";
var src2 = "class C<T> { int a = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 1]@17 -> [a = 2]@17");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "a = 2", GetResource("field")),
],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.Baseline | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MemberInitializer_Update_Property_GenericType()
{
var src1 = "class C<T> { int a { get; } = 1; }";
var src2 = "class C<T> { int a { get; } = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "int a", GetResource("auto-property"))],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MemberInitializer_Update_StackAllocInConstructor()
{
var src1 = "unsafe class C { int a = 1; public C() { int* a = stackalloc int[10]; } }";
var src2 = "unsafe class C { int a = 2; public C() { int* a = stackalloc int[10]; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 1]@21 -> [a = 2]@21");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[10]", FeaturesResources.constructor));
}
[Fact]
public void MemberInitializer_Update_StackAllocInConstructor_ThisInitializer()
{
var src1 = "class C { int a = 1; C() : this(stackalloc int[1]) { } C(System.Span<int> s) { } }";
var src2 = "class C { int a = 2; C() : this(stackalloc int[1]) { } C(System.Span<int> s) { } }";
var edits = GetTopEdits(src1, src2);
// no rude edits for constructors that field initializers are not emitted to
edits.VerifySemanticDiagnostics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/68731")]
public void MemberInitializer_Update_StackAllocInConstructor_Initializer_Field()
{
var src1 = "class C : B { int a = 1; C() : base(stackalloc int[1]) { } } class B(System.Span<int> s);";
var src2 = "class C : B { int a = 2; C() : base(stackalloc int[1]) { } } class B(System.Span<int> s);";
var edits = GetTopEdits(src1, src2);
// TODO: allow https://github.com/dotnet/roslyn/issues/68731
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("constructor")));
}
[Fact]
public void FieldInitializerUpdate_StackAllocInConstructor_PrimaryBaseInitializer()
{
var src1 = "class C : B(stackalloc int[1]) { int a = 1; } class B(System.Span<int> s);";
var src2 = "class C : B(stackalloc int[1]) { int a = 2; } class B(System.Span<int> s);";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67307")]
public void MemberInitializer_Update_StackAllocInOtherInitializer()
{
var src1 = "class C { int a = 1; int b = G(stackalloc int[10]); static int G(Span<int> span) => 1; }";
var src2 = "class C { int a = 2; int b = G(stackalloc int[10]); static int G(Span<int> span) => 1; }";
var edits = GetTopEdits(src1, src2);
// TODO: allow https://github.com/dotnet/roslyn/issues/67307
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "class C", GetResource("constructor", "C()")));
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/37172")]
[WorkItem("https://github.com/dotnet/roslyn/issues/43099")]
public void MemberInitializer_Update_SwitchExpressionInConstructor()
{
var src1 = "class C { int a = 1; public C() { var b = a switch { 0 => 0, _ => 1 }; } }";
var src2 = "class C { int a = 2; public C() { var b = a switch { 0 => 0, _ => 1 }; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_StackAlloc_Update()
{
var src1 = "class C { int a { get; } = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; }";
var src2 = "class C { int a { get; } = G(stackalloc int[20]); static int G(System.Span<int> span) => 1; }";
var edits = GetTopEdits(src1, src2);
// Note: One edit is for the initializer and the other for implicit constructor.
// We don't attempt to avoid duplicates reported for different members.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[20]", GetResource("auto-property")),
Diagnostic(RudeEditKind.StackAllocUpdate, "class C", GetResource("constructor", "C()")));
}
[Fact]
public void MemberInitializer_Update_StackAlloc_Delete()
{
var src1 = "class C { int a { get; } = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; }";
var src2 = "class C { int a { get; } = G(default); static int G(System.Span<int> span) => 1; }";
var edits = GetTopEdits(src1, src2);
// Note: One edit is for the initializer and the other for implicit constructor.
// We don't attempt to avoid duplicates reported for different memebers.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "int a", GetResource("auto-property")),
Diagnostic(RudeEditKind.StackAllocUpdate, "class C", GetResource("constructor", "C()")));
}
[Fact]
public void MemberInitializer_Update_StackAlloc_Insert()
{
var src1 = "class C { int a { get; } = G(default); static int G(System.Span<int> span) => 1; }";
var src2 = "class C { int a { get; } = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[10]", GetResource("auto-property")));
}
[Fact]
public void PropertyInitializerUpdate_StackAlloc_Delete()
{
var src1 = "class C { int a { get; } = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; }";
var src2 = "class C { int a { get; } = G(default); static int G(System.Span<int> span) => 1; }";
var edits = GetTopEdits(src1, src2);
// Note: One edit is for the initializer and the other for implicit constructor.
// We don't attempt to avoid duplicates reported for different memebers.
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "int a", GetResource("auto-property")),
Diagnostic(RudeEditKind.StackAllocUpdate, "class C", GetResource("constructor", "C()")));
}
[Fact]
public void PropertyInitializerUpdate_StackAlloc_Insert()
{
var src1 = "class C { int a { get; } = G(default); static int G(System.Span<int> span) => 1; }";
var src2 = "class C { int a { get; } = G(stackalloc int[10]); static int G(System.Span<int> span) => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[10]", GetResource("auto-property")));
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_StackAlloc(string accessor)
{
var src1 = "unsafe class C { int a " + accessor + " = G(stackalloc int[10]); public G(Span<int> span) => 1; }";
var src2 = "unsafe class C { int a " + accessor + " = G(stackalloc int[20]); public G(Span<int> span) => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[20]", GetResource(accessor == "" ? "field" : "auto-property")),
Diagnostic(RudeEditKind.StackAllocUpdate, "public G(Span<int> span)", GetResource("constructor")));
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_StackAlloc_InConstructorWithInitializers1(string accessor)
{
var src1 = "unsafe class C { int a " + accessor + " = 1; public C() { int* a = stackalloc int[10]; } }";
var src2 = "unsafe class C { int a " + accessor + " = 2; public C() { int* a = stackalloc int[10]; } }";
var edits = GetTopEdits(src1, src2);
// TODO (tomat): diagnostic should point to the property initializer
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[10]", GetResource("constructor")));
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_StackAlloc_InConstructorWithInitializers2(string accessor)
{
var src1 = "unsafe class C { int a " + accessor + " = 1; public C() { } public C(int b) { int* a = stackalloc int[10]; } }";
var src2 = "unsafe class C { int a " + accessor + " = 2; public C() { } public C(int b) { int* a = stackalloc int[10]; } }";
var edits = GetTopEdits(src1, src2);
// TODO (tomat): diagnostic should point to the property initializer
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[10]", FeaturesResources.constructor));
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_StackAlloc_InConstructorWithoutInitializers(string accessor)
{
var src1 = "unsafe class C { int a " + accessor + " = 1; public C() : this(1) { int* a = stackalloc int[10]; } public C(int a) { } }";
var src2 = "unsafe class C { int a " + accessor + " = 2; public C() : this(1) { int* a = stackalloc int[10]; } public C(int a) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
[WorkItem("https://github.com/dotnet/roslyn/issues/43099")]
[WorkItem("https://github.com/dotnet/roslyn/issues/37172")]
public void MemberInitializer_Update_SwitchExpression_InConstructorWithInitializers(string accessor)
{
var src1 = "class C { int a " + accessor + " = 1; public C() { var b = a switch { 0 => 0, _ => 1 }; } }";
var src2 = "class C { int a " + accessor + " = 2; public C() { var b = a switch { 0 => 0, _ => 1 }; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
[WorkItem("https://github.com/dotnet/roslyn/issues/43099")]
[WorkItem("https://github.com/dotnet/roslyn/issues/37172")]
public void MemberInitializer_Update_SwitchExpression_InConstructorWithInitializers2(string accessor)
{
var src1 = "class C { int a " + accessor + " = 1; public C() { } public C(int b) { var b = a switch { 0 => 0, _ => 1 }; } }";
var src2 = "class C { int a " + accessor + " = 2; public C() { } public C(int b) { var b = a switch { 0 => 0, _ => 1 }; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
[WorkItem("https://github.com/dotnet/roslyn/issues/37172")]
[WorkItem("https://github.com/dotnet/roslyn/issues/43099")]
public void MemberInitializer_Update_SwitchExpression_InConstructorWithoutInitializers(string accessor)
{
var src1 = "class C { int a " + accessor + " = 1; public C() : this(1) { var b = a switch { 0 => 0, _ => 1 }; } public C(int a) { } }";
var src2 = "class C { int a " + accessor + " = 2; public C() : this(1) { var b = a switch { 0 => 0, _ => 1 }; } public C(int a) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_LambdaInConstructor_Field()
{
var src1 = "class C { int a = 1; public C() { F(() => {}); } static void F(System.Action a) {} }";
var src2 = "class C { int a = 2; public C() { F(() => {}); } static void F(System.Action a) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 1]@14 -> [a = 2]@14");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_LambdaInConstructor_Property()
{
var src1 = "class C { int a { get; } = 1; public C() { F(() => {}); } static void F(System.Action a) {} }";
var src2 = "class C { int a { get; } = 2; public C() { F(() => {}); } static void F(System.Action a) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_QueryInConstructor_Field()
{
var src1 = "using System.Linq; class C { int a = 1; public C() { F(from a in new[] {1,2,3} select a + 1); } static void F(System.Collections.Generic.IEnumerable<int> x) {} }";
var src2 = "using System.Linq; class C { int a = 2; public C() { F(from a in new[] {1,2,3} select a + 1); } static void F(System.Collections.Generic.IEnumerable<int> x) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 1]@33 -> [a = 2]@33");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_QueryInConstructor_Property()
{
var src1 = "using System.Linq; class C { int a { get; } = 1; public C() { F(from a in new[] {1,2,3} select a + 1); } static void F(System.Collections.Generic.IEnumerable<int> x) {} }";
var src2 = "using System.Linq; class C { int a { get; } = 2; public C() { F(from a in new[] {1,2,3} select a + 1); } static void F(System.Collections.Generic.IEnumerable<int> x) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_AnonymousTypeInConstructor_Field()
{
var src1 = "class C { int a = 1; C() { F(new { A = 1, B = 2 }); } static void F(object x) {} }";
var src2 = "class C { int a = 2; C() { F(new { A = 1, B = 2 }); } static void F(object x) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_AnonymousTypeInConstructor_Property()
{
var src1 = "class C { int a { get; } = 1; C() { F(new { A = 1, B = 2 }); } static void F(object x) {} }";
var src2 = "class C { int a { get; } = 2; C() { F(new { A = 1, B = 2 }); } static void F(object x) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_PartialTypeWithSingleDeclaration_Field()
{
var src1 = "partial class C { int a = 1; }";
var src2 = "partial class C { int a = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 1]@22 -> [a = 2]@22");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), preserveLocalVariables: true)
]);
}
[Fact]
public void MemberInitializer_Update_PartialTypeWithSingleDeclaration_Property()
{
var src1 = "partial class C { int a { get; } = 1; }";
var src2 = "partial class C { int a { get; } = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), preserveLocalVariables: true)
]);
}
[Fact]
public void MemberInitializer_Update_PartialTypeWithMultipleDeclarations_Field()
{
var src1 = "partial class C { int a = 1; } partial class C { }";
var src2 = "partial class C { int a = 2; } partial class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 1]@22 -> [a = 2]@22");
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), preserveLocalVariables: true)
]);
}
[Fact]
public void MemberInitializer_Update_PartialTypeWithMultipleDeclarations_Property()
{
var src1 = "partial class C { int a { get; } = 1; } partial class C { }";
var src2 = "partial class C { int a { get; } = 2; } partial class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), preserveLocalVariables: true)
]);
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_ParenthesizedLambda(string accessor)
{
var src1 = "class C { int a " + accessor + " = F(1, (x, y) => x + y); }";
var src2 = "class C { int a " + accessor + " = F(2, (x, y) => x + y); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_AnonymousType(string accessor)
{
var src1 = "class C { int a " + accessor + " = F(1, new { A = 1, B = 2 }); }";
var src2 = "class C { int a " + accessor + " = F(2, new { A = 1, B = 2 }); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_Query_Field(string accessor)
{
var src1 = "class C { int a " + accessor + " = F(1, from goo in bar select baz); }";
var src2 = "class C { int a " + accessor + " = F(2, from goo in bar select baz); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Theory]
[InlineData("")]
[InlineData("{ get; }")]
public void MemberInitializer_Update_Lambda(string accessor)
{
var src1 = "class C { int a " + accessor + " = F(1, x => x); }";
var src2 = "class C { int a " + accessor + " = F(2, x => x); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void MemberInitializer_Update_Lambda_ImplicitCtor_EditInitializerWithLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 2</N:0.1>);
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])]);
}
[Fact]
public void MemberInitializer_Update_Lambda_ImplicitCtor_EditInitializerWithoutLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = 1;
int B = F(<N:0.0>b => b + 1</N:0.0>);
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = 2;
int B = F(<N:0.0>b => b + 1</N:0.0>);
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])]);
}
[Fact]
public void MemberInitializer_Update_Lambda_CtorIncludingInitializers_EditInitializerWithLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
<N:0.2>public C() {}</N:0.2>
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 2</N:0.1>);
<N:0.2>public C() {}</N:0.2>
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])]);
}
[Fact]
public void MemberInitializer_Update_Lambda_CtorIncludingInitializers_EditInitializerWithoutLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = 1;
int B = F(<N:0.0>b => b + 1</N:0.0>);
public C() {}
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = 2;
int B = F(<N:0.0>b => b + 1</N:0.0>);
public C() {}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])]);
}
[Fact]
public void MemberInitializer_Update_Lambda_MultipleCtorsIncludingInitializers_EditInitializerWithLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) {}
public C(bool b) {}
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 2</N:0.1>);
public C(int a) {}
public C(bool b) {}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[0], syntaxMap[0]),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[1], syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_MultipleCtorsIncludingInitializersContainingLambdas_EditInitializerWithLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 1</N:0.2>); }
public C(bool b) { F(<N:0.3>d => d + 1</N:0.3>); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 2</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 1</N:0.2>); }
public C(bool b) { F(<N:0.3>d => d + 1</N:0.3>); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[0], syntaxMap[0]),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[1], syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_MultipleCtorsIncludingInitializersContainingLambdas_EditInitializerWithLambda_Trivia1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 1</N:0.2>); }
public C(bool b) { F(<N:0.3>d => d + 1</N:0.3>); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 1</N:0.2>); }
public C(bool b) { F(<N:0.3>d => d + 1</N:0.3>); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[0], syntaxMap[0]),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[1], syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_MultipleCtorsIncludingInitializersContainingLambdas_EditConstructorWithLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 1</N:0.2>); }
public C(bool b) { F(d => d + 1); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 2</N:0.2>); }
public C(bool b) { F(d => d + 1); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Int32 a)"), syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_MultipleCtorsIncludingInitializersContainingLambdas_EditConstructorWithLambda_Trivia1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 1</N:0.2>); }
public C(bool b) { F(d => d + 1); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(<N:0.2>c => c + 1</N:0.2>); }
public C(bool b) { F(d => d + 1); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Int32 a)"), syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_MultipleCtorsIncludingInitializersContainingLambdas_EditConstructorWithoutLambda1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(c => c + 1); }
public C(bool b) { Console.WriteLine(1); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(int a) { F(c => c + 1); }
public C(bool b) { Console.WriteLine(2); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Boolean b)"), syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_EditConstructorNotIncludingInitializers()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(a => a + 1);
int B = F(b => b + 1);
public C(int a) { F(c => c + 1); }
public C(bool b) : this(1) { Console.WriteLine(1); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(a => a + 1);
int B = F(b => b + 1);
public C(int a) { F(c => c + 1); }
public C(bool b) : this(1) { Console.WriteLine(2); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Boolean b)"))
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_RemoveCtorInitializer1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
unsafe public C(int a) { char* buffer = stackalloc char[16]; F(c => c + 1); }
public C(bool b) : this(1) { Console.WriteLine(1); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
unsafe public C(int a) { char* buffer = stackalloc char[16]; F(c => c + 1); }
public C(bool b) { Console.WriteLine(1); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Boolean b)"), syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_AddCtorInitializer1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(a => a + 1);
int B = F(b => b + 1);
public C(int a) { F(c => c + 1); }
public C(bool b) { Console.WriteLine(1); }
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(a => a + 1);
int B = F(b => b + 1);
public C(int a) { F(c => c + 1); }
public C(bool b) : this(1) { Console.WriteLine(1); }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Boolean b)"))
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_UpdateBaseCtorInitializerWithLambdas1()
{
var src1 = @"
using System;
class B
{
public B(int a) { }
}
class C : B
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(bool b)
: base(F(<N:0.2>c => c + 1</N:0.2>))
{
F(<N:0.3>d => d + 1</N:0.3>);
}
}
";
var src2 = @"
using System;
class B
{
public B(int a) { }
}
class C : B
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(<N:0.1>b => b + 1</N:0.1>);
public C(bool b)
: base(F(<N:0.2>c => c + 2</N:0.2>))
{
F(<N:0.3>d => d + 1</N:0.3>);
}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(ctor => ctor.ToTestDisplayString() == "C..ctor(System.Boolean b)"), syntaxMap[0])
]);
}
[Theory]
[InlineData("")]
[InlineData(" : base()")]
public void MemberInitializer_Update_Lambda_ConstructorWithMemberInitializers_ReplacingCustomWithSynthesized(string initializer)
{
var src1 = $$"""
using System;
class C : B
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
public C() {{initializer}}
{
}
}
""";
var src2 = @"
using System;
class C : B
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])
]);
}
[Theory, CombinatorialData]
public void MemberInitializer_Update_Lambda_ConstructorWithMemberInitializers_ReplacingCustomWithSynthesized_Primary(
[CombinatorialValues("", "()")] string initializer, bool isInsert)
{
var src1 = $$"""
using System;
class C() : B{{initializer}}
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
""";
var src2 = @"
using System;
class C : B
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
";
if (isInsert)
{
(src1, src2) = (src2, src1);
}
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])
]);
}
[Fact]
public void MemberInitializer_Update_Lambda_PartialDeclarationDelete_SingleDocument()
{
var src1 = @"
partial class C
{
int x = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int y = F(<N:0.1>a => a + 10</N:0.1>);
}
partial class C
{
public C() { }
static int F(Func<int, int> x) => 1;
}
";
var src2 = @"
partial class C
{
int x = F(<N:0.0>a => a + 1</N:0.0>);
}
partial class C
{
int y = F(<N:0.1>a => a + 10</N:0.1>);
static int F(Func<int, int> x) => 1;
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), syntaxMap[0]),
]);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
public void FieldInitializerUpdate_Lambdas_InsertPrimaryConstructorParameterUse(string keyword)
{
var src1 = keyword + " C(int x, int y) { public System.Func<int> F = new(() => x); }";
var src2 = keyword + " C(int x, int y) { public System.Func<int> F = new(() => x + y); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(c => c.Parameters is [_, _]), preserveLocalVariables: true),
]);
}
[Fact]
public void MemberInitializer_Update_ActiveStatements1()
{
var src1 = @"
using System;
class C
{
<AS:0>int A = <N:0.0>1</N:0.0>;</AS:0>
int B = 1;
public C(int a) { Console.WriteLine(1); }
public C(bool b) { Console.WriteLine(1); }
}
";
var src2 = @"
using System;
class C
{
<AS:0>int A = <N:0.0>1</N:0.0>;</AS:0>
int B = 2;
public C(int a) { Console.WriteLine(1); }
public C(bool b) { Console.WriteLine(1); }
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
var activeStatements = GetActiveStatements(src1, src2);
edits.VerifySemantics(
activeStatements,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[0], syntaxMap[0]),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors[1], syntaxMap[0]),
]);
}
[Fact]
public void MemberInitializer_Update_Partial_SemanticError()
{
var src1 = @"
partial class C
{
partial int P => 1;
}
partial class C
{
partial int P => 1;
}
";
var src2 = @"
partial class C
{
partial int P => 1;
}
partial class C
{
partial int P => 2;
public C() { }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => ((IPropertySymbol)c.GetMember<INamedTypeSymbol>("C").GetMembers("P").First()).GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true));
}
[Fact]
public void MemberInitializer_Rename_Property()
{
var src1 = "class C { int A { get; } = 1; }";
var src2 = "class C { int B { get; } = 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.A"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_A"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.B")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_B")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void MemberInitializer_Update_Reloadable_Partial()
{
var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int x = 1; }";
var srcB1 = "partial class C { int y = 1; }";
var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { int x = 2; }";
var srcB2 = "partial class C { int y = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
#endregion
#region Fields
[Fact]
public void Field_Rename()
{
var src1 = "class C { int a = 0; }";
var src2 = "class C { int b = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a = 0]@14 -> [b = 0]@14");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "b = 0", GetResource("field", "a")));
}
[Fact]
public void Field_Kind_Update()
{
var src1 = "class C { Action a; }";
var src2 = "class C { event Action a; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [Action a;]@10 -> [event Action a;]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.FieldKindUpdate, "event Action a", GetResource("field")));
}
[Theory]
[InlineData("static")]
[InlineData("volatile")]
[InlineData("const")]
public void Field_Modifiers_Update(string oldModifiers, string newModifiers = "")
{
if (oldModifiers != "")
{
oldModifiers += " ";
}
if (newModifiers != "")
{
newModifiers += " ";
}
var src1 = "class C { " + oldModifiers + "int F = 0; }";
var src2 = "class C { " + newModifiers + "int F = 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [" + oldModifiers + "int F = 0;]@10 -> [" + newModifiers + "int F = 0;]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "int F = 0", GetResource(oldModifiers.Contains("const") ? "const field" : "field")));
}
[Fact]
public void Field_Modifier_Add_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int F; }";
var srcA2 = "partial class C { static int F; }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.ModifiersUpdate, "F", FeaturesResources.field)
]),
DocumentResults(),
]);
}
[Fact]
public void Field_Attribute_Add_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int F; }";
var srcA2 = "partial class C { [System.Obsolete]int F; }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))
]),
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Field_FixedSize_Update()
{
var src1 = "struct S { public unsafe fixed byte a[1], b[2]; }";
var src2 = "struct S { public unsafe fixed byte a[2], b[3]; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a[1]]@36 -> [a[2]]@36",
"Update [b[2]]@42 -> [b[3]]@42");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.FixedSizeFieldUpdate, "a[2]", FeaturesResources.field),
Diagnostic(RudeEditKind.FixedSizeFieldUpdate, "b[3]", FeaturesResources.field));
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1120407")]
public void Field_Const_Update()
{
var src1 = "class C { const int x = 0; }";
var src2 = "class C { const int x = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [x = 0]@20 -> [x = 1]@20");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, "x = 1", FeaturesResources.const_field));
}
[Fact]
public void EventField_VariableDeclarator_Update()
{
var src1 = "class C { event Action a; }";
var src2 = "class C { event Action a = () => { }; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [a]@23 -> [a = () => { }]@23");
edits.VerifySemanticDiagnostics(capabilities:
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.AddStaticFieldToExistingType |
EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Field_Reorder()
{
var src1 = """
class C
{
int a = 0;
int b = 1;
int c;
}
""";
var src2 = """
class C
{
int c;
int a = 0;
int b = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics();
}
[Fact]
public void Field_Reorder_WithInitializer()
{
var src1 = """
class C
{
int a = 0;
int b = 1;
int c = 2;
}
""";
var src2 = """
class C
{
int c = 2;
int a = 0;
int b = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics([SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Theory]
[CombinatorialData]
public void Field_Reorder_WithInitializer_RemoveOrAdd(bool remove)
{
var src1 = $$"""
class C
{
int a = 0;
int b = 1;
int c{{(remove ? " = 2" : "")}};
}
""";
var src2 = $$"""
class C
{
int c{{(remove ? "" : " = 2")}};
int a = 0;
int b = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder, EditKind.Update);
edits.VerifySemantics([SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Fact]
public void Field_Reorder_Modifiers_Update()
{
var src1 = """
class C
{
int a = 0;
int b = 1;
static int c;
}
""";
var src2 = """
class C
{
int c;
int a = 0;
int b = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder, EditKind.Update);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, "int c", GetResource("field")));
}
[Fact]
public void Field_Reorder_Attribute_Update()
{
var attributeDecl = """
class A : System.Attribute {}
""";
var src1 = attributeDecl + """
class C
{
int a = 0;
int b = 1;
int c;
}
""";
var src2 = attributeDecl + """
class C
{
[A]int c;
int a = 0;
int b = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder, EditKind.Update);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.c"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Theory]
[InlineData("", "struct", RudeEditKind.InsertOrMoveStructMember)]
[InlineData("", "record struct", RudeEditKind.InsertOrMoveStructMember)]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "class", RudeEditKind.InsertOrMoveTypeWithLayoutMember)]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "record class", RudeEditKind.InsertOrMoveTypeWithLayoutMember)]
internal void Field_Reorder_TypeLayout(string attribute, string keyword, RudeEditKind rudeEditKind)
{
var src1 = attribute + keyword + " C { int a = 0; int b = 1; int c = 2; }";
var src2 = attribute + keyword + " C { int c = 2; int a = 0; int b = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemanticDiagnostics(
Diagnostic(rudeEditKind, "int c = 2", GetResource("field"), GetResource(keyword)));
}
[Fact]
public void Field_Reorder_Reloadable()
{
var src1 = ReloadableAttributeSrc + """
[CreateNewOnMetadataUpdate]
struct C
{
int a = 0;
int b = 1;
int c;
}
""";
var src2 = ReloadableAttributeSrc + """
[CreateNewOnMetadataUpdate]
struct C
{
int c;
int a = 0;
int b = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Field_Insert()
{
var src1 = "class C { }";
var src2 = "class C { int a = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [int a = 1;]@10",
"Insert [int a = 1]@10",
"Insert [a = 1]@14");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.a")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Field_Insert_IntoStruct()
{
var src1 = @"
struct S
{
public int a;
public S(int z) { this = default(S); a = z; }
}
";
var src2 = @"
struct S
{
public int a;
private int b;
private static int c;
private static int f = 1;
private event System.Action d;
public S(int z) { this = default(S); a = z; }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "b", GetResource("field"), GetResource("struct")),
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "c", GetResource("field"), GetResource("struct")),
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "f = 1", GetResource("field"), GetResource("struct")),
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "d", GetResource("event field"), GetResource("struct")));
}
[Fact]
public void Field_Insert_IntoLayoutClass_Auto()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Auto)]
class C
{
private int a;
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Auto)]
class C
{
private int a;
private int b;
private int c;
private static int d;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.b")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.c")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.d")),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddStaticFieldToExistingType);
}
[Fact]
public void Field_Insert_IntoLayoutClass_Explicit()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Explicit)]
class C
{
[FieldOffset(0)]
private int a;
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Explicit)]
class C
{
[FieldOffset(0)]
private int a;
[FieldOffset(0)]
private int b;
[FieldOffset(4)]
private int c;
private static int d;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "b", GetResource("field"), GetResource("class")),
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "c", GetResource("field"), GetResource("class")),
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "d", GetResource("field"), GetResource("class")));
}
[Fact]
public void Field_Insert_IntoLayoutClass_Sequential()
{
var src1 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C
{
private int a;
}
";
var src2 = @"
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C
{
private int a;
private int b;
private int c;
private static int d;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "b", GetResource("field"), GetResource("class")),
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "c", GetResource("field"), GetResource("class")),
Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "d", GetResource("field"), GetResource("class")));
}
[Fact]
public void Field_Insert_WithInitializersAndLambdas1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
public C()
{
F(<N:0.1>c => c + 1</N:0.1>);
}
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(b => b + 1); // new field
public C()
{
F(<N:0.1>c => c + 1</N:0.1>);
}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.B")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Field_Insert_ConstructorReplacingImplicitConstructor_WithInitializersAndLambdas()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(b => b + 1); // new field
public C() // new ctor replacing existing implicit constructor
{
F(c => c + 1);
}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.B")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])
],
capabilities:
EditAndContinueCapabilities.AddInstanceFieldToExistingType |
EditAndContinueCapabilities.AddStaticFieldToExistingType |
EditAndContinueCapabilities.AddMethodToExistingType |
EditAndContinueCapabilities.NewTypeDefinition);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "c", GetResource("lambda")),
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "B = F(b => b + 1)", GetResource("field")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2504")]
public void Field_Insert_ParameterlessConstructorInsert_WithInitializersAndLambdas()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
public C(int x) {}
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
public C(int x) {}
public C() // new ctor
{
F(c => c + 1);
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "public C()")],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
// TODO (bug https://github.com/dotnet/roslyn/issues/2504):
//edits.VerifySemantics(
// ActiveStatementsDescription.Empty,
// new[]
// {
// SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<NamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])
// });
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2504")]
public void Field_Insert_ConstructorInsert_WithInitializersAndLambdas1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(<N:0.0>a => a + 1</N:0.0>);
int B = F(b => b + 1); // new field
public C(int x) // new ctor
{
F(c => c + 1);
}
}
";
var edits = GetTopEdits(src1, src2);
_ = GetSyntaxMap(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertConstructorToTypeWithInitializersWithLambdas, "public C(int x)"));
// TODO (bug https://github.com/dotnet/roslyn/issues/2504):
//edits.VerifySemantics(
// ActiveStatementsDescription.Empty,
// new[]
// {
// SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.B")),
// SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<NamedTypeSymbol>("C").Constructors.Single(), syntaxMap[0])
// });
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/2504")]
public void Field_Insert_ConstructorInsert_WithInitializersButNoExistingLambdas1()
{
var src1 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(null);
}
";
var src2 = @"
using System;
class C
{
static int F(Func<int, int> x) => 1;
int A = F(null);
int B = F(b => b + 1); // new field
public C(int x) // new ctor
{
F(c => c + 1);
}
}
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.B")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").Constructors.Single()),
SemanticEdit(SemanticEditKind.Delete, c => c.GetParameterlessConstructor("C"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType | EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Field_Insert_NotSupportedByRuntime()
{
var src1 = "class C { }";
var src2 = "class C { public int a = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "a = 1", FeaturesResources.field)],
capabilities: EditAndContinueCapabilities.AddStaticFieldToExistingType);
}
[Fact]
public void Field_Insert_Static_NotSupportedByRuntime()
{
var src1 = "class C { }";
var src2 = "class C { public static int a = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "a = 1", FeaturesResources.field)],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Field_Attribute_Add_NotSupportedByRuntime()
{
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.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public int a = 1, x = 1", FeaturesResources.field),
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "public int a = 1, x = 1", FeaturesResources.field),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Field_Attribute_Add()
{
var src1 = @"
class C
{
public int a, b;
}";
var src2 = @"
class C
{
[System.Obsolete]public int a, b;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.a")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.b"))
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Field_Attribute_Add_WithInitializer()
{
var src1 = @"
class C
{
int a;
}";
var src2 = @"
class C
{
[System.Obsolete]int a = 0;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.a")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Field_Attribute_DeleteInsertUpdate_WithInitializer()
{
var srcA1 = "partial class C { int a = 1; }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { [System.Obsolete]int a = 2; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.a")),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Field_Delete1()
{
var src1 = "class C { int a = 1; }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [int a = 1;]@10",
"Delete [int a = 1]@10",
"Delete [a = 1]@14");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C", DeletedSymbolDisplay(FeaturesResources.field, "a")));
}
[Fact]
public void Field_UnsafeModifier_Update()
{
var src1 = "struct Node { unsafe Node* left; }";
var src2 = "struct Node { Node* left; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [unsafe Node* left;]@14 -> [Node* left;]@14");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Field_ModifierAndType_Update()
{
var src1 = "struct Node { unsafe Node* left; }";
var src2 = "struct Node { Node left; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [unsafe Node* left;]@14 -> [Node left;]@14",
"Update [Node* left]@21 -> [Node left]@14");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "Node left", FeaturesResources.field));
}
[Theory]
[InlineData("string", "string?")]
[InlineData("object", "dynamic")]
[InlineData("(int a, int b)", "(int a, int c)")]
public void Field_Type_Update_RuntimeTypeUnchanged(string oldType, string newType)
{
var src1 = "class C { " + oldType + " F, G; }";
var src2 = "class C { " + newType + " F, G; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.G")));
}
[Theory]
[InlineData("int", "string")]
[InlineData("int", "int?")]
[InlineData("(int a, int b)", "(int a, double b)")]
public void Field_Type_Update_RuntimeTypeChanged(string oldType, string newType)
{
var src1 = "class C { " + oldType + " F, G; }";
var src2 = "class C { " + newType + " F, G; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, newType + " F, G", GetResource("field")),
Diagnostic(RudeEditKind.TypeUpdate, newType + " F, G", GetResource("field")));
}
[Fact]
public void Field_Type_Update_ReorderRemoveAdd()
{
var src1 = "class C { int F, G, H; bool U; }";
var src2 = "class C { string G, F; double V, U; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int F, G, H]@10 -> [string G, F]@10",
"Reorder [G]@17 -> @17",
"Update [bool U]@23 -> [double V, U]@23",
"Insert [V]@30",
"Delete [H]@20");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.TypeUpdate, "string G, F", FeaturesResources.field),
Diagnostic(RudeEditKind.TypeUpdate, "string G, F", FeaturesResources.field),
Diagnostic(RudeEditKind.TypeUpdate, "double V, U", FeaturesResources.field),
Diagnostic(RudeEditKind.Delete, "string G, F", DeletedSymbolDisplay(FeaturesResources.field, "H")));
}
#endregion
#region Properties
[Theory]
[InlineData("static")]
[InlineData("virtual")]
[InlineData("abstract")]
[InlineData("override")]
[InlineData("sealed override", "override")]
public void Property_Modifiers_Update(string oldModifiers, string newModifiers = "")
{
if (oldModifiers != "")
{
oldModifiers += " ";
}
if (newModifiers != "")
{
newModifiers += " ";
}
var src1 = "class C { " + oldModifiers + "int F => 0; }";
var src2 = "class C { " + newModifiers + "int F => 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [" + oldModifiers + "int F => 0;]@10 -> [" + newModifiers + "int F => 0;]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "int F", FeaturesResources.property_));
}
[Fact]
public void Property_ExpressionBody_Rename()
{
var src1 = "class C { int P => 1; }";
var src2 = "class C { int Q => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Q")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Q")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_ExpressionBody_Update()
{
var src1 = "class C { int P => 1; }";
var src2 = "class C { int P => 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P => 1;]@10 -> [int P => 2;]@10",
"Update [=> 1]@16 -> [=> 2]@16");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/48628")]
public void Property_ExpressionBody_ModifierUpdate()
{
var src1 = "class C { int P => 1; }";
var src2 = "class C { unsafe int P => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [int P => 1;]@10 -> [unsafe int P => 1;]@10");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Property_ExpressionBodyToBlockBody1()
{
var src1 = "class C { int P => 1; }";
var src2 = "class C { int P { get { return 2; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P => 1;]@10 -> [int P { get { return 2; } }]@10",
"Insert [{ get { return 2; } }]@16",
"Insert [get { return 2; }]@18",
"Delete [=> 1]@16");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false)
]);
}
[Fact]
public void Property_ExpressionBodyToBlockBody2()
{
var src1 = "class C { int P => 1; }";
var src2 = "class C { int P { get { return 2; } set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P => 1;]@10 -> [int P { get { return 2; } set { } }]@10",
"Insert [{ get { return 2; } set { } }]@16",
"Insert [get { return 2; }]@18",
"Insert [set { }]@36",
"Delete [=> 1]@16");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_P"), preserveLocalVariables: false)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_BlockBodyToExpressionBody1()
{
var src1 = "class C { int P { get { return 2; } } }";
var src2 = "class C { int P => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P { get { return 2; } }]@10 -> [int P => 1;]@10",
"Insert [=> 1]@16",
"Delete [{ get { return 2; } }]@16",
"Delete [get { return 2; }]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false)
]);
}
[Fact]
public void Property_BlockBodyToExpressionBody2()
{
var src1 = "class C { int P { get { return 2; } set { } } }";
var src2 = "class C { int P => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P { get { return 2; } set { } }]@10 -> [int P => 1;]@10",
"Insert [=> 1]@16",
"Delete [{ get { return 2; } set { } }]@16",
"Delete [get { return 2; }]@18",
"Delete [set { }]@36");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_ExpressionBodyToGetterExpressionBody()
{
var src1 = "class C { int P => 1; }";
var src2 = "class C { int P { get => 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P => 1;]@10 -> [int P { get => 2; }]@10",
"Insert [{ get => 2; }]@16",
"Insert [get => 2;]@18",
"Delete [=> 1]@16");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_GetterExpressionBodyToExpressionBody()
{
var src1 = "class C { int P { get => 2; } }";
var src2 = "class C { int P => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P { get => 2; }]@10 -> [int P => 1;]@10",
"Insert [=> 1]@16",
"Delete [{ get => 2; }]@16",
"Delete [get => 2;]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_GetterBlockBodyToGetterExpressionBody()
{
var src1 = "class C { int P { get { return 2; } } }";
var src2 = "class C { int P { get => 2; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get { return 2; }]@18 -> [get => 2;]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_SetterBlockBodyToSetterExpressionBody()
{
var src1 = "class C { int P { set { } } }";
var src2 = "class C { int P { set => F(); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [set { }]@18 -> [set => F();]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").SetMethod),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_InitBlockBodyToInitExpressionBody()
{
var src1 = "class C { int P { init { } } }";
var src2 = "class C { int P { init => F(); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [init { }]@18 -> [init => F();]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").SetMethod, preserveLocalVariables: false),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_GetterExpressionBodyToGetterBlockBody()
{
var src1 = "class C { int P { get => 2; } }";
var src2 = "class C { int P { get { return 2; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get => 2;]@18 -> [get { return 2; }]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_GetterBlockBodyWithSetterToGetterExpressionBodyWithSetter()
{
var src1 = "class C { int P { get => 2; set { Console.WriteLine(0); } } }";
var src2 = "class C { int P { get { return 2; } set { Console.WriteLine(0); } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get => 2;]@18 -> [get { return 2; }]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Property_GetterExpressionBodyWithSetterToGetterBlockBodyWithSetter()
{
var src1 = "class C { int P { get { return 2; } set { Console.WriteLine(0); } } }";
var src2 = "class C { int P { get => 2; set { Console.WriteLine(0); } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get { return 2; }]@18 -> [get => 2;]@18");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P"), preserveLocalVariables: false),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P"), preserveLocalVariables: false)
]);
}
[Fact]
public void Property_Rename1()
{
var src1 = "class C { int P { get { return 1; } } }";
var src2 = "class C { int Q { get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Q")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Q"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int Q", FeaturesResources.property_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Rename2()
{
var interfaces = """
interface I
{
int P { get; }
}
interface J
{
int P { get; }
}
""";
var src1 = "class C { int I.P { get { return 1; } } } " + interfaces;
var src2 = "class C { int J.P { get { return 1; } } } " + interfaces;
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "int J.P", GetResource("property", "I.P")));
}
[Fact]
public void Property_Rename3()
{
var src1 = "class C { int P { get { return 1; } set { } } }";
var src2 = "class C { int Q { get { return 1; } set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Q")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Q")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Q")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Rename4()
{
var src1 = "class C { int P { get; set; } }";
var src2 = "class C { int Q { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Q")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Q")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Q")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Property_Rename_SemanticError_NoAccessors()
{
var src1 = "class C { System.Action E { } }";
var src2 = "class C { System.Action F { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.F"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Property_RenameAndUpdate()
{
var src1 = "class C { int P { get { return 1; } } }";
var src2 = "class C { int Q { get { return 2; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Q")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.Q"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Rename_Stackalloc()
{
var src1 = "class C { int G(Span<char> s) => 0; int P { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }";
var src2 = "class C { int G(Span<char> s) => 0; int Q { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("property getter")),
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("property setter"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Theory]
[InlineData("class")]
[InlineData("struct")]
public void Property_Delete(string keyword)
{
var src1 = keyword + " C { int P { get { return 1; } set { } } }";
var src2 = keyword + " C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Delete_GetOnly()
{
var src1 = "class C { int P { get { return 1; } } }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Delete_Auto_Class()
{
var src1 = "class C { int P { get; set; } }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Delete_Auto_Struct()
{
var src1 = "struct C { int P { get; set; } }";
var src2 = "struct C { }";
var edits = GetTopEdits(src1, src2);
// We do not report rude edits when deleting auto-properties/events of a type with a sequential or explicit layout.
// The properties are updated to throw and the backing field remains in the type.
// The deleted field will remain unused since adding the property/event back is a rude edit.
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void PropertyAccessorDelete1()
{
var src1 = "class C { int P { get { return 1; } set { } } }";
var src2 = "class C { int P { get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void PropertyAccessorDelete2()
{
var src1 = "class C { int P { set { } get { return 1; } } }";
var src2 = "class C { int P { set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Reorder_Auto()
{
var src1 = """
class C
{
int P { get; } = 0;
int Q { get; } = 1;
int R { get; }
}
""";
var src2 = """
class C
{
int R { get; }
int P { get; } = 0;
int Q { get; } = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics();
}
[Fact]
public void Property_Reorder_Auto_WithInitializer()
{
var src1 = """
class C
{
int P { get; } = 0;
int Q { get; } = 1;
int R { get; } = 2;
}
""";
var src2 = """
class C
{
int R { get; } = 2;
int P { get; } = 0;
int Q { get; } = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics([SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Theory]
[InlineData("", "struct")]
[InlineData("", "record struct")]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "class")]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "record class")]
internal void Property_Reorder_TypeLayout(string attribute, string keyword)
{
var src1 = attribute + keyword + """
C
{
int P => 0;
int Q => 1;
int R => 2;
}
""";
var src2 = attribute + keyword + """
C
{
int R => 2;
int P => 0;
int Q => 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics();
}
[Theory]
[InlineData("", "struct", RudeEditKind.InsertOrMoveStructMember)]
[InlineData("", "record struct", RudeEditKind.InsertOrMoveStructMember)]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "class", RudeEditKind.InsertOrMoveTypeWithLayoutMember)]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "record class", RudeEditKind.InsertOrMoveTypeWithLayoutMember)]
internal void Property_Reorder_Auto_TypeLayout(string attribute, string keyword, RudeEditKind rudeEditKind)
{
var src1 = attribute + keyword + " C { int P { get; } int Q { get; } int R { get; } }";
var src2 = attribute + keyword + " C { int R { get; } int P { get; } int Q { get; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemanticDiagnostics(
Diagnostic(rudeEditKind, "int R", GetResource("auto-property"), GetResource(keyword)));
}
[Fact]
public void Property_Reorder_Auto_Reloadable()
{
var src1 = ReloadableAttributeSrc + """
[CreateNewOnMetadataUpdate]
struct C
{
int P { get; } = 0;
int Q { get; } = 1;
int R { get; }
}
""";
var src2 = ReloadableAttributeSrc + """
[CreateNewOnMetadataUpdate]
struct C
{
int R { get; }
int P { get; } = 0;
int Q { get; } = 1;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void PropertyAccessor_Reorder_GetSet()
{
var src1 = "class C { int P { get { return 1; } set { } } }";
var src2 = "class C { int P { set { } get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [set { }]@36 -> @18");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void PropertyAccessor_Reorder_GetInit()
{
var src1 = "class C { int P { get { return 1; } init { } } }";
var src2 = "class C { int P { init { } get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [init { }]@36 -> @18");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Property_Update_Type()
{
var src1 = "class C { byte P { get; set; } }";
var src2 = "class C { char P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_P")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "char P", GetResource("auto-property"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Update_Type_Stackalloc()
{
// only type is changed, no changes to the accessors (not even whitespace)
var src1 = "class C { byte G(Span<char> s) => 0; byte P { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }";
var src2 = "class C { byte G(Span<char> s) => 0; long P { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("property getter")),
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("property setter"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Property_Update_Type_WithBodies()
{
var src1 = "class C { int P { get { return 1; } set { } } }";
var src2 = "class C { char P { get { return 'a'; } set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_P")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.P")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "char P", FeaturesResources.property_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Update_Type_TypeLayout()
{
var src1 = "struct C { byte P { get; } }";
var src2 = "struct C { long P { get; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertOrMoveStructMember, "long P", GetResource("auto-property"), GetResource("struct"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Property_Update_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.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int P", GetResource("auto-property"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact(Skip = "https://github.com/dotnet/roslyn/issues/68458")]
[WorkItem("https://github.com/dotnet/roslyn/issues/68458")]
public void Property_Update_AddAttribute_FieldTarget()
{
var src1 = "class C { int P { get; set; } }";
var src2 = "class C { [field: System.Obsolete]int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.P"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int P", FeaturesResources.property_)],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[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.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_P")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "get", CSharpFeaturesResources.property_getter)],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[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.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").SetMethod)],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "set", CSharpFeaturesResources.property_setter)],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Insert()
{
var src1 = "class C { }";
var src2 = "class C { int P { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").GetMember("P"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Insert_Static()
{
var src1 = "class C { }";
var src2 = "class C { static int P { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").GetMember("P"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void PropertyInsert_NotSupportedByRuntime()
{
var src1 = "class C { }";
var src2 = "class C { int P { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "int P", FeaturesResources.property_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/992578")]
public void Property_Insert_Incomplete()
{
var src1 = "class C { }";
var src2 = "class C { public int P { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [public int P { }]@10", "Insert [{ }]@23");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/835827")]
public void Property_Insert_PInvoke()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
class C
{
}";
var src2 = @"
using System;
using System.Runtime.InteropServices;
class C
{
private static extern int P1 { [DllImport(""x.dll"")]get; }
private static extern int P2 { [DllImport(""x.dll"")]set; }
private static extern int P3 { [DllImport(""x.dll"")]get; [DllImport(""x.dll"")]set; }
}
";
var edits = GetTopEdits(src1, src2);
// CLR doesn't support methods without a body
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertExtern, "private static extern int P1", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertExtern, "private static extern int P2", FeaturesResources.property_),
Diagnostic(RudeEditKind.InsertExtern, "private static extern int P3", FeaturesResources.property_));
}
[Theory]
[InlineData("", "struct", RudeEditKind.InsertOrMoveStructMember)]
[InlineData("[System.Runtime.InteropServices.StructLayoutAttribute(System.Runtime.InteropServices.LayoutKind.Sequential)]", "class", RudeEditKind.InsertOrMoveTypeWithLayoutMember)]
internal void Property_Insert_TypeLayout(string attribute, string type, RudeEditKind rudeEditKind)
{
var src1 = attribute + type + """
S
{
public int a;
public S(int z) { a = z; }
}
""";
var src2 = attribute + type + """
S
{
public int a;
int c { get; set; }
int e { get { return 0; } set { } }
int g { get; } = 1;
int i { get; set; } = 1;
int k => 1;
int l { get => 1; set {} }
int m { get => 1; set => k; }
public S(int z) { a = z; }
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(rudeEditKind, "int c", GetResource("auto-property"), GetResource(type)),
Diagnostic(rudeEditKind, "int g", GetResource("auto-property"), GetResource(type)),
Diagnostic(rudeEditKind, "int i", GetResource("auto-property"), GetResource(type)));
}
[Fact]
public void Property_Insert_IntoStruct_Static()
{
var src1 = @"
struct S
{
public int a;
public S(int z) { a = z; }
}
";
var src2 = @"
struct S
{
public int a;
static int c { get; set; }
static int e { get { return 0; } set { } }
static int g { get; } = 1;
static int i { get; set; } = 1;
static int k => 1;
static int l { get => 1; set {} }
static int m { get => 1; set => k; }
public S(int z) { a = z; }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InsertOrMoveStructMember, "static int c", 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")));
}
[Fact]
public void Property_Insert_Auto()
{
var src1 = "class C { }";
var src2 = "class C { int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").GetMember("P"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Property_Insert_Auto_Static()
{
var src1 = "class C { }";
var src2 = "class C { static int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").GetMember("P"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddStaticFieldToExistingType);
}
[Fact]
public void Property_Insert_Auto_GenericType()
{
var src1 = "class C<T> { }";
var src2 = "class C<T> { int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("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()
{
var src1 = "class C<T> { }";
var src2 = "class C<T> { static int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("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]
public void Property_Private_AccessorAdd()
{
var src1 = "class C { int _p; int P { get { return 1; } } }";
var src2 = "class C { int _p; int P { get { return 1; } set { _p = value; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [set { _p = value; }]@44");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755975")]
public void Property_Private_AccessorDelete()
{
var src1 = "class C { int _p; int P { get { return 1; } set { _p = value; } } }";
var src2 = "class C { int _p; int P { get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [set { _p = value; }]@44");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Private_AccessorAdd1()
{
var src1 = "class C { int P { get; } }";
var src2 = "class C { int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [set;]@23");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Auto_Private_AccessorAdd2()
{
var src1 = "class C { public int P { get; } }";
var src2 = "class C { public int P { get; private set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [private set;]@30");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Auto_Private_AccessorAdd4()
{
var src1 = "class C { public int P { get; } }";
var src2 = "class C { public int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [set;]@30");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Auto_Private_AccessorAdd5()
{
var src1 = "class C { public int P { get; } }";
var src2 = "class C { public int P { get; internal set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [internal set;]@30");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Auto_Private_AccessorAdd6()
{
var src1 = "class C { int P { get; } = 1; }";
var src2 = "class C { int P { get; set; } = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [set;]@23");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Auto_Private_AccessorAdd_Init()
{
var src1 = "class C { int P { get; } = 1; }";
var src2 = "class C { int P { get; init; } = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [init;]@23");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/755975")]
public void Property_Auto_Private_AccessorDelete_Get()
{
var src1 = "class C { int P { get; set; } }";
var src2 = "class C { int P { set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [get;]@18");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_P")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Accessor_SetToInit()
{
var src1 = "class C { int P { get; set; } }";
var src2 = "class C { int P { get; init; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [set;]@23 -> [init;]@23");
// not allowed since it changes the backing field readonly-ness and the signature of the setter (modreq)
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.AccessorKindUpdate, "init"));
}
[Fact]
public void Property_Auto_Accessor_InitToSet()
{
var src1 = "class C { int P { get; init; } }";
var src2 = "class C { int P { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [init;]@23 -> [set;]@23");
// not allowed since it changes the backing field readonly-ness and the signature of the setter (modreq)
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.AccessorKindUpdate, "set"));
}
[Fact]
public void Propert_Auto_Private_AccessorDelete_Set()
{
var src1 = "class C { int P { get; set; } = 1; }";
var src2 = "class C { int P { get; } = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [set;]@23");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C..ctor"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Propert_Auto_Private_AccessorDelete_Init()
{
var src1 = "class C { int P { get; init; } = 1; }";
var src2 = "class C { int P { get; } = 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [init;]@23");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_P"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C..ctor"), preserveLocalVariables: true),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Accessor_Update()
{
var src1 = "class C { int P { get; } }";
var src2 = "class C { int P { set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get;]@18 -> [set;]@18");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.AccessorKindUpdate, "set"));
}
[Fact]
public void Property_Auto_Accessor_Update_ReplacingExplicitWithImplicit()
{
var src1 = "class C { int P { get => 1; } }";
var src2 = "class C { int P { get; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod)
],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "get", GetResource("property getter"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Accessor_Update_ReplacingExplicitWithImplicit_GenericType()
{
var src1 = "class C<T> { int P { get => 1; } }";
var src2 = "class C<T> { int P { get; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod)
],
capabilities:
EditAndContinueCapabilities.AddInstanceFieldToExistingType |
EditAndContinueCapabilities.GenericAddFieldToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.InsertNotSupportedByRuntime, "get", GetResource("property getter"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Accessor_Update_ReplacingImplicitWithExplicit()
{
var src1 = "class C { int P { get; } }";
var src2 = "class C { int P { get => 1; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Accessor_Update_ReplacingImplicitWithExplicit_GenericType()
{
var src1 = "class C<T> { int P { get; } }";
var src2 = "class C<T> { int P { get => 1; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod)
],
capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "get", GetResource("property getter"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Accessor_Update_ReplacingImplicitWithExpressionBodiedProperty()
{
var src1 = "class C { int P { get; } }";
var src2 = "class C { int P => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Auto_Accessor_Update_ReplacingImplicitWithExpressionBodiedProperty_GenericType()
{
var src1 = "class C<T> { int P { get; } }";
var src2 = "class C<T> { int P => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").GetMethod)
],
capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "int P", GetResource("auto-property")),
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "int P", GetResource("property getter"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_ReadOnlyRef_Insert()
{
var src1 = "class Test { }";
var src2 = "class Test { ref readonly int P { get; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [ref readonly int P { get; }]@13",
"Insert [{ get; }]@32",
"Insert [get;]@34");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Property_ReadOnlyRef_Update()
{
var src1 = "class Test { int P { get; } }";
var src2 = "class Test { ref readonly int P { get; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int P { get; }]@13 -> [ref readonly int P { get; }]@13");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("Test.P"), deletedSymbolContainerProvider: c => c.GetMember("Test")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("Test.get_P"), deletedSymbolContainerProvider: c => c.GetMember("Test")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("Test.P")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("Test.get_P")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "ref readonly int P", GetResource("auto-property"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Property_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int P { get => 1; set { } } }";
var srcA2 = "partial class C { int P { get => 1; set { } } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").SetMethod)
]),
DocumentResults(),
]);
}
[Fact]
public void PropertyInit_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int Q { get => 1; init { } }}";
var srcA2 = "partial class C { int Q { get => 1; init { } }}";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("Q").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("Q").SetMethod)
]),
DocumentResults(),
]);
}
[Fact]
public void Property_Auto_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int P { get; set; } int Q { get; init; } }";
var srcA2 = "partial class C { int P { get; set; } int Q { get; init; } }";
var srcB2 = "partial class C { }";
// Accessors need to be updated even though they do not have an explicit body.
// There is still a sequence point generated for them whose location needs to be updated.
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("Q").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("Q").SetMethod),
]),
DocumentResults(),
]);
}
[Fact]
public void Property_AutoWithInitializer_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int P { get; set; } = 1; }";
var srcA2 = "partial class C { int P { get; set; } = 1; }";
var srcB2 = "partial class C { }";
// Accessors need to be updated even though they do not have an explicit body.
// There is still a sequence point generated for them whose location needs to be updated.
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
DocumentResults(),
]);
}
[Fact]
public void Property_WithExpressionBody_Partial_InsertDeleteUpdate()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int P => 1; }";
var srcA2 = "partial class C { int P => 2; }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("P").GetMethod)]),
DocumentResults(),
]);
}
[Fact]
public void Property_Auto_ReadOnly_Add()
{
var src1 = @"
struct S
{
int P { get; }
}";
var src2 = @"
struct S
{
readonly int P { get; }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Property_InMutableStruct_ReadOnly_Add()
{
var src1 = @"
struct S
{
int P1 { get => 1; }
int P2 { get => 1; set {}}
int P3 { get => 1; set {}}
int P4 { get => 1; set {}}
}";
var src2 = @"
struct S
{
readonly int P1 { get => 1; }
int P2 { readonly get => 1; set {}}
int P3 { get => 1; readonly set {}}
readonly int P4 { get => 1; set {}}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.get_P1")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.get_P2")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.get_P4")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.set_P2")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.set_P3")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.set_P4")),
]);
}
[Fact]
public void Property_InReadOnlyStruct_ReadOnly_Add()
{
// indent to align accessor bodies and avoid updates caused by sequence point location changes
var src1 = @"
readonly struct S
{
int P1 { get => 1; }
int P2 { get => 1; set {}}
int P3 { get => 1; set {}}
int P4 { get => 1; set {}}
}";
var src2 = @"
readonly struct S
{
readonly int P1 { get => 1; }
int P2 { readonly get => 1; set {}}
int P3 { get => 1; readonly set {}}
readonly int P4 { get => 1; set {}}
}";
var edits = GetTopEdits(src1, src2);
// updates only for accessors whose modifiers were explicitly updated
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("S").GetMember<IPropertySymbol>("P2").GetMethod, preserveLocalVariables: false),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("S").GetMember<IPropertySymbol>("P3").SetMethod, preserveLocalVariables: false)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69317")]
public void Property_Rename_ShadowingPrimaryParameter()
{
var src1 = @"
class C(int A, int B)
{
public int B { get; init; }
public int F() => B;
}
";
var src2 = @"
class C(int A, int B)
{
public int D { get; init; }
public int F() => B;
}
";
var edits = GetTopEdits(src1, src2);
// TODO: https://github.com/dotnet/roslyn/issues/69317
// Update D getter/setter to use deleted B property
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.B"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_B"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_B"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.D")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_D")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_D")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/69317")]
[WorkItem("https://github.com/dotnet/roslyn/issues/69216")]
public void Property_Rename_ShadowingPrimaryParameter_WithInitializer()
{
var src1 = @"
class C(int A, int B)
{
public int B { get; init; } = B;
public int F() => B;
}
";
var src2 = @"
class C(int A, int B)
{
public int D { get; init; } = B;
public int F() => B;
}
";
var edits = GetTopEdits(src1, src2);
// TODO: https://github.com/dotnet/roslyn/issues/69317
// Update D getter/setter to use deleted B property
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetPrimaryConstructor("C"), preserveLocalVariables: true),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.B"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_B"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_B"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.D")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_D")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_D")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Property_Partial_DeleteInsert_DefinitionPart()
{
var srcA1 = "partial class C { partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcC1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { partial int P => 1; }";
var srcC2 = "partial class C { partial int P { get; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C")]),
]);
}
[Fact]
public void Property_Partial_DeleteInsert_ImplementationPart()
{
var srcA1 = "partial class C { partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcC1 = "partial class C { }";
var srcA2 = "partial class C { partial int P { get; } }";
var srcB2 = "partial class C { }";
var srcC2 = "partial class C { partial int P => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C")]),
]);
}
[Fact]
public void Property_Partial_Swap_ImplementationAndDefinitionParts()
{
var srcA1 = "partial class C { partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcA2 = "partial class C { partial int P => 1; }";
var srcB2 = "partial class C { partial int P { get; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C")]),
DocumentResults(),
]);
}
[Fact]
public void Property_Partial_DeleteBoth()
{
var srcA1 = "partial class C { partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C")
]),
]);
}
[Fact]
public void Property_Partial_DeleteInsertBoth()
{
var srcA1 = "partial class C { partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcC1 = "partial class C { }";
var srcD1 = "partial class C { }";
var srcA2 = "partial class C { }";
var srcB2 = "partial class C { }";
var srcC2 = "partial class C { partial int P { get; } }";
var srcD2 = "partial class C { partial int P => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2), GetTopEdits(srcC1, srcC2), GetTopEdits(srcD1, srcD2)],
[
DocumentResults(),
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C")]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C")])
]);
}
[Fact]
public void Property_Partial_Insert()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = "partial class C { partial int P { get; } }";
var srcB2 = "partial class C { partial int P => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart)]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Property_Partial_Insert_Reloadable()
{
var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { }";
var srcB1 = "partial class C { }";
var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C { partial int P { get; } }";
var srcB2 = "partial class C { partial int P { get => 1; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")]),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void Property_Partial_Update_Attribute_Definition()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var srcA1 = attribute +
"partial class C { [A(1)]partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcA2 = attribute +
"partial class C { [A(2)]partial int P { get; } }";
var srcB2 = "partial class C { partial int P => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, partialType: "C")]),
DocumentResults(),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Property_Partial_Update_Attribute_Implementation()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var srcA1 = attribute +
"partial class C { partial int P { get; } }";
var srcB1 = "partial class C { [A(1)]partial int P => 1; }";
var srcA2 = attribute +
"partial class C { partial int P { get; } }";
var srcB2 = "partial class C { [A(2)]partial int P => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, partialType: "C"),
// Updating the accessor is superfluous.
// It is added since we see an update to an expression bodied property. We don't distinguish between update to the body and update to an attribute.
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Property_Partial_Update_Attribute_DefinitionAndImplementation()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var srcA1 = attribute +
"partial class C { [A(1)]partial int P { get; } }";
var srcB1 = "partial class C { [A(1)]partial int P => 1; }";
var srcA2 = attribute +
"partial class C { [A(2)]partial int P { get; } }";
var srcB2 = "partial class C { [A(2)]partial int P => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, partialType: "C")]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Property_Partial_DeleteInsert_DefinitionWithAttributeChange()
{
var attribute = """
public class A : System.Attribute {}
""";
var srcA1 = attribute +
"partial class C { [A]partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcA2 = attribute +
"partial class C { }";
var srcB2 = "partial class C { partial int P => 1; partial int P { get; } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Property_Partial_Parameter_TypeChange()
{
var srcA1 = "partial class C { partial int P { get; } }";
var srcB1 = "partial class C { partial int P => 1; }";
var srcA2 = "partial class C { partial long P { get; } }";
var srcB2 = "partial class C { partial long P => 1; }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, partialType: "C"),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.get_P").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IPropertySymbol>("C.P").PartialImplementationPart, partialType: "C"),
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "partial long P", GetResource("property"))]),
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "partial long P", GetResource("property"))]),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
#endregion
#region Indexers
[Theory]
[InlineData("virtual")]
[InlineData("abstract")]
[InlineData("override")]
[InlineData("sealed override", "override")]
public void Indexer_Modifiers_Update(string oldModifiers, string newModifiers = "")
{
if (oldModifiers != "")
{
oldModifiers += " ";
}
if (newModifiers != "")
{
newModifiers += " ";
}
var src1 = "class C { " + oldModifiers + "int this[int a] => 0; }";
var src2 = "class C { " + newModifiers + "int this[int a] => 0; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [" + oldModifiers + "int this[int a] => 0;]@10 -> [" + newModifiers + "int this[int a] => 0;]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "int this[int a]", FeaturesResources.indexer_));
}
[Fact]
public void Indexer_GetterUpdate()
{
var src1 = "class C { int this[int a] { get { return 1; } } }";
var src2 = "class C { int this[int a] { get { return 2; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [get { return 1; }]@28 -> [get { return 2; }]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"), preserveLocalVariables: false)
]);
}
[Fact]
public void Indexer_SetterUpdate()
{
var src1 = "class C { int this[int a] { get { return 1; } set { System.Console.WriteLine(value); } } }";
var src2 = "class C { int this[int a] { get { return 1; } set { System.Console.WriteLine(value + 1); } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [set { System.Console.WriteLine(value); }]@46 -> [set { System.Console.WriteLine(value + 1); }]@46");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item"), preserveLocalVariables: false)
]);
}
[Fact]
public void Indexer_InitUpdate()
{
var src1 = "class C { int this[int a] { get { return 1; } init { System.Console.WriteLine(value); } } }";
var src2 = "class C { int this[int a] { get { return 1; } init { System.Console.WriteLine(value + 1); } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [init { System.Console.WriteLine(value); }]@46 -> [init { System.Console.WriteLine(value + 1); }]@46");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item"), preserveLocalVariables: false)
]);
}
[Fact]
public void IndexerWithExpressionBody_Update()
{
var src1 = "class C { int this[int a] => 1; }";
var src2 = "class C { int this[int a] => 2; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int a] => 1;]@10 -> [int this[int a] => 2;]@10",
"Update [=> 1]@26 -> [=> 2]@26");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"))
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_ExpressionBodyToBlockBody()
{
var src1 = "class C { int this[int a] => 1; }";
var src2 = "class C { int this[int a] { get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int a] => 1;]@10 -> [int this[int a] { get { return 1; } }]@10",
"Insert [{ get { return 1; } }]@26",
"Insert [get { return 1; }]@28",
"Delete [=> 1]@26");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"))
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_BlockBodyToExpressionBody()
{
var src1 = "class C { int this[int a] { get { return 1; } } }";
var src2 = "class C { int this[int a] => 1; } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int a] { get { return 1; } }]@10 -> [int this[int a] => 1;]@10",
"Insert [=> 1]@26",
"Delete [{ get { return 1; } }]@26",
"Delete [get { return 1; }]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"))
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_GetterExpressionBodyToBlockBody()
{
var src1 = "class C { int this[int a] { get => 1; } }";
var src2 = "class C { int this[int a] { get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get => 1;]@28 -> [get { return 1; }]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"), preserveLocalVariables: false)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_BlockBodyToGetterExpressionBody()
{
var src1 = "class C { int this[int a] { get { return 1; } } }";
var src2 = "class C { int this[int a] { get => 1; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get { return 1; }]@28 -> [get => 1;]@28");
edits.VerifySemanticDiagnostics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_GetterExpressionBodyToExpressionBody()
{
var src1 = "class C { int this[int a] { get => 1; } }";
var src2 = "class C { int this[int a] => 1; } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int a] { get => 1; }]@10 -> [int this[int a] => 1;]@10",
"Insert [=> 1]@26",
"Delete [{ get => 1; }]@26",
"Delete [get => 1;]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"))
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_ExpressionBodyToGetterExpressionBody()
{
var src1 = "class C { int this[int a] => 1; }";
var src2 = "class C { int this[int a] { get => 1; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int a] => 1;]@10 -> [int this[int a] { get => 1; }]@10",
"Insert [{ get => 1; }]@26",
"Insert [get => 1;]@28",
"Delete [=> 1]@26");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"))
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_GetterBlockBodyToGetterExpressionBody()
{
var src1 = "class C { int this[int a] { get { return 1; } set { Console.WriteLine(0); } } }";
var src2 = "class C { int this[int a] { get => 1; set { Console.WriteLine(0); } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get { return 1; }]@28 -> [get => 1;]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"), preserveLocalVariables: false),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_SetterBlockBodyToSetterExpressionBody()
{
var src1 = "class C { int this[int a] { set { } } void F() { } }";
var src2 = "class C { int this[int a] { set => F(); } void F() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [set { }]@28 -> [set => F();]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_InitBlockBodyToInitExpressionBody()
{
var src1 = "class C { int this[int a] { init { } } void F() { } }";
var src2 = "class C { int this[int a] { init => F(); } void F() { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [init { }]@28 -> [init => F();]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")),
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_GetterExpressionBodyToGetterBlockBody()
{
var src1 = "class C { int this[int a] { get => 1; set { Console.WriteLine(0); } } }";
var src2 = "class C { int this[int a] { get { return 1; } set { Console.WriteLine(0); } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [get => 1;]@28 -> [get { return 1; }]@28");
edits.VerifySemantics(ActiveStatementsDescription.Empty,
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item"), preserveLocalVariables: false),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item"), preserveLocalVariables: false)
]);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_GetterAndSetterBlockBodiesToExpressionBody()
{
var src1 = "class C { int this[int a] { get { return 1; } set { Console.WriteLine(0); } } }";
var src2 = "class C { int this[int a] => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int a] { get { return 1; } set { Console.WriteLine(0); } }]@10 -> [int this[int a] => 1;]@10",
"Insert [=> 1]@26",
"Delete [{ get { return 1; } set { Console.WriteLine(0); } }]@26",
"Delete [get { return 1; }]@28",
"Delete [set { Console.WriteLine(0); }]@46");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Indexer_ExpressionBodyToGetterAndSetterBlockBodies()
{
var src1 = "class C { int this[int a] => 1; }";
var src2 = "class C { int this[int a] { get { return 1; } set { Console.WriteLine(0); } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int a] => 1;]@10 -> [int this[int a] { get { return 1; } set { Console.WriteLine(0); } }]@10",
"Insert [{ get { return 1; } set { Console.WriteLine(0); } }]@26",
"Insert [get { return 1; }]@28",
"Insert [set { Console.WriteLine(0); }]@46",
"Delete [=> 1]@26");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Item"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_Rename()
{
var interfaces = """
interface I
{
int this[int a] { get; }
}
interface J
{
int this[int a] { get; }
}
""";
var src1 = "class C { int I.this[int a] { get { return 1; } } } " + interfaces;
var src2 = "class C { int J.this[int a] { get { return 1; } } } " + interfaces;
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "int J.this[int a]", GetResource("indexer", "I.this[int a]")));
}
[Fact]
public void Indexer_Rename_ExpressionBody()
{
var interfaces = """
interface I
{
int this[int a] { get; }
}
interface J
{
int this[int a] { get; }
}
""";
var src1 = "class C { int I.this[int a] => 1; } " + interfaces;
var src2 = "class C { int J.this[int a] => 1; } " + interfaces;
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "int J.this[int a]", GetResource("indexer", "I.this[int a]")));
}
[Fact]
public void Indexer_Rename_Stackalloc()
{
var interfaces = """
interface I
{
int this[int a] { get; }
}
interface J
{
int this[int a] { get; }
}
""";
// only type is changed, no changes to the accessors (not even whitespace)
var src1 = "class C { byte G(Span<char> s) => 0; byte I.this[int a] { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }" + interfaces;
var src2 = "class C { byte G(Span<char> s) => 0; long J.this[int a] { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }" + interfaces;
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer getter")),
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer setter"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Indexer_Rename_Stackalloc_ExpressionBody()
{
var interfaces = """
interface I
{
int this[int a] { get; }
}
interface J
{
int this[int a] { get; }
}
""";
// only type is changed, no changes to the body (not even whitespace)
var src1 = "class C { byte G(Span<char> s) => 0; byte I.this[int a] => G(stackalloc int[1]); } " + interfaces;
var src2 = "class C { byte G(Span<char> s) => 0; long J.this[int a] => G(stackalloc int[1]); } " + interfaces;
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer getter"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Indexer_Reorder1()
{
var src1 = "class C { int this[int a] { get { return 1; } } int this[string a] { get { return 1; } } }";
var src2 = "class C { int this[string a] { get { return 1; } } int this[int a] { get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [int this[string a] { get { return 1; } }]@48 -> @10");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Indexer_AccessorReorder()
{
var src1 = "class C { int this[int a] { get { return 1; } set { } } }";
var src2 = "class C { int this[int a] { set { } get { return 1; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [set { }]@46 -> @28");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Indexer_Update_Attribute()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var src1 = attribute + "class C { [A(1)]int this[int a] { get; set; } }";
var src2 = attribute + "class C { [A(2)]int this[int a] { get; set; } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int this[int a]", GetResource("indexer"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Update_Type()
{
var src1 = "class C { byte this[int a] { get => 1; set {} } }";
var src2 = "class C { long this[int a] { get => 1; set {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Item")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "long this[int a]", CSharpFeaturesResources.indexer)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Update_Type_WithExpressionBody()
{
var src1 = "class C { byte this[int a] => 1; }";
var src2 = "class C { long this[int a] => 1; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "long this[int a]", CSharpFeaturesResources.indexer)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Update_Type_Stackalloc()
{
// only type is changed, no changes to the accessors (not even whitespace)
var src1 = "class C { byte G(Span<char> s) => 0; byte this[int x] { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }";
var src2 = "class C { byte G(Span<char> s) => 0; long this[int x] { get => G(stackalloc int[1]); set => G(stackalloc int[1]); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer getter")),
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer setter"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Indexer_Update_Type_Stackalloc_WithExpressionBody()
{
// only type is changed, no changes to the body (not even whitespace)
var src1 = "class C { byte G(Span<char> s) => 0; byte this[int x] => G(stackalloc int[1]); }";
var src2 = "class C { byte G(Span<char> s) => 0; long this[int x] => G(stackalloc int[1]); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer getter"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Indexer_Parameter_TypeChange()
{
var src1 = "class C { int this[byte a] { get => 1; set { } } }";
var src2 = "class C { int this[long a] { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Item")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_Partial_Parameter_TypeChange()
{
var srcA1 = "partial class C { partial int this[long x] { get; set; } }";
var srcB1 = "partial class C { partial int this[long x] { get => 1; set { } } }";
var srcA2 = "partial class C { partial int this[byte x] { get; set; } }";
var srcB2 = "partial class C { partial int this[byte x] { get => 1; set { } } }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IPropertySymbol>("C.this[]").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.get_Item").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.set_Item").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IPropertySymbol>("C.this[]").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.get_Item").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.set_Item").PartialImplementationPart, partialType: "C"),
]),
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IPropertySymbol>("C.this[]").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.get_Item").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.set_Item").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IPropertySymbol>("C.this[]").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.get_Item").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.set_Item").PartialImplementationPart, partialType: "C"),
]),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "byte x", GetResource("indexer"))]),
DocumentResults(
diagnostics: [Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "byte x", GetResource("indexer"))]),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Parameter_Rename()
{
var src1 = "class C { int this[int a] { get => 1; set { } } }";
var src2 = "class C { int this[int b] { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.set_Item")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]")),
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", GetResource("parameter"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Parameter_Update_Attribute()
{
var attribute = """
public class A : System.Attribute { public A(int x) {} }
""";
var src1 = attribute + "class C { int this[[A(1)]int a] { get => 1; set { } } }";
var src2 = attribute + "class C { int this[[A(2)]int a] { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.this[]"))
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", GetResource("parameter"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Parameter_Insert()
{
var src1 = "class C { int this[int a] { get => 1; set { } } }";
var src2 = "class C { int this[int a, string b] { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Item")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_Parameter_Insert_Partial()
{
var src1 = """
class C
{
partial int this[int a] { get; set; }
partial int this[int a] { get => 1; set { } }
}
""";
var src2 = """
class C
{
partial int this[int a, int/*1*/b, int c] { get; set; }
partial int this[int a, int/*2*/b, int c] { get => 1; set { } }
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IPropertySymbol>("C.this[]").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.get_Item").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember<IMethodSymbol>("C.set_Item").PartialImplementationPart, deletedSymbolContainerProvider: c => c.GetMember("C"), partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IPropertySymbol>("C.this[]").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.get_Item").PartialImplementationPart, partialType: "C"),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<IMethodSymbol>("C.set_Item").PartialImplementationPart, partialType: "C"),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int/*1*/b", GetResource("indexer")),
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "int/*2*/b", GetResource("indexer"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Parameter_Delete()
{
var src1 = "class C { int this[int a, string b] { get => 1; set { } } }";
var src2 = "class C { int this[int a] { get => 1; set { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.set_Item")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_Parameter_Reorder_Stackalloc()
{
var src1 = @"
using System;
class C
{
int this[int a, byte b] { get { return stackalloc int[1].Length; } }
}
";
var src2 = @"
using System;
class C
{
int this[byte b, int a] { get { return stackalloc int[1].Length; } }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer getter"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_Parameter_Reorder_Stackalloc_WithGetter_WithExpressionBody()
{
var src1 = @"
using System;
class C
{
int this[int a, byte b] { get => stackalloc int[1].Length; }
}
";
var src2 = @"
using System;
class C
{
int this[byte b, int a] { get => stackalloc int[1].Length; }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer getter"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_Parameter_Reorder_Stackalloc_WithExpressionBody()
{
var src1 = @"
using System;
class C
{
int this[int a, byte b] => stackalloc int[1].Length;
}
";
var src2 = @"
using System;
class C
{
int this[byte b, int a] => stackalloc int[1].Length;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("indexer getter"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_AddSetAccessor()
{
var src1 = @"
class C
{
public int this[int i] { get { return default; } }
}";
var src2 = @"
class C
{
public int this[int i] { get { return default; } set { } }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [set { }]@67");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IPropertySymbol>("this[]").SetMethod)],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_Delete()
{
var src1 = @"
class C<T>
{
public T this[int i]
{
get { return arr[i]; }
set { arr[i] = value; }
}
}";
var src2 = @"
class C<T>
{
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/750109")]
public void Indexer_DeleteGetAccessor()
{
var src1 = @"
class C<T>
{
public T this[int i]
{
get { return arr[i]; }
set { arr[i] = value; }
}
}";
var src2 = @"
class C<T>
{
public T this[int i]
{
set { arr[i] = value; }
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_DeleteSetAccessor()
{
var src1 = @"
class C
{
public int this[int i] { get { return 0; } set { } }
}";
var src2 = @"
class C
{
public int this[int i] { get { return 0; } }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.set_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem("http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1174850")]
public void Indexer_Insert()
{
var src1 = "struct C { }";
var src2 = "struct C { public int this[int x, int y] { get { return x + y; } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_ReadOnlyRef_Parameter_InsertWhole()
{
var src1 = "class Test { }";
var src2 = "class Test { int this[in int i] => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [int this[in int i] => throw null;]@13",
"Insert [[in int i]]@21",
"Insert [=> throw null]@32",
"Insert [in int i]@22");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_ReadOnlyRef_Parameter_Update()
{
var src1 = "class C { int this[int i] => throw null; }";
var src2 = "class C { int this[in int i] => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int i]@19 -> [in int i]@19");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.this[]"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_ReadOnlyRef_ReturnType_Insert()
{
var src1 = "class Test { }";
var src2 = "class Test { ref readonly int this[int i] => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [ref readonly int this[int i] => throw null;]@13",
"Insert [[int i]]@34",
"Insert [=> throw null]@42",
"Insert [int i]@35");
edits.VerifySemanticDiagnostics(
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Indexer_ReadOnlyRef_ReturnType_Update()
{
var src1 = "class C { int this[int i] => throw null; }";
var src2 = "class C { ref readonly int this[int i] => throw null; }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int this[int i] => throw null;]@10 -> [ref readonly int this[int i] => throw null;]@10");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.this[]"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.get_Item"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.this[]")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.get_Item")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "ref readonly int this[int i]", FeaturesResources.indexer_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int this[int x] { get => 1; set { } } }";
var srcA2 = "partial class C { int this[int x] { get => 1; set { } } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]").SetMethod)
]),
DocumentResults(),
]);
}
[Fact]
public void IndexerInit_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int this[int x] { get => 1; init { } }}";
var srcA2 = "partial class C { int this[int x] { get => 1; init { } }}";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]").SetMethod)
]),
DocumentResults(),
]);
}
[Fact]
public void AutoIndexer_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { int this[int x] { get; set; } }";
var srcA2 = "partial class C { int this[int x] { get; set; } }";
var srcB2 = "partial class C { }";
// Accessors need to be updated even though they do not have an explicit body.
// There is still a sequence point generated for them whose location needs to be updated.
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]").GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("C.this[]").SetMethod),
]),
DocumentResults(),
]);
}
[Fact]
public void AutoIndexer_ReadOnly_Add()
{
var src1 = @"
struct S
{
int this[int x] { get; }
}";
var src2 = @"
struct S
{
readonly int this[int x] { get; }
}";
var edits = GetTopEdits(src1, src2);
// Compiler generated attribute changed, we do not require runtime capability for custom attribute changes.
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("S.this[]")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IPropertySymbol>("S.this[]").GetMethod)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Indexer_InMutableStruct_ReadOnly_Add()
{
var src1 = @"
struct S
{
int this[int x] { get => 1; }
int this[uint x] { get => 1; set {}}
int this[byte x] { get => 1; set {}}
int this[sbyte x] { get => 1; set {}}
}";
var src2 = @"
struct S
{
readonly int this[int x] { get => 1; }
int this[uint x] { readonly get => 1; set {}}
int this[byte x] { get => 1; readonly set {}}
readonly int this[sbyte x] { get => 1; set {}}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Int32 }])),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_SByte }])),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Int32 }]).GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_SByte }]).GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_SByte }]).SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_UInt32 }]).GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Byte }]).SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_UInt32 }]).SetMethod));
}
[Fact]
public void Indexer_InReadOnlyStruct_ReadOnly_Add()
{
var src1 = @"
readonly struct S
{
int this[int x] { get => 1; }
int this[uint x] { get => 1; set {}}
int this[byte x] { get => 1; set {}}
int this[sbyte x] { get => 1; set {}}
}";
var src2 = @"
readonly struct S
{
readonly int this[int x] { get => 1; }
int this[uint x] { readonly get => 1; set {}}
int this[byte x] { get => 1; readonly set {}}
readonly int this[sbyte x] { get => 1; set {}}
}";
var edits = GetTopEdits(src1, src2);
// Updates only for accessors whose modifiers were explicitly updated.
// Indexers themselves are only updated when their modifiers change. The update is not necessary and could be eliminated.
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_UInt32 }]).GetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Byte }]).SetMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_Int32 }])),
SemanticEdit(SemanticEditKind.Update, c => c.GetMembers<IPropertySymbol>("S.this[]").Single(m => m.Parameters is [{ Type.SpecialType: SpecialType.System_SByte }])),
]);
}
#endregion
#region Events
[Theory]
[InlineData("static")]
[InlineData("virtual")]
[InlineData("abstract")]
[InlineData("override")]
[InlineData("sealed override", "override")]
public void Event_Update_Modifiers(string oldModifiers, string newModifiers = "")
{
if (oldModifiers != "")
{
oldModifiers += " ";
}
if (newModifiers != "")
{
newModifiers += " ";
}
var src1 = "class C { " + oldModifiers + "event Action F { add {} remove {} } }";
var src2 = "class C { " + newModifiers + "event Action F { add {} remove {} } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [" + oldModifiers + "event Action F { add {} remove {} }]@10 -> [" + newModifiers + "event Action F { add {} remove {} }]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ModifiersUpdate, newModifiers + "event Action F", FeaturesResources.event_));
}
[Fact]
public void Event_Accessor_Reorder1()
{
var src1 = "class C { event int E { add { } remove { } } }";
var src2 = "class C { event int E { remove { } add { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [remove { }]@32 -> @24");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Event_Accessor_Reorder2()
{
var src1 = "class C { event int E1 { add { } remove { } } event int E1 { add { } remove { } } }";
var src2 = "class C { event int E2 { remove { } add { } } event int E2 { remove { } add { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [event int E1 { add { } remove { } }]@10 -> [event int E2 { remove { } add { } }]@10",
"Update [event int E1 { add { } remove { } }]@49 -> [event int E2 { remove { } add { } }]@49",
"Reorder [remove { }]@33 -> @25",
"Reorder [remove { }]@72 -> @64");
}
[Fact]
public void Event_Accessor_Reorder3()
{
var src1 = "class C { event int E1 { add { } remove { } } event int E2 { add { } remove { } } }";
var src2 = "class C { event int E2 { remove { } add { } } event int E1 { remove { } add { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [event int E2 { add { } remove { } }]@49 -> @10",
"Reorder [remove { }]@72 -> @25",
"Reorder [remove { }]@33 -> @64");
}
[Fact]
public void Event_Insert()
{
var src1 = "class C { }";
var src2 = "class C { event int E { remove { } add { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember<INamedTypeSymbol>("C").GetMember("E"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Event_Delete()
{
var src1 = "class C { event int E { remove { } add { } } }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.add_E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.remove_E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void EventField_Delete()
{
var src1 = "class C { event System.Action E; }";
var src2 = "class C { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.Delete, "class C", "event field 'E'")],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Event_Insert_TypeLayout()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C
{
}
";
var src2 = @"
using System;
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C
{
private event Action c { add { } remove { } }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void EventField_Insert_TypeLayout()
{
var src1 = @"
using System;
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C
{
}
";
var src2 = @"
using System;
using System.Runtime.InteropServices;
[StructLayoutAttribute(LayoutKind.Sequential)]
class C
{
private event Action c;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.InsertOrMoveTypeWithLayoutMember, "c", GetResource("event field"), GetResource("class"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Event_ExpressionBodyToBlockBody()
{
var src1 = @"
using System;
public class C
{
event Action E { add => F(); remove => F(); }
}
";
var src2 = @"
using System;
public class C
{
event Action E { add { F(); } remove { } }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [add => F();]@57 -> [add { F(); }]@56",
"Update [remove => F();]@69 -> [remove { }]@69"
);
edits.VerifySemanticDiagnostics();
}
[Fact, WorkItem("https://github.com/dotnet/roslyn/issues/17681")]
public void Event_BlockBodyToExpressionBody()
{
var src1 = @"
using System;
public class C
{
event Action E { add { F(); } remove { } }
}
";
var src2 = @"
using System;
public class C
{
event Action E { add => F(); remove => F(); }
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [add { F(); }]@56 -> [add => F();]@57",
"Update [remove { }]@69 -> [remove => F();]@69"
);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Event_Partial_InsertDelete()
{
var srcA1 = "partial class C { }";
var srcB1 = "partial class C { event int E { add { } remove { } } }";
var srcA2 = "partial class C { event int E { add { } remove { } } }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IEventSymbol>("E").AddMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").GetMember<IEventSymbol>("E").RemoveMethod)
]),
DocumentResults(),
]);
}
[Fact]
public void Event_InMutableStruct_ReadOnly_Add()
{
var src1 = @"
struct S
{
public event Action E
{
add {} remove {}
}
}";
var src2 = @"
struct S
{
public readonly event Action E
{
add {} remove {}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.E")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.add_E")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("S.remove_E")));
}
[Fact]
public void Event_InReadOnlyStruct_ReadOnly_Add1()
{
var src1 = @"
readonly struct S
{
public event Action E
{
add {} remove {}
}
}";
var src2 = @"
readonly struct S
{
public readonly event Action E
{
add {} remove {}
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Fact]
public void EventField_Attribute_Add()
{
var src1 = @"
class C
{
event Action F;
}";
var src2 = @"
class C
{
[System.Obsolete]event Action F;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [event Action F;]@18 -> [[System.Obsolete]event Action F;]@18");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "event Action F", GetResource("event field"))],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F"))
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Event_Attribute_Add_CustomAccessors()
{
var src1 = @"
class C
{
event Action F { add {} remove {} }
}";
var src2 = @"
class C
{
[System.Obsolete]event Action F { add {} remove {} }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [event Action F { add {} remove {} }]@18 -> [[System.Obsolete]event Action F { add {} remove {} }]@18");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "event Action F", FeaturesResources.event_)],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F").AddMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F").RemoveMethod)
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Event_Accessor_Attribute_Add()
{
var src1 = @"
class C
{
event Action F { add {} remove {} }
}";
var src2 = @"
class C
{
event Action F { add {} [System.Obsolete]remove {} }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [remove {}]@42 -> [[System.Obsolete]remove {}]@42");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "remove", FeaturesResources.event_accessor)],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F").RemoveMethod)],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void EventField_Attribute_Delete()
{
var src1 = @"
class C
{
[System.Obsolete]event Action F;
}";
var src2 = @"
class C
{
event Action F;
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[System.Obsolete]event Action F;]@18 -> [event Action F;]@18");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "event Action F", GetResource("event field"))],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Event_Attribute_Delete()
{
var src1 = @"
class C
{
[System.Obsolete]event Action F { add {} remove {} }
}";
var src2 = @"
class C
{
event Action F { add {} remove {} }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[System.Obsolete]event Action F { add {} remove {} }]@18 -> [event Action F { add {} remove {} }]@18");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "event Action F", FeaturesResources.event_)],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F").AddMethod),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F").RemoveMethod)
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Event_Accessor_Attribute_Delete()
{
var src1 = @"
class C
{
event Action F { add {} [System.Obsolete]remove {} }
}";
var src2 = @"
class C
{
event Action F { add {} remove {} }
}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[System.Obsolete]remove {}]@42 -> [remove {}]@42");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "remove", FeaturesResources.event_accessor)],
capabilities: EditAndContinueCapabilities.Baseline);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<IEventSymbol>("C.F").RemoveMethod)
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Event_Rename_Stackalloc()
{
// only name is changed, no changes to the accessors (not even whitespace)
var src1 = "class C { void G(Span<char> s) {} event System.Action E { add => G(stackalloc int[1]); remove => G(stackalloc int[1]); } }";
var src2 = "class C { void G(Span<char> s) {} event System.Action F { add => G(stackalloc int[1]); remove => G(stackalloc int[1]); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("event accessor")),
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("event accessor")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Event_Update_Type_Stackalloc()
{
// only type is changed, no changes to the accessors (not even whitespace)
var src1 = "class C { void G(Span<char> s) {} event System.Action<byte> E { add => G(stackalloc int[1]); remove => G(stackalloc int[1]); } }";
var src2 = "class C { void G(Span<char> s) {} event System.Action<long> E { add => G(stackalloc int[1]); remove => G(stackalloc int[1]); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("event accessor")),
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[1]", GetResource("event accessor"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.AddInstanceFieldToExistingType);
}
[Fact]
public void Event_Rename()
{
var src1 = "class C { event System.Action E { remove { } add { } } }";
var src2 = "class C { event System.Action F { remove { } add { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.add_E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.remove_E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.F")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.add_F")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.remove_F")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "event System.Action F", FeaturesResources.event_)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void EventField_Rename()
{
var src1 = "class C { event System.Action E; }";
var src2 = "class C { event System.Action F; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.Renamed, "F", GetResource("event field", "E"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Event_Update_Type()
{
var src1 = "class C { event System.Action<long> E { remove { } add { } } }";
var src2 = "class C { event System.Action<byte> E { remove { } add { } } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.E")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.add_E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.remove_E"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.add_E")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.remove_E")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "event System.Action<byte> E", GetResource("event"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void EventField_Update_Type_RuntimeTypeChanged()
{
var src1 = "class C { event System.Action<long> E; }";
var src2 = "class C { event System.Action<byte> E; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.TypeUpdate, "event System.Action<byte> E", GetResource("event field"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Theory]
[InlineData("string", "string?")]
[InlineData("object", "dynamic")]
[InlineData("(int a, int b)", "(int a, int c)")]
public void EventField_Update_Type_RuntimeTypeUnchanged(string oldType, string newType)
{
var src1 = "class C { event System.Action<" + oldType + "> F, G; }";
var src2 = "class C { event System.Action<" + newType + "> F, G; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")),
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.G")));
}
[Fact]
public void EventField_Reorder()
{
var src1 = """
using System;
class C
{
event Action a = null;
event Action b = null;
event Action c;
}
""";
var src2 = """
using System;
class C
{
event Action c;
event Action a = null;
event Action b = null;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics();
}
[Fact]
public void EventField_Reorder_WithInitializer()
{
var src1 = """
using System;
class C
{
event Action a = null;
event Action b = null;
event Action c = null;
}
""";
var src2 = """
using System;
class C
{
event Action c = null;
event Action a = null;
event Action b = null;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics([SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), preserveLocalVariables: true)]);
}
[Theory]
[InlineData("", "struct", RudeEditKind.InsertOrMoveStructMember)]
[InlineData("", "record struct", RudeEditKind.InsertOrMoveStructMember)]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "class", RudeEditKind.InsertOrMoveTypeWithLayoutMember)]
[InlineData("[System.Runtime.InteropServices.StructLayout(System.Runtime.InteropServices.LayoutKind.Sequential)]", "record class", RudeEditKind.InsertOrMoveTypeWithLayoutMember)]
internal void EventField_Reorder_TypeLayout(string attribute, string keyword, RudeEditKind rudeEditKind)
{
var src1 = $$"""
using System;
{{attribute}}{{keyword}} C
{
event Action a;
event Action b;
event Action c;
}
""";
var src2 = $$"""
using System;
{{attribute}}{{keyword}} C
{
event Action c;
event Action a;
event Action b;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemanticDiagnostics(
Diagnostic(rudeEditKind, "event Action c", GetResource("event field"), GetResource(keyword)));
}
[Fact]
public void EventField_Reorder_Reloadable()
{
var src1 = "using System;" + ReloadableAttributeSrc + """
[CreateNewOnMetadataUpdate]
class C
{
event Action a = null;
event Action b = null;
event Action c;
}
""";
var src2 = "using System;" + ReloadableAttributeSrc + """
[CreateNewOnMetadataUpdate]
class C
{
event Action c;
event Action a = null;
event Action b = null;
}
""";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(EditKind.Reorder);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void EventField_Partial_InsertDelete()
{
var srcA1 = "partial class C { static void F() {} }";
var srcB1 = "partial class C { event System.Action E = F; }";
var srcA2 = "partial class C { static void F() {} event System.Action E = F; }";
var srcB2 = "partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetParameterlessConstructor("C"), partialType: "C", preserveLocalVariables: true)
]),
DocumentResults(),
]);
}
#endregion
#region Parameter
[Fact]
public void Parameter_Rename_Method()
{
var src1 = @"class C { public void M(int a) { } }";
var src2 = @"class C { public void M(int b) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a]@24 -> [int b]@24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Rename_Constructor()
{
var src1 = @"class C { public C(int a) {} }";
var src2 = @"class C { public C(int b) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [int a]@19 -> [int b]@19");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Rename_Operator1()
{
var src1 = @"class C { public static implicit operator int(C a) {} }";
var src2 = @"class C { public static implicit operator int(C b) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [C a]@46 -> [C b]@46");
}
[Fact]
public void Parameter_Rename_Operator2()
{
var src1 = @"class C { public static int operator +(C a, C b) { return 0; } }";
var src2 = @"class C { public static int operator +(C a, C x) { return 0; } } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [C b]@44 -> [C x]@44");
}
[Fact]
public void Parameter_Insert()
{
var src1 = @"class C { public void M() {} }";
var src2 = @"class C { public void M(int a) { a.ToString(); } } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public void M() {}]@10 -> [public void M(int a) { a.ToString(); }]@10",
"Insert [int a]@24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Parameter_Insert_Ref()
{
var src1 = @"class C { public void M(int a) {} }";
var src2 = @"class C { public void M(int a, ref int b) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [(int a)]@23 -> [(int a, ref int b)]@23",
"Insert [ref int b]@31");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Parameter_Insert_WithUpdatedReturnType()
{
var src1 = @"class C { public void M() {} }";
var src2 = @"class C { public int M(int a) { return a; } } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public void M() {}]@10 -> [public int M(int a) { return a; }]@10",
"Insert [int a]@23");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Parameter_Delete1()
{
var src1 = @"class C { public void M(int a) {} }";
var src2 = @"class C { public void M() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [int a]@24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 0)?.ISymbol)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Parameter_Delete2()
{
var src1 = @"class C { public void M(int a, int b) {} }";
var src2 = @"class C { public void M(int b) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [(int a, int b)]@23 -> [(int b)]@23",
"Delete [int a]@24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 2)?.ISymbol, deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMembers("C.M").FirstOrDefault(m => m.GetParameterCount() == 1)?.ISymbol)
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
}
[Fact]
public void Parameter_Reorder()
{
var src1 = @"class C { public void M(int a, int b) {} }";
var src2 = @"class C { public void M(int b, int a) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [int b]@31 -> @24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact, WorkItem(67268, "https://github.com/dotnet/roslyn/issues/67268")]
public void Parameter_Reorder_Constructor()
{
var src1 = @"class C { C(int a, int b) {} }";
var src2 = @"class C { C(int b, int a) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [int b]@19 -> @12");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember<INamedTypeSymbol>("C").InstanceConstructors.Single(), preserveLocalVariables: true)
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Reorder_UpdateBody()
{
var src1 = @"class C { public void M(int a, int b) { a.ToString(); } }";
var src2 = @"class C { public void M(int b, int a) { b.ToString(); } } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public void M(int a, int b) { a.ToString(); }]@10 -> [public void M(int b, int a) { b.ToString(); }]@10",
"Reorder [int b]@31 -> @24");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))],
capabilities: EditAndContinueCapabilities.UpdateParameters);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Reorder_DifferentTypes()
{
var src1 = @"class C { public void M(string a, int b) { a.ToString(); } }";
var src2 = @"class C { public void M(int b, string a) { b.ToString(); } } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [public void M(string a, int b) { a.ToString(); }]@10 -> [public void M(int b, string a) { b.ToString(); }]@10",
"Reorder [int b]@34 -> @24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "public void M(int b, string a)", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Reorder_Rename()
{
var src1 = @"class C { public void M(int a, int b) {} }";
var src2 = @"class C { public void M(int b, int c) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [int b]@31 -> @24",
"Update [int a]@24 -> [int c]@31");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))
],
capabilities: EditAndContinueCapabilities.UpdateParameters);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter),
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int c", FeaturesResources.parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Reorder_Rename_Generic()
{
var src1 = @"class C<T> { public void M(int a, int b) {} }";
var src2 = @"class C<T> { public void M(int b, int c) {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))
],
capabilities:
EditAndContinueCapabilities.UpdateParameters |
EditAndContinueCapabilities.GenericAddMethodToExistingType |
EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "int b", GetResource("method")),
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", GetResource("parameter")),
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int c", GetResource("parameter"))
],
capabilities: EditAndContinueCapabilities.GenericAddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int b", FeaturesResources.parameter),
Diagnostic(RudeEditKind.RenamingNotSupportedByRuntime, "int c", FeaturesResources.parameter)
],
capabilities: EditAndContinueCapabilities.GenericUpdateMethod);
}
[Theory]
[InlineData("int")]
[InlineData("in byte")]
[InlineData("ref byte")]
[InlineData("out byte")]
[InlineData("ref readonly byte")]
public void Parameter_Update_TypeOrRefKind_RuntimeTypeChanged(string oldType)
{
var src1 = "class C { int F(" + oldType + " a) => throw null!; }";
var src2 = "class C { int F(byte a) => throw null!; }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.F"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.F"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "byte a", GetResource("method"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Theory]
[InlineData("int", "this int")]
[InlineData("string[]", "params string[]")]
[InlineData("string", "string?")]
[InlineData("object", "dynamic")]
[InlineData("(int a, int b)", "(int a, int c)")]
public void Parameter_Update_Type_RuntimeTypeUnchanged(string oldType, string newType)
{
var src1 = "static class C { static void M(" + oldType + " a) {} }";
var src2 = "static class C { static void M(" + newType + " a) {} }";
var edits = GetTopEdits(src1, src2);
// We don't require a runtime capability to update attributes.
// All runtimes support changing the attributes in metadata, some just don't reflect the changes in the Reflection model.
// Having compiler-generated attributes visible via Reflaction API is not that important.
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M")));
}
[Fact]
public void Parameter_Update_Type_Nullable()
{
var src1 = @"
#nullable enable
class C { static void M(string a) { } }
";
var src2 = @"
#nullable disable
class C { static void M(string a) { } }
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics();
}
[Theory]
[InlineData("this")]
[InlineData("params")]
public void Parameter_Modifier_Remove(string modifier)
{
var src1 = @"static class C { static void F(" + modifier + " int[] a) { } }";
var src2 = @"static class C { static void F(int[] a) { } }";
var edits = GetTopEdits(src1, src2);
// We don't require a runtime capability to update attributes.
// All runtimes support changing the attributes in metadata, some just don't reflect the changes in the Reflection model.
// Having compiler-generated attributes visible via Reflaction API is not that important.
edits.VerifySemantics(
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F")));
}
[Theory]
[InlineData("int a = 1", "int a = 2")]
[InlineData("int a = 1", "int a")]
[InlineData("int a", "int a = 2")]
[InlineData("object a = null", "object a")]
[InlineData("object a", "object a = null")]
[InlineData("double a = double.NaN", "double a = 1.2")]
public void Parameter_Initializer_Update(string oldParameter, string newParameter)
{
var src1 = @"static class C { static void F(" + oldParameter + ") { } }";
var src2 = @"static class C { static void F(" + newParameter + ") { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.InitializerUpdate, newParameter, FeaturesResources.parameter));
}
[Fact]
public void Parameter_Initializer_NaN()
{
var src1 = @"static class C { static void F(double a = System.Double.NaN) { } }";
var src2 = @"static class C { static void F(double a = double.NaN) { } }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics();
}
[Fact]
public void Parameter_Initializer_InsertDeleteUpdate()
{
var srcA1 = @"partial class C { }";
var srcB1 = @"partial class C { public static void F(int x = 1) {} }";
var srcA2 = @"partial class C { public static void F(int x = 2) {} }";
var srcB2 = @"partial class C { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(
diagnostics:
[
Diagnostic(RudeEditKind.InitializerUpdate, "int x = 2", FeaturesResources.parameter)
]),
DocumentResults(),
]);
}
[Fact]
public void Parameter_Attribute_Insert()
{
var attribute = "public class A : 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]@63 -> [[A]int a]@63");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.M"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Parameter_Attribute_Insert_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(
"Update [int a]@101 -> [[A]int a]@101");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNonCustomAttribute, "int a", "AAttribute", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Parameter_Attribute_Insert_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.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingNonCustomAttribute, "int a", "AAttribute", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Parameter_Attribute_Insert_NotSupportedByRuntime1()
{
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.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Attribute_Insert_NotSupportedByRuntime2()
{
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]int a]@120 -> [[A, B]int a]@120");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Attribute_Delete_NotSupportedByRuntime()
{
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(
"Update [[A]int a]@72 -> [int a]@72");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Attribute_Update_NotSupportedByRuntime()
{
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 [[System.Obsolete(\"1\"), B]int a]@120 -> [[System.Obsolete(\"2\"), A]int a]@120");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "int a", FeaturesResources.parameter)],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void Parameter_Attribute_Update()
{
var attribute = "class A : System.Attribute { public A(int x) {} } ";
var src1 = attribute + "class C { void F([A(0)]int a) {} }";
var src2 = attribute + "class C { void F([A(1)]int a) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[A(0)]int a]@67 -> [[A(1)]int a]@67");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void Parameter_Attribute_Update_WithBodyUpdate()
{
var attribute = "class A : System.Attribute { public A(int x) {} } ";
var src1 = attribute + "class C { void F([A(0)]int a) { F(0); } }";
var src2 = attribute + "class C { void F([A(1)]int a) { F(1); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [void F([A(0)]int a) { F(0); }]@60 -> [void F([A(1)]int a) { F(1); }]@60",
"Update [[A(0)]int a]@67 -> [[A(1)]int a]@67");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"))],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
#endregion
#region Method Type Parameter
[Fact]
public void MethodTypeParameterInsert1()
{
var src1 = @"class C { public void M() {} }";
var src2 = @"class C { public void M<A>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [<A>]@23",
"Insert [A]@24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "A", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodTypeParameterInsert2()
{
var src1 = @"class C { public void M<A>() {} }";
var src2 = @"class C { public void M<A,B>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [<A>]@23 -> [<A,B>]@23",
"Insert [B]@26");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "B", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodTypeParameterDelete1()
{
var src1 = @"class C { public void M<A>() {} }";
var src2 = @"class C { public void M() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [<A>]@23",
"Delete [A]@24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "public void M()", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodTypeParameterDelete2()
{
var src1 = @"class C { public void M<A,B>() {} }";
var src2 = @"class C { public void M<B>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [<A,B>]@23 -> [<B>]@23",
"Delete [A]@24");
edits.VerifySemantics(
[
SemanticEdit(SemanticEditKind.Delete, c => c.GetMember("C.M"), deletedSymbolContainerProvider: c => c.GetMember("C")),
SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("C.M")),
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.ChangingSignatureNotSupportedByRuntime, "public void M<B>()", GetResource("method"))],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodTypeParameterUpdate()
{
var src1 = @"class C { public void M<A>() {} }";
var src2 = @"class C { public void M<B>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [A]@24 -> [B]@24");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.Renamed, "B", GetResource("type parameter", "A"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "B", GetResource("method")),
Diagnostic(RudeEditKind.Renamed, "B", GetResource("type parameter", "A"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void MethodTypeParameterReorder()
{
var src1 = @"class C { public void M<A,B>() {} }";
var src2 = @"class C { public void M<B,A>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [B]@26 -> @24");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.Move, "B", GetResource("type parameter"))],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MethodTypeParameterReorderAndUpdate()
{
var src1 = @"class C { public void M<A,B>() {} }";
var src2 = @"class C { public void M<B,C>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [B]@26 -> @24",
"Update [A]@24 -> [C]@26");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.Move, "B", GetResource("type parameter")),
Diagnostic(RudeEditKind.Renamed, "C", GetResource("type parameter", "A"))
],
capabilities: EditAndContinueCapabilities.AddMethodToExistingType | EditAndContinueCapabilities.GenericAddMethodToExistingType | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MethodTypeParameter_Attribute_Insert1()
{
var attribute = "public class AAttribute : System.Attribute { }\n\n";
var src1 = attribute + @"class C { public void M< T>() {} }";
var src2 = attribute + @"class C { public void M<[A]T>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [T]@75 -> [[A]T]@72");
// Updating attributes of type parameters not supported:
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.GenericMethodUpdate, "T")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MethodTypeParameter_Attribute_Insert2()
{
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 ]T]@120 -> [[A, B]T]@120");
// Updating attributes of type parameters not supported:
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.GenericMethodUpdate, "T")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MethodTypeParameter_Attribute_Delete()
{
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< T>() {} } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[A]T]@72 -> [T]@75");
// Updating attributes of type parameters not supported:
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.GenericMethodUpdate, "T")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MethodTypeParameter_Attribute_Update()
{
var attribute = "class A : System.Attribute { public A(int x) {} } ";
var src1 = attribute + "class C { void F<[A(0)]T>(T a) {} }";
var src2 = attribute + "class C { void F<[A(1)]T>(T a) {} }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[A(0)]T]@67 -> [[A(1)]T]@67");
// Updating attributes of type parameters not supported:
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.GenericMethodUpdate, "T")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes | EditAndContinueCapabilities.GenericUpdateMethod);
}
[Fact]
public void MethodTypeParameter_Attribute_Update_WithBodyUpdate()
{
var attribute = "class A : System.Attribute { public A(int x) {} } ";
var src1 = attribute + "class C { void F<[A(0)]T>(T a) { F(0); } }";
var src2 = attribute + "class C { void F<[A(1)]T>(T a) { F(1); } }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [void F<[A(0)]T>(T a) { F(0); }]@60 -> [void F<[A(1)]T>(T a) { F(1); }]@60",
"Update [[A(0)]T]@67 -> [[A(1)]T]@67");
// Updating attributes of type parameters not supported:
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.GenericMethodUpdate, "T")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes | EditAndContinueCapabilities.GenericUpdateMethod);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingGenericNotSupportedByRuntime, "void F<[A(1)]T>(T a)", GetResource("method")),
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", GetResource("type parameter"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
#endregion
#region Type Type Parameter
[Fact]
public void TypeTypeParameterInsert1()
{
var src1 = @"class C {}";
var src2 = @"class C<A> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [<A>]@7",
"Insert [A]@8");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "A", FeaturesResources.type_parameter));
}
[Fact]
public void TypeTypeParameterInsert2()
{
var src1 = @"class C<A> {}";
var src2 = @"class C<A,B> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [<A>]@7 -> [<A,B>]@7",
"Insert [B]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Insert, "B", FeaturesResources.type_parameter));
}
[Fact]
public void TypeTypeParameterDelete1()
{
var src1 = @"using System; class C<A> { }";
var src2 = @"using System; class C { } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [<A>]@21",
"Delete [A]@22");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C", GetResource("type parameter", "A")));
}
[Fact]
public void TypeTypeParameterDelete2()
{
var src1 = @"class C<A,B> {}";
var src2 = @"class C<B> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [<A,B>]@7 -> [<B>]@7",
"Delete [A]@8");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C<B>", GetResource("type parameter", "A")));
}
[Fact]
public void TypeTypeParameterUpdate()
{
var src1 = @"class C<A> {}";
var src2 = @"class C<B> {} ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [A]@8 -> [B]@8");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Renamed, "B", GetResource("type parameter", "A")));
}
[Fact]
public void TypeTypeParameterReorder()
{
var src1 = @"class C<A,B> { }";
var src2 = @"class C<B,A> { } ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [B]@10 -> @8");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter));
}
[Fact]
public void TypeTypeParameterReorderAndUpdate()
{
var src1 = @"class C<A,B> {}";
var src2 = @"class C<B,C> {} ";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [B]@10 -> @8",
"Update [A]@8 -> [C]@10");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "B", FeaturesResources.type_parameter),
Diagnostic(RudeEditKind.Renamed, "C", GetResource("type parameter", "A")));
}
[Fact]
public void TypeTypeParameterAttributeInsert1()
{
var attribute = "public class AAttribute : System.Attribute { }\n\n";
var src1 = attribute + @"class C<T> {}";
var src2 = attribute + @"class C<[A]T> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [T]@56 -> [[A]T]@56");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void TypeTypeParameterAttributeInsert2()
{
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]T]@104 -> [[A, B]T]@104");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void TypeTypeParameterAttributeInsert_SupportedByRuntime()
{
var attribute = "public class AAttribute : System.Attribute { }\n\n";
var src1 = attribute + @"class C<T> {}";
var src2 = attribute + @"class C<[A]T> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [T]@56 -> [[A]T]@56");
edits.VerifySemanticDiagnostics(
[Diagnostic(RudeEditKind.GenericTypeUpdate, "T")],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void TypeTypeParameterAttributeDelete()
{
var attribute = "public class AAttribute : System.Attribute { }\n\n";
var src1 = attribute + @"class C<[A]T> {}";
var src2 = attribute + @"class C<T> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [[A]T]@56 -> [T]@56");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void TypeTypeParameterAttributeUpdate()
{
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 [[System.Obsolete(\"1\"), B]T]@104 -> [[System.Obsolete(\"2\"), A]T]@104");
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangingAttributesNotSupportedByRuntime, "T", FeaturesResources.type_parameter)
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void TypeTypeParameter_Partial_Attribute_AddMultiple()
{
var attributes = @"
class A : System.Attribute {}
class B : System.Attribute {}
";
var srcA1 = "partial class C<T> { }" + attributes;
var srcB1 = "partial class C<T> { }";
var srcA2 = "partial class C<[A]T> { }" + attributes;
var srcB2 = "partial class C<[B]T> { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(diagnostics:
[
Diagnostic(RudeEditKind.GenericTypeUpdate, "T"),
]),
DocumentResults(diagnostics:
[
Diagnostic(RudeEditKind.GenericTypeUpdate, "T"),
]),
],
capabilities: EditAndContinueCapabilities.ChangeCustomAttributes);
}
[Fact]
public void TypeTypeParameter_Partial_Attribute_AddMultiple_Reloadable()
{
var attributes = @"
class A : System.Attribute {}
class B : System.Attribute {}
";
var srcA1 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C<T> { }" + attributes;
var srcB1 = "partial class C<T> { }";
var srcA2 = ReloadableAttributeSrc + "[CreateNewOnMetadataUpdate]partial class C<[A]T> { }" + attributes;
var srcB2 = "partial class C<[B]T> { }";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
DocumentResults(semanticEdits:
[
SemanticEdit(SemanticEditKind.Replace, c => c.GetMember("C"), partialType: "C")
]),
],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
#endregion
#region Type Parameter Constraints
[Theory]
[InlineData("nonnull")]
[InlineData("struct")]
[InlineData("class")]
[InlineData("new()")]
[InlineData("unmanaged")]
[InlineData("System.IDisposable")]
[InlineData("System.Delegate")]
[InlineData("allows ref struct")]
public void TypeConstraint_Insert(string newConstraint)
{
var src1 = "class C<S,T> { }";
var src2 = "class C<S,T> where T : " + newConstraint + " { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [where T : " + newConstraint + "]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingConstraints, "where T : " + newConstraint, FeaturesResources.type_parameter));
}
[Theory]
[InlineData("nonnull")]
[InlineData("struct")]
[InlineData("class")]
[InlineData("new()")]
[InlineData("unmanaged")]
[InlineData("System.IDisposable")]
[InlineData("System.Delegate")]
[InlineData("allows ref struct")]
public void TypeConstraint_Delete(string oldConstraint)
{
var src1 = "class C<S,T> where T : " + oldConstraint + " { }";
var src2 = "class C<S,T> { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [where T : " + oldConstraint + "]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingConstraints, "T", FeaturesResources.type_parameter));
}
[Theory]
[InlineData("string", "string?")]
[InlineData("(int a, int b)", "(int a, int c)")]
public void TypeConstraint_Update_RuntimeTypeUnchanged(string oldType, string newType)
{
// note: dynamic is not allowed in constraints
var src1 = "class C<T> where T : System.Collections.Generic.List<" + oldType + "> {}";
var src2 = "class C<T> where T : System.Collections.Generic.List<" + newType + "> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
semanticEdits:
[
SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C"))
]);
}
[Theory]
[InlineData("int", "string")]
[InlineData("int", "int?")]
[InlineData("(int a, int b)", "(int a, double b)")]
public void TypeConstraint_Update_RuntimeTypeChanged(string oldType, string newType)
{
var src1 = "class C<T> where T : System.Collections.Generic.List<" + oldType + "> {}";
var src2 = "class C<T> where T : System.Collections.Generic.List<" + newType + "> {}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingConstraints, "where T : System.Collections.Generic.List<" + newType + ">", FeaturesResources.type_parameter));
}
[Fact]
public void TypeConstraint_Delete_WithParameter()
{
var src1 = "class C<S,T> where S : new() where T : class { }";
var src2 = "class C<S> where S : new() { }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "class C<S>", GetResource("type parameter", "T")));
}
[Fact]
public void TypeConstraint_MultipleClauses_Insert()
{
var src1 = "class C<S,T> where T : class { }";
var src2 = "class C<S,T> where S : unmanaged where T : class { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Insert [where S : unmanaged]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingConstraints, "where S : unmanaged", FeaturesResources.type_parameter));
}
[Fact]
public void TypeConstraint_MultipleClauses_Delete()
{
var src1 = "class C<S,T> where S : new() where T : class { }";
var src2 = "class C<S,T> where T : class { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Delete [where S : new()]@13");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangingConstraints, "S", FeaturesResources.type_parameter));
}
[Fact]
public void TypeConstraint_MultipleClauses_Reorder()
{
var src1 = "class C<S,T> where S : struct where T : class { }";
var src2 = "class C<S,T> where T : class where S : struct { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [where T : class]@30 -> @13");
edits.VerifySemanticDiagnostics();
}
[Fact]
public void TypeConstraint_MultipleClauses_UpdateAndReorder()
{
var src1 = "class C<S,T> where S : new() where T : class { }";
var src2 = "class C<T,S> where T : class, I where S : class, new() { }";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Reorder [where T : class]@29 -> @13",
"Reorder [T]@10 -> @8",
"Update [where T : class]@29 -> [where T : class, I]@13",
"Update [where S : new()]@13 -> [where S : class, new()]@32");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Move, "T", FeaturesResources.type_parameter),
Diagnostic(RudeEditKind.ChangingConstraints, "where T : class, I", FeaturesResources.type_parameter),
Diagnostic(RudeEditKind.ChangingConstraints, "where S : class, new()", FeaturesResources.type_parameter));
}
#endregion
#region Top Level Statements
[Fact]
public void TopLevelStatements_Update()
{
var src1 = @"
using System;
Console.WriteLine(""Hello"");
";
var src2 = @"
using System;
Console.WriteLine(""Hello World"");
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Update [Console.WriteLine(\"Hello\");]@19 -> [Console.WriteLine(\"Hello World\");]@19");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, @"Console.WriteLine(""Hello World"");", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_InsertAndUpdate()
{
var src1 = @"
using System;
Console.WriteLine(""Hello"");
";
var src2 = @"
using System;
Console.WriteLine(""Hello World"");
Console.WriteLine(""What is your name?"");
var name = Console.ReadLine();
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits(
"Update [Console.WriteLine(\"Hello\");]@19 -> [Console.WriteLine(\"Hello World\");]@19",
"Insert [Console.WriteLine(\"What is your name?\");]@54",
"Insert [var name = Console.ReadLine();]@96");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, @"Console.WriteLine(""Hello World"");", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_Insert_NoImplicitMain()
{
var src1 = @"
using System;
";
var src2 = @"
using System;
Console.WriteLine(""Hello World"");
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [Console.WriteLine(\"Hello World\");]@19");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Insert, c => c.GetMember("Program.<Main>$"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}
[Fact]
public void TopLevelStatements_Insert_ImplicitMain()
{
var src1 = @"
using System;
Console.WriteLine(""Hello"");
";
var src2 = @"
using System;
Console.WriteLine(""Hello"");
Console.WriteLine(""World"");
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Insert [Console.WriteLine(\"World\");]@48");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, @"Console.WriteLine(""Hello"");", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_Delete_NoImplicitMain()
{
var src1 = @"
using System;
Console.WriteLine(""Hello World"");
";
var src2 = @"
using System;
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [Console.WriteLine(\"Hello World\");]@19");
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.Delete, "using System;", GetResource("top-level statement")));
}
[Fact]
public void TopLevelStatements_Delete_ImplicitMain()
{
var src1 = @"
using System;
Console.WriteLine(""Hello"");
Console.WriteLine(""World"");
";
var src2 = @"
using System;
Console.WriteLine(""Hello"");
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Delete [Console.WriteLine(\"World\");]@48");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, @"Console.WriteLine(""Hello"");", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_StackAlloc()
{
var src1 = @"Span<int> = stackalloc int[1];";
var src2 = @"Span<int> = stackalloc int[2];";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[2]", GetResource("top-level code")),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Span<int> = stackalloc int[2];", GetResource("top-level code"))
]);
}
[Fact]
public void TopLevelStatements_StackAllocInUnsafeBlock()
{
var src1 = @"unsafe { var x = stackalloc int[3]; System.Console.Write(1); }";
var src2 = @"unsafe { var x = stackalloc int[3]; System.Console.Write(2); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[3]", GetResource("top-level code")),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "unsafe { var x = stackalloc int[3]; System.Console.Write(2); }", GetResource("top-level code"))
]);
}
[Fact]
public void TopLevelStatements_StackAllocInTopBlock()
{
var src1 = @"{ var x = stackalloc int[3]; System.Console.Write(1); }";
var src2 = @"{ var x = stackalloc int[3]; System.Console.Write(2); }";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.StackAllocUpdate, "stackalloc int[3]", GetResource("top-level code")),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "{ var x = stackalloc int[3]; System.Console.Write(2); }", GetResource("top-level code"))
]);
}
[Fact]
public void TopLevelStatements_VoidToInt1()
{
var src1 = @"
using System;
Console.Write(1);
";
var src2 = @"
using System;
Console.Write(1);
return 1;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_VoidToInt2()
{
var src1 = @"
using System;
Console.Write(1);
return;
";
var src2 = @"
using System;
Console.Write(1);
return 1;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_VoidToInt3()
{
var src1 = @"
using System;
Console.Write(1);
int Goo()
{
return 1;
}
";
var src2 = @"
using System;
Console.Write(1);
return 1;
int Goo()
{
return 1;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_Await_Insert_First()
{
var src1 = @"
using System.Threading.Tasks;
return 1;
";
var src2 = @"
using System.Threading.Tasks;
await Task.Delay(200);
return 1;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "await Task.Delay(200);"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(200);", GetResource("top-level code"))
]);
}
[Fact]
public void TopLevelStatements_Await_Insert_Second()
{
var src1 = @"
using System.Threading.Tasks;
await Task.Delay(100);
";
var src2 = @"
using System.Threading.Tasks;
await Task.Delay(100);
await Task.Delay(200);
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"), preserveLocalVariables: true)],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(100);", GetResource("top-level code"))],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingStateMachineMethodNotSupportedByRuntime, "await Task.Delay(100);"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(100);", GetResource("top-level code"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void TopLevelStatements_Await_Delete_Last()
{
var src1 = @"
using System.Threading.Tasks;
await Task.Delay(100);
return 1;
";
var src2 = @"
using System.Threading.Tasks;
return 1;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;"),
Diagnostic(RudeEditKind.ChangingFromAsynchronousToSynchronous, "return 1;", GetResource("top-level code")),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "return 1;", GetResource("top-level code"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void TopLevelStatements_Await_Delete_Second()
{
var src1 = @"
using System.Threading.Tasks;
await Task.Delay(100);
await Task.Delay(200);
";
var src2 = @"
using System.Threading.Tasks;
await Task.Delay(100);
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"), preserveLocalVariables: true)],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(100);", GetResource("top-level code"))],
capabilities: EditAndContinueCapabilities.AddInstanceFieldToExistingType);
edits.VerifySemanticDiagnostics(
[
Diagnostic(RudeEditKind.UpdatingStateMachineMethodNotSupportedByRuntime, "await Task.Delay(100);"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(100);", GetResource("top-level code"))
],
capabilities: EditAndContinueCapabilities.Baseline);
}
[Fact]
public void TopLevelStatements_VoidToTask()
{
var src1 = @"
using System;
using System.Threading.Tasks;
Console.Write(1);
";
var src2 = @"
using System;
using System.Threading.Tasks;
await Task.Delay(100);
Console.Write(1);
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "await Task.Delay(100);"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(100);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_TaskToTaskInt()
{
var src1 = @"
using System;
using System.Threading.Tasks;
await Task.Delay(100);
Console.Write(1);
";
var src2 = @"
using System;
using System.Threading.Tasks;
await Task.Delay(100);
Console.Write(1);
return 1;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return 1;"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(100);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_VoidToTaskInt()
{
var src1 = @"
using System;
using System.Threading.Tasks;
Console.Write(1);
";
var src2 = @"
using System;
using System.Threading.Tasks;
Console.Write(1);
return await GetInt();
Task<int> GetInt()
{
return Task.FromResult(1);
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return await GetInt();"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_IntToVoid1()
{
var src1 = @"
using System;
Console.Write(1);
return 1;
";
var src2 = @"
using System;
Console.Write(1);
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_IntToVoid2()
{
var src1 = @"
using System;
Console.Write(1);
return 1;
";
var src2 = @"
using System;
Console.Write(1);
return;
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "return;"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_IntToVoid3()
{
var src1 = @"
using System;
Console.Write(1);
return 1;
int Goo()
{
return 1;
}
";
var src2 = @"
using System;
Console.Write(1);
int Goo()
{
return 1;
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, """
int Goo()
{
return 1;
}
"""),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_IntToVoid4()
{
var src1 = @"
using System;
Console.Write(1);
return 1;
public class C
{
public int Goo()
{
return 1;
}
}
";
var src2 = @"
using System;
Console.Write(1);
public class C
{
public int Goo()
{
return 1;
}
}
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_TaskToVoid()
{
var src1 = @"
using System;
using System.Threading.Tasks;
await Task.Delay(100);
Console.Write(1);
";
var src2 = @"
using System;
using System.Threading.Tasks;
Console.Write(1);
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"),
Diagnostic(RudeEditKind.ChangingFromAsynchronousToSynchronous, "Console.Write(1);", GetResource("top-level code")),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_TaskIntToTask()
{
var src1 = @"
using System;
using System.Threading.Tasks;
await Task.Delay(100);
Console.Write(1);
return 1;
";
var src2 = @"
using System;
using System.Threading.Tasks;
await Task.Delay(100);
Console.Write(1);
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "await Task.Delay(100);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_TaskIntToVoid()
{
var src1 = @"
using System;
using System.Threading.Tasks;
Console.Write(1);
return await GetInt();
Task<int> GetInt()
{
return Task.FromResult(1);
}
";
var src2 = @"
using System;
using System.Threading.Tasks;
Console.Write(1);
";
var edits = GetTopEdits(src1, src2);
edits.VerifySemanticDiagnostics(
Diagnostic(RudeEditKind.ChangeImplicitMainReturnType, "Console.Write(1);"),
Diagnostic(RudeEditKind.ChangingFromAsynchronousToSynchronous, "Console.Write(1);", GetResource("top-level code")),
Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.Write(1);", GetResource("top-level code")));
}
[Fact]
public void TopLevelStatements_WithLambda_Insert()
{
var src1 = @"
using System;
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
";
var src2 = @"
using System;
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
Console.WriteLine(1);
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"), syntaxMap[0])],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Func<int> a = () => { return 1; };", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_WithLambda_Update()
{
var src1 = @"
using System;
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
Console.WriteLine(1);
public class C { }
";
var src2 = @"
using System;
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
Console.WriteLine(2);
public class C { }
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"), syntaxMap[0])],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Func<int> a = () => { return 1; };", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_WithLambda_Delete()
{
var src1 = @"
using System;
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
Console.WriteLine(1);
public class C { }
";
var src2 = @"
using System;
Func<int> a = () => { <N:0.0>return 1;</N:0.0> };
Func<Func<int>> b = () => () => { <N:0.1>return 1;</N:0.1> };
public class C { }
";
var edits = GetTopEdits(src1, src2);
var syntaxMap = GetSyntaxMap(src1, src2);
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"), syntaxMap[0])],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Func<int> a = () => { return 1; };", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_UpdateMultiple()
{
var src1 = @"
using System;
Console.WriteLine(1);
Console.WriteLine(2);
public class C { }
";
var src2 = @"
using System;
Console.WriteLine(3);
Console.WriteLine(4);
public class C { }
";
var edits = GetTopEdits(src1, src2);
// Since each individual statement is a separate update to a separate node, this just validates we correctly
// only analyze the things once
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.WriteLine(3);", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_MoveToOtherFile()
{
var srcA1 = @"
using System;
Console.WriteLine(1);
public class A
{
}";
var srcB1 = @"
using System;
public class B
{
}";
var srcA2 = @"
using System;
public class A
{
}";
var srcB2 = @"
using System;
Console.WriteLine(2);
public class B
{
}";
EditAndContinueValidation.VerifySemantics(
[GetTopEdits(srcA1, srcA2), GetTopEdits(srcB1, srcB2)],
[
DocumentResults(),
DocumentResults(
semanticEdits: [SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
diagnostics: [Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "Console.WriteLine(2);", GetResource("top-level code"))]),
]);
}
[Fact]
public void TopLevelStatements_BlockReorder()
{
var src1 = @"
{ int a; }
{ int b; }
";
var src2 = @"
{ int b; }
{ int a; }
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Reorder [{ int b; }]@14 -> @2");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "{ int b; }", GetResource("top-level code"))]);
}
[Fact]
public void TopLevelStatements_Reorder()
{
var src1 = @"
System.Console.Write(1);
System.Console.Write(2);
";
var src2 = @"
System.Console.Write(2);
System.Console.Write(1);
";
var edits = GetTopEdits(src1, src2);
edits.VerifyEdits("Reorder [System.Console.Write(2);]@28 -> @2");
edits.VerifySemantics(
[SemanticEdit(SemanticEditKind.Update, c => c.GetMember("Program.<Main>$"))],
[Diagnostic(RudeEditKind.UpdateMightNotHaveAnyEffect, "System.Console.Write(2);", GetResource("top-level code"))]);
}
#endregion
}
|